mirror of
https://git.minetest.land/MineClone2/MineClone2.git
synced 2024-11-20 09:53:46 +01:00
a6136ad158
The absorption effect won't work on damage disabled servers and the health bar and hunger bar is also hidden leaving the absorption bar alone which makes it look not good. So not initializing it on those servers might be a good idea.
1999 lines
74 KiB
Lua
1999 lines
74 KiB
Lua
local S = minetest.get_translator(minetest.get_current_modname())
|
|
|
|
local EF = {}
|
|
mcl_potions.registered_effects = {}
|
|
local registered_effects = mcl_potions.registered_effects -- shorthand ref
|
|
|
|
-- effects affecting item speed utilize numerous hacks, so they have to be counted separately
|
|
local item_speed_effects = {}
|
|
|
|
local EFFECT_TYPES = 0
|
|
minetest.register_on_mods_loaded(function()
|
|
for _,_ in pairs(EF) do
|
|
EFFECT_TYPES = EFFECT_TYPES + 1
|
|
end
|
|
end)
|
|
|
|
-- ██████╗░███████╗░██████╗░██╗░██████╗████████╗███████╗██████╗
|
|
-- ██╔══██╗██╔════╝██╔════╝░██║██╔════╝╚══██╔══╝██╔════╝██╔══██╗
|
|
-- ██████╔╝█████╗░░██║░░██╗░██║╚█████╗░░░░██║░░░█████╗░░██████╔╝
|
|
-- ██╔══██╗██╔══╝░░██║░░╚██╗██║░╚═══██╗░░░██║░░░██╔══╝░░██╔══██╗
|
|
-- ██║░░██║███████╗╚██████╔╝██║██████╔╝░░░██║░░░███████╗██║░░██║
|
|
-- ╚═╝░░╚═╝╚══════╝░╚═════╝░╚═╝╚═════╝░░░░╚═╝░░░╚══════╝╚═╝░░╚═╝
|
|
--
|
|
-- ███████╗███████╗███████╗███████╗░█████╗░████████╗░██████╗
|
|
-- ██╔════╝██╔════╝██╔════╝██╔════╝██╔══██╗╚══██╔══╝██╔════╝
|
|
-- █████╗░░█████╗░░█████╗░░█████╗░░██║░░╚═╝░░░██║░░░╚█████╗░
|
|
-- ██╔══╝░░██╔══╝░░██╔══╝░░██╔══╝░░██║░░██╗░░░██║░░░░╚═══██╗
|
|
-- ███████╗██║░░░░░██║░░░░░███████╗╚█████╔╝░░░██║░░░██████╔╝
|
|
-- ╚══════╝╚═╝░░░░░╚═╝░░░░░╚══════╝░╚════╝░░░░╚═╝░░░╚═════╝░
|
|
|
|
local function generate_linear_lvl_to_fac(l1, l2)
|
|
local a = l2 - l1
|
|
local b = 2*l1 - l2
|
|
return function(level)
|
|
return (a*level + b)
|
|
end
|
|
end
|
|
|
|
local function generate_linear_fac_to_lvl(l1, l2)
|
|
local a = 1/(l2 - l1)
|
|
local b = -(2*l1 - l2) * a
|
|
return function(factor)
|
|
return math.round(a*factor + b)
|
|
end
|
|
end
|
|
|
|
local function generate_rational_lvl_to_fac(l1, l2)
|
|
local a = (l1 - l2) * 2
|
|
local b = 2*l2 - l1
|
|
return function(level)
|
|
if level == 0 then return 0 end
|
|
return (a/level + b)
|
|
end
|
|
end
|
|
|
|
local function generate_rational_fac_to_lvl(l1, l2)
|
|
local a = (l1 - l2) * 2
|
|
local b = 2*l2 - l1
|
|
return function(factor)
|
|
if (factor - b) == 0 then return math.huge end
|
|
return math.round(a/(factor - b))
|
|
end
|
|
end
|
|
|
|
local function generate_modifier_func(name, dmg_flag, mod_func, is_type)
|
|
if dmg_flag == "" then return function(object, damage, reason)
|
|
if EF[name][object] and not reason.flags.bypasses_magic then
|
|
return mod_func and mod_func(damage, EF[name][object]) or 0
|
|
end
|
|
end
|
|
elseif is_type then return function(object, damage, reason)
|
|
if EF[name][object] and not reason.flags.bypasses_magic and reason.type == dmg_flag then
|
|
return mod_func and mod_func(damage, EF[name][object]) or 0
|
|
end
|
|
end
|
|
else return function(object, damage, reason)
|
|
if EF[name][object] and not reason.flags.bypasses_magic and reason.flags[dmg_flag] then
|
|
return mod_func and mod_func(damage, EF[name][object]) or 0
|
|
end
|
|
end end
|
|
end
|
|
|
|
-- API - registers an effect
|
|
-- required parameters in def:
|
|
-- name - string - effect name in code
|
|
-- description - translated string - actual effect name in game
|
|
-- optional parameters in def:
|
|
-- get_tt - function(factor) - returns tooltip description text for use with potions
|
|
-- icon - string - file name of the effect icon in HUD - defaults to one based on name
|
|
-- res_condition - function(object) - returning true if target is to be resistant to the effect
|
|
-- on_start - function(object, factor) - called when dealing the effect
|
|
-- on_load - function(object, factor) - called on_joinplayer and on_activate
|
|
-- on_step - function(dtime, object, factor, duration) - running every step for all objects with this effect
|
|
-- on_hit_timer - function(object, factor, duration) - if defined runs a hit_timer depending on timer_uses_factor value
|
|
-- on_end - function(object) - called when the effect wears off
|
|
-- after_end - function(object) - called when the effect wears off, after purging the data of the effect
|
|
-- on_save_effect - function(object - called when the effect is to be serialized for saving (supposed to do cleanup)
|
|
-- particle_color - string - colorstring for particles - defaults to #3000EE
|
|
-- uses_factor - bool - whether factor affects the effect
|
|
-- lvl1_factor - number - factor for lvl1 effect - defaults to 1 if uses_factor
|
|
-- lvl2_factor - number - factor for lvl2 effect - defaults to 2 if uses_factor
|
|
-- timer_uses_factor - bool - whether hit_timer uses factor (uses_factor must be true) or a constant value (hit_timer_step must be defined)
|
|
-- hit_timer_step - float - interval between hit_timer hits
|
|
-- damage_modifier - string - damage flag of which damage is changed as defined by modifier_func, pass empty string for all damage
|
|
-- dmg_mod_is_type - bool - damage_modifier string is used as type instead of flag of damage, defaults to false
|
|
-- modifier_func - function(damage, effect_vals) - see damage_modifier, if not defined damage_modifier defaults to 100% resistance
|
|
-- modifier_priority - integer - priority passed when registering damage_modifier - defaults to -50
|
|
-- affects_item_speed - table
|
|
-- -- if provided, effect gets added to the item_speed_effects table, this should be true if the effect affects item speeds,
|
|
-- -- otherwise it won't work properly with other such effects (like haste and fatigue)
|
|
-- -- -- factor_is_positive - bool - whether values of factor between 0 and 1 should be considered +factor% or speed multiplier
|
|
-- -- -- - obviously +factor% is positive and speed multiplier is negative interpretation
|
|
-- -- -- - values of factor higher than 1 will have a positive effect regardless
|
|
-- -- -- - values of factor lower than 0 will have a negative effect regardless
|
|
-- -- -- - open an issue on our tracker if you have a usage that isn't supported by this API
|
|
function mcl_potions.register_effect(def)
|
|
local modname = minetest.get_current_modname()
|
|
local name = def.name
|
|
if name == nil then
|
|
error("Unable to register effect: name is nil")
|
|
end
|
|
if type(name) ~= "string" then
|
|
error("Unable to register effect: name is not a string")
|
|
end
|
|
if name == "list" or name == "heal" or name == "remove" or name == "clear" then
|
|
error("Unable to register effect: " .. name .. " is a reserved word")
|
|
end
|
|
if registered_effects[name] then
|
|
error("Effect named "..name.." already registered!")
|
|
end
|
|
if not def.description or type(def.description) ~= "string" then
|
|
error("Unable to register effect: description is not a string")
|
|
end
|
|
local pdef = {}
|
|
pdef.description = def.description
|
|
if not def.icon then
|
|
pdef.icon = modname.."_effect_"..name..".png"
|
|
else
|
|
pdef.icon = def.icon
|
|
end
|
|
pdef.get_tt = def.get_tt
|
|
pdef.res_condition = def.res_condition
|
|
pdef.on_start = def.on_start
|
|
pdef.on_load = def.on_load
|
|
pdef.on_step = def.on_step
|
|
pdef.on_hit_timer = def.on_hit_timer
|
|
pdef.on_end = def.on_end
|
|
pdef.on_save_effect = def.on_save_effect
|
|
if not def.particle_color then
|
|
pdef.particle_color = "#3000EE"
|
|
else
|
|
pdef.particle_color = def.particle_color
|
|
end
|
|
if def.uses_factor then
|
|
pdef.uses_factor = true
|
|
local l1 = def.lvl1_factor or 1
|
|
local l2 = def.lvl2_factor or 2*l1
|
|
if l1 < l2 then
|
|
pdef.level_to_factor = generate_linear_lvl_to_fac(l1, l2)
|
|
pdef.factor_to_level = generate_linear_fac_to_lvl(l1, l2)
|
|
pdef.inv_factor = false
|
|
elseif l1 > l2 then
|
|
pdef.level_to_factor = generate_rational_lvl_to_fac(l1, l2)
|
|
pdef.factor_to_level = generate_rational_fac_to_lvl(l1, l2)
|
|
pdef.inv_factor = true
|
|
else
|
|
error("Can't extrapolate levels from lvl1 and lvl2 bearing the same factor")
|
|
end
|
|
else
|
|
pdef.uses_factor = false
|
|
end
|
|
if def.on_hit_timer then
|
|
if def.timer_uses_factor then
|
|
if not def.uses_factor then error("Uses factor but does not use factor?") end
|
|
pdef.timer_uses_factor = true
|
|
else
|
|
if not def.hit_timer_step then error("If hit_timer does not use factor, hit_timer_step must be defined") end
|
|
pdef.timer_uses_factor = false
|
|
pdef.hit_timer_step = def.hit_timer_step
|
|
end
|
|
end
|
|
if def.damage_modifier then
|
|
mcl_damage.register_modifier(
|
|
generate_modifier_func(name, def.damage_modifier, def.modifier_func, def.dmg_mod_is_type),
|
|
def.modifier_priority or -50
|
|
)
|
|
end
|
|
registered_effects[name] = pdef
|
|
EF[name] = {}
|
|
item_speed_effects[name] = def.affects_item_speed
|
|
end
|
|
|
|
mcl_potions.register_effect({
|
|
name = "invisibility",
|
|
description = S("Invisiblity"),
|
|
get_tt = function(factor)
|
|
return S("body is invisible")
|
|
end,
|
|
on_start = function(object, factor)
|
|
mcl_potions.make_invisible(object, true)
|
|
end,
|
|
on_load = function(object, factor)
|
|
mcl_potions.make_invisible(object, true)
|
|
end,
|
|
on_end = function(object)
|
|
mcl_potions.make_invisible(object, false)
|
|
end,
|
|
particle_color = "#7F8392",
|
|
uses_factor = false,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "poison",
|
|
description = S("Poison"),
|
|
get_tt = function(factor)
|
|
return S("-1 HP / @1 s", factor)
|
|
end,
|
|
res_condition = function(object)
|
|
local entity = object:get_luaentity()
|
|
return (entity and (entity.harmed_by_heal or string.find(entity.name, "spider")))
|
|
end,
|
|
on_hit_timer = function(object, factor, duration)
|
|
if mcl_util.get_hp(object) - 1 > 0 then
|
|
mcl_util.deal_damage(object, 1, {type = "magic"})
|
|
end
|
|
end,
|
|
particle_color = "#4E9331",
|
|
uses_factor = true,
|
|
lvl1_factor = 1.25,
|
|
lvl2_factor = 0.6,
|
|
timer_uses_factor = true,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "regeneration",
|
|
description = S("Regeneration"),
|
|
get_tt = function(factor)
|
|
return S("+1 HP / @1 s", factor)
|
|
end,
|
|
res_condition = function(object)
|
|
local entity = object:get_luaentity()
|
|
return (entity and entity.harmed_by_heal)
|
|
end,
|
|
on_hit_timer = function(object, factor, duration)
|
|
local entity = object:get_luaentity()
|
|
if object:is_player() then
|
|
object:set_hp(math.min(object:get_properties().hp_max or 20, object:get_hp() + 1), { type = "set_hp", other = "regeneration" })
|
|
elseif entity and entity.is_mob then
|
|
entity.health = math.min(entity.hp_max, entity.health + 1)
|
|
end
|
|
end,
|
|
particle_color = "#CD5CAB",
|
|
uses_factor = true,
|
|
lvl1_factor = 2.5,
|
|
lvl2_factor = 1.25,
|
|
timer_uses_factor = true,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "strength",
|
|
description = S("Strength"),
|
|
get_tt = function(factor)
|
|
return S("+@1% melee damage", 100*(factor-1))
|
|
end,
|
|
particle_color = "#932423",
|
|
uses_factor = true,
|
|
lvl1_factor = 1.3,
|
|
lvl2_factor = 1.6,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "weakness",
|
|
description = S("Weakness"),
|
|
get_tt = function(factor)
|
|
return S("-@1% melee damage", 100*(1-factor))
|
|
end,
|
|
particle_color = "#485D48",
|
|
uses_factor = true,
|
|
lvl1_factor = 0.8,
|
|
lvl2_factor = 0.6,
|
|
})
|
|
|
|
-- implementation of strength and weakness effects
|
|
-- mobs have this implemented in mcl_mobs/combat.lua in mob_class:on_punch()
|
|
mcl_damage.register_modifier(function(object, damage, reason)
|
|
if reason.direct and reason.direct == reason.source then
|
|
local hitter = reason.direct
|
|
local strength = EF.strength[hitter]
|
|
local weakness = EF.weakness[hitter]
|
|
if not strength and not weakness then return end
|
|
local str_fac = strength and strength.factor or 1
|
|
local weak_fac = weakness and weakness.factor or 1
|
|
return damage * str_fac * weak_fac
|
|
end
|
|
end, 0)
|
|
|
|
mcl_potions.register_effect({
|
|
name = "water_breathing",
|
|
description = S("Water Breathing"),
|
|
get_tt = function(factor)
|
|
return S("limitless breathing under water")
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO add support for breath setting for mobs
|
|
end,
|
|
on_step = function(dtime, object, factor, duration)
|
|
if object:get_breath() then
|
|
hb.hide_hudbar(object, "breath")
|
|
if object:get_breath() < 10 then object:set_breath(10) end
|
|
end
|
|
end,
|
|
particle_color = "#2E5299",
|
|
uses_factor = false,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "dolphin_grace",
|
|
description = S("Dolphin's Grace"),
|
|
get_tt = function(factor)
|
|
return S("swimming gracefully")
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO needs mob physics factor API
|
|
end,
|
|
on_hit_timer = function(object, factor, duration)
|
|
local node = minetest.get_node_or_nil(object:get_pos())
|
|
if node and minetest.registered_nodes[node.name]
|
|
and minetest.get_item_group(node.name, "liquid") ~= 0 then
|
|
playerphysics.add_physics_factor(object, "speed", "mcl_potions:dolphin", 2)
|
|
else
|
|
playerphysics.remove_physics_factor(object, "speed", "mcl_potions:dolphin", 2)
|
|
end
|
|
end,
|
|
particle_color = "#6AABFD",
|
|
uses_factor = false,
|
|
timer_uses_factor = false,
|
|
hit_timer_step = 1,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "leaping",
|
|
description = S("Leaping"),
|
|
get_tt = function(factor)
|
|
if factor > 0 then return S("+@1% jumping power", math.floor(factor*100)) end
|
|
return S("-@1% jumping power", math.floor(-factor*100))
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO needs mob physics factor API
|
|
end,
|
|
on_start = function(object, factor)
|
|
playerphysics.add_physics_factor(object, "jump", "mcl_potions:leaping", 1+factor)
|
|
end,
|
|
on_end = function(object)
|
|
playerphysics.remove_physics_factor(object, "jump", "mcl_potions:leaping")
|
|
end,
|
|
particle_color = "#22FF4C",
|
|
uses_factor = true,
|
|
lvl1_factor = 0.5,
|
|
lvl2_factor = 1,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "slow_falling",
|
|
description = S("Slow Falling"),
|
|
get_tt = function(factor)
|
|
return S("decreases gravity effects")
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO needs mob physics factor API
|
|
end,
|
|
on_start = function(object, factor)
|
|
playerphysics.add_physics_factor(object, "gravity", "mcl_potions:slow_falling", 0.5)
|
|
end,
|
|
on_step = function(dtime, object, factor, duration)
|
|
local vel = object:get_velocity()
|
|
if not vel then return end
|
|
vel = vel.y
|
|
if vel < -3 then object:add_velocity(vector.new(0,-3-vel,0)) end
|
|
end,
|
|
on_end = function(object)
|
|
playerphysics.remove_physics_factor(object, "gravity", "mcl_potions:slow_falling")
|
|
end,
|
|
particle_color = "#ACCCFF",
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "swiftness",
|
|
description = S("Swiftness"),
|
|
get_tt = function(factor)
|
|
return S("+@1% running speed", math.floor(factor*100))
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO needs mob physics factor API
|
|
end,
|
|
on_start = function(object, factor)
|
|
playerphysics.add_physics_factor(object, "speed", "mcl_potions:swiftness", 1+factor)
|
|
end,
|
|
on_end = function(object)
|
|
playerphysics.remove_physics_factor(object, "speed", "mcl_potions:swiftness")
|
|
end,
|
|
particle_color = "#7CAFC6",
|
|
uses_factor = true,
|
|
lvl1_factor = 0.2,
|
|
lvl2_factor = 0.4,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "slowness",
|
|
description = S("Slowness"),
|
|
get_tt = function(factor)
|
|
return S("-@1% running speed", math.floor(factor*100))
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO needs mob physics factor API
|
|
end,
|
|
on_start = function(object, factor)
|
|
playerphysics.add_physics_factor(object, "speed", "mcl_potions:slowness", 1-factor)
|
|
end,
|
|
on_end = function(object)
|
|
playerphysics.remove_physics_factor(object, "speed", "mcl_potions:slowness")
|
|
end,
|
|
particle_color = "#5A6C81",
|
|
uses_factor = true,
|
|
lvl1_factor = 0.15,
|
|
lvl2_factor = 0.3,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "levitation",
|
|
description = S("Levitation"),
|
|
get_tt = function(factor)
|
|
return S("moves body upwards at @1 nodes/s", factor)
|
|
end,
|
|
on_step = function(dtime, object, factor, duration)
|
|
local vel = object:get_velocity()
|
|
if not vel then return end
|
|
vel = vel.y
|
|
if vel<factor then object:add_velocity(vector.new(0,factor,0)) end
|
|
end,
|
|
particle_color = "#420E7E",
|
|
uses_factor = true,
|
|
lvl1_factor = 0.9,
|
|
lvl2_factor = 1.8,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "night_vision",
|
|
description = S("Night Vision"),
|
|
get_tt = function(factor)
|
|
return S("improved vision during the night")
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO what should it do for mobs?
|
|
end,
|
|
on_start = function(object, factor)
|
|
object:get_meta():set_int("night_vision", 1)
|
|
mcl_weather.skycolor.update_sky_color({object})
|
|
end,
|
|
on_load = function(object, factor)
|
|
object:get_meta():set_int("night_vision", 1)
|
|
mcl_weather.skycolor.update_sky_color({object})
|
|
end,
|
|
on_step = function(dtime, object, factor, duration)
|
|
mcl_weather.skycolor.update_sky_color({object})
|
|
end,
|
|
on_end = function(object)
|
|
local meta = object:get_meta()
|
|
if not meta then return end
|
|
meta:set_int("night_vision", 0)
|
|
mcl_weather.skycolor.update_sky_color({object})
|
|
end,
|
|
particle_color = "#1F1FA1",
|
|
uses_factor = false,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "darkness",
|
|
description = S("Darkness"),
|
|
get_tt = function(factor)
|
|
return S("surrounded by darkness").."\n"..S("not seeing anything beyond @1 nodes", factor)
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO what should it do for mobs?
|
|
end,
|
|
on_start = function(object, factor)
|
|
object:get_meta():set_int("darkness", 1)
|
|
mcl_weather.skycolor.update_sky_color({object})
|
|
object:set_sky({fog = {
|
|
fog_distance = factor,
|
|
}})
|
|
EF.darkness[object].flash = 0.6
|
|
end,
|
|
on_step = function(dtime, object, factor, duration)
|
|
if object:get_meta():get_int("night_vision") ~= 1 then
|
|
local flash = EF.darkness[object].flash
|
|
if flash < 0.2 then EF.darkness[object].flashdir = true
|
|
elseif flash > 0.6 then EF.darkness[object].flashdir = false end
|
|
flash = EF.darkness[object].flashdir and (flash + dtime) or (flash - dtime)
|
|
object:set_sky({fog = {
|
|
fog_start = flash,
|
|
}})
|
|
EF.darkness[object].flash = flash
|
|
else
|
|
object:set_sky({fog = {
|
|
fog_start = 0.9,
|
|
}})
|
|
end
|
|
mcl_weather.skycolor.update_sky_color({object})
|
|
end,
|
|
on_end = function(object)
|
|
local meta = object:get_meta()
|
|
if not meta then return end
|
|
meta:set_int("darkness", 0)
|
|
mcl_weather.skycolor.update_sky_color({object})
|
|
object:set_sky({fog = {
|
|
fog_distance = -1,
|
|
fog_start = -1,
|
|
}})
|
|
end,
|
|
particle_color = "#000000",
|
|
uses_factor = true,
|
|
lvl1_factor = 30,
|
|
lvl2_factor = 20,
|
|
})
|
|
|
|
local GLOW_DISTANCE = 30
|
|
local CLOSE_GLOW_LIMIT = 3
|
|
local MIN_GLOW_SCALE = 1
|
|
local MAX_GLOW_SCALE = 4
|
|
local SCALE_DIFF = MAX_GLOW_SCALE - MIN_GLOW_SCALE
|
|
local SCALE_FACTOR = (GLOW_DISTANCE - CLOSE_GLOW_LIMIT) / SCALE_DIFF
|
|
local abs = math.abs
|
|
mcl_potions.register_effect({
|
|
name = "glowing",
|
|
description = S("Glowing"),
|
|
get_tt = function(factor)
|
|
return S("more visible at all times")
|
|
end,
|
|
on_start = function(object, factor)
|
|
EF.glowing[object].waypoints = {}
|
|
end,
|
|
on_step = function(dtime, object, factor, duration)
|
|
local pos = object:get_pos()
|
|
if not pos then return end
|
|
local x, y, z = pos.x, pos.y, pos.z
|
|
for _, player in pairs(minetest.get_connected_players()) do
|
|
local pp = player:get_pos()
|
|
if pp and player ~= object then
|
|
local hud_id = EF.glowing[object].waypoints[player]
|
|
if abs(pp.x-x) < GLOW_DISTANCE and abs(pp.y-y) < GLOW_DISTANCE
|
|
and abs(pp.z-z) < GLOW_DISTANCE then
|
|
local distance = vector.distance(pos, pp)
|
|
local scale
|
|
if distance <= CLOSE_GLOW_LIMIT then scale = MAX_GLOW_SCALE
|
|
elseif distance >= GLOW_DISTANCE then scale = MIN_GLOW_SCALE
|
|
else scale = (GLOW_DISTANCE - distance) / SCALE_FACTOR + MIN_GLOW_SCALE end
|
|
if hud_id then
|
|
player:hud_change(hud_id, "world_pos", pos)
|
|
player:hud_change(hud_id, "scale", {x = scale, y = scale})
|
|
else
|
|
EF.glowing[object].waypoints[player] = player:hud_add({
|
|
hud_elem_type = "image_waypoint",
|
|
position = {x = 0.5, y = 0.5},
|
|
scale = {x = scale, y = scale},
|
|
text = "mcl_potions_glow_waypoint.png",
|
|
alignment = {x = 0, y = -1},
|
|
world_pos = pos,
|
|
})
|
|
end
|
|
elseif hud_id then
|
|
player:hud_remove(hud_id)
|
|
EF.glowing[object].waypoints[player] = nil
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
on_end = function(object)
|
|
for player, hud_id in pairs(EF.glowing[object].waypoints) do
|
|
if player:get_pos() then player:hud_remove(hud_id) end
|
|
end
|
|
end,
|
|
on_save_effect = function(object)
|
|
for player, hud_id in pairs(EF.glowing[object].waypoints) do
|
|
if player:get_pos() then player:hud_remove(hud_id) end
|
|
end
|
|
EF.glowing[object].waypoints = {}
|
|
end,
|
|
particle_color = "#FFFF77",
|
|
uses_factor = false,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "health_boost",
|
|
description = S("Health Boost"),
|
|
get_tt = function(factor)
|
|
return S("HP increased by @1", factor)
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO needs mob HP modifier API?
|
|
end,
|
|
on_start = function(object, factor)
|
|
object:set_properties({hp_max = minetest.PLAYER_MAX_HP_DEFAULT+factor})
|
|
end,
|
|
on_end = function(object)
|
|
object:set_properties({hp_max = minetest.PLAYER_MAX_HP_DEFAULT})
|
|
end,
|
|
particle_color = "#FF2222",
|
|
uses_factor = true,
|
|
lvl1_factor = 4,
|
|
lvl2_factor = 8,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "absorption",
|
|
description = S("Absorption"),
|
|
get_tt = function(factor)
|
|
return S("absorbs up to @1 incoming damage", factor)
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO dmg modifiers don't work for mobs
|
|
end,
|
|
on_start = function(object, factor)
|
|
hb.change_hudbar(object, "absorption", factor, (math.floor(factor/20-0.05)+1)*20)
|
|
EF.absorption[object].absorb = factor
|
|
end,
|
|
on_load = function(object, factor)
|
|
minetest.after(0, function() hb.change_hudbar(object, "absorption", nil, (math.floor(factor/20-0.05)+1)*20) end)
|
|
end,
|
|
on_step = function(dtime, object, factor, duration)
|
|
hb.change_hudbar(object, "absorption", EF.absorption[object].absorb)
|
|
end,
|
|
on_end = function(object)
|
|
hb.change_hudbar(object, "absorption", 0)
|
|
end,
|
|
particle_color = "#B59500",
|
|
uses_factor = true,
|
|
lvl1_factor = 4,
|
|
lvl2_factor = 8,
|
|
damage_modifier = "",
|
|
modifier_func = function(damage, effect_vals)
|
|
local absorb = effect_vals.absorb
|
|
local carryover = 0
|
|
if absorb > damage then
|
|
effect_vals.absorb = absorb - damage
|
|
else
|
|
carryover = damage - absorb
|
|
effect_vals.absorb = 0
|
|
end
|
|
return carryover
|
|
end,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "fire_resistance",
|
|
description = S("Fire Resistance"),
|
|
get_tt = function(factor)
|
|
return S("resistance to fire damage")
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO dmg modifiers don't work for mobs
|
|
end,
|
|
particle_color = "#E49A3A",
|
|
uses_factor = false,
|
|
damage_modifier = "is_fire",
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "resistance",
|
|
description = S("Resistance"),
|
|
get_tt = function(factor)
|
|
return S("resist @1% of incoming damage", math.floor(factor*100))
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO dmg modifiers don't work for mobs
|
|
end,
|
|
particle_color = "#2552A5",
|
|
uses_factor = true,
|
|
lvl1_factor = 0.2,
|
|
lvl2_factor = 0.4,
|
|
damage_modifier = "",
|
|
modifier_func = function(damage, effect_vals)
|
|
return damage - (effect_vals.factor)*damage
|
|
end,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "luck",
|
|
description = S("Luck"),
|
|
particle_color = "#7BFF42",
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO what should it do for mobs?
|
|
end,
|
|
on_start = function(object, factor)
|
|
mcl_luck.apply_luck_modifier(object:get_player_name(), "mcl_potions:luck", factor)
|
|
end,
|
|
on_load = function(object, factor)
|
|
mcl_luck.apply_luck_modifier(object:get_player_name(), "mcl_potions:luck", factor)
|
|
end,
|
|
on_end = function(object)
|
|
mcl_luck.remove_luck_modifier(object:get_player_name(), "mcl_potions:luck")
|
|
end,
|
|
uses_factor = true,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "bad_luck",
|
|
description = S("Bad Luck"),
|
|
particle_color = "#887343",
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO what should it do for mobs?
|
|
end,
|
|
on_start = function(object, factor)
|
|
mcl_luck.apply_luck_modifier(object:get_player_name(), "mcl_potions:bad_luck", -factor)
|
|
end,
|
|
on_load = function(object, factor)
|
|
mcl_luck.apply_luck_modifier(object:get_player_name(), "mcl_potions:bad_luck", -factor)
|
|
end,
|
|
on_end = function(object)
|
|
mcl_luck.remove_luck_modifier(object:get_player_name(), "mcl_potions:bad_luck")
|
|
end,
|
|
uses_factor = true,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "bad_omen",
|
|
description = S("Bad Omen"),
|
|
get_tt = function(factor)
|
|
return S("danger is imminent")
|
|
end,
|
|
particle_color = "#472331",
|
|
uses_factor = true,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "hero_of_village",
|
|
description = S("Hero of the Village"),
|
|
particle_color = "#006D2A",
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "withering",
|
|
description = S("Withering"),
|
|
get_tt = function(factor)
|
|
return S("-1 HP / @1 s, can kill", factor)
|
|
end,
|
|
res_condition = function(object)
|
|
local entity = object:get_luaentity()
|
|
return (entity and string.find(entity.name, "wither"))
|
|
end,
|
|
on_hit_timer = function(object, factor, duration)
|
|
if object:is_player() or object:get_luaentity() then
|
|
mcl_util.deal_damage(object, 1, {type = "magic"})
|
|
end
|
|
end,
|
|
particle_color = "#292929",
|
|
uses_factor = true,
|
|
lvl1_factor = 2,
|
|
lvl2_factor = 1,
|
|
timer_uses_factor = true,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "frost",
|
|
description = S("Frost"),
|
|
get_tt = function(factor)
|
|
return S("-1 HP / 1 s, can kill, -@1% running speed", math.floor(factor*100))
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO needs mob physics factor API
|
|
end,
|
|
on_start = function(object, factor)
|
|
mcl_burning.extinguish(object)
|
|
playerphysics.add_physics_factor(object, "speed", "mcl_potions:frost", 1-factor)
|
|
if EF.frost[object].vignette then return end
|
|
EF.frost[object].vignette = object:hud_add({
|
|
hud_elem_type = "image",
|
|
position = {x = 0.5, y = 0.5},
|
|
scale = {x = -101, y = -101},
|
|
text = "mcl_potions_frost_hud.png",
|
|
z_index = -400
|
|
})
|
|
end,
|
|
on_load = function(object, factor)
|
|
EF.frost[object].vignette = object:hud_add({
|
|
hud_elem_type = "image",
|
|
position = {x = 0.5, y = 0.5},
|
|
scale = {x = -101, y = -101},
|
|
text = "mcl_potions_frost_hud.png",
|
|
z_index = -400
|
|
})
|
|
end,
|
|
on_hit_timer = function(object, factor, duration)
|
|
if object:is_player() or object:get_luaentity() then
|
|
mcl_util.deal_damage(object, 1, {type = "magic"})
|
|
end
|
|
end,
|
|
on_end = function(object)
|
|
playerphysics.remove_physics_factor(object, "speed", "mcl_potions:frost")
|
|
if not EF.frost[object] then return end
|
|
object:hud_remove(EF.frost[object].vignette)
|
|
end,
|
|
particle_color = "#5B7DAA",
|
|
uses_factor = true,
|
|
lvl1_factor = 0.1,
|
|
lvl2_factor = 0.2,
|
|
timer_uses_factor = false,
|
|
hit_timer_step = 1,
|
|
damage_modifier = "is_fire",
|
|
modifier_func = function(damage, effect_vals)
|
|
effect_vals.timer = effect_vals.dur
|
|
return 0
|
|
end,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "blindness",
|
|
description = "Blindness",
|
|
get_tt = function(factor)
|
|
return S("impaired sight")
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO what should it do for mobs?
|
|
end,
|
|
on_start = function(object, factor)
|
|
EF.blindness[object].vignette = object:hud_add({
|
|
hud_elem_type = "image",
|
|
position = {x = 0.5, y = 0.5},
|
|
scale = {x = -101, y = -101},
|
|
text = "mcl_potions_blindness_hud.png",
|
|
z_index = -401
|
|
})
|
|
mcl_fovapi.apply_modifier(object, "mcl_potions:blindness")
|
|
end,
|
|
on_load = function(object, factor)
|
|
EF.blindness[object].vignette = object:hud_add({
|
|
hud_elem_type = "image",
|
|
position = {x = 0.5, y = 0.5},
|
|
scale = {x = -101, y = -101},
|
|
text = "mcl_potions_blindness_hud.png",
|
|
z_index = -401
|
|
})
|
|
mcl_fovapi.apply_modifier(object, "mcl_potions:blindness")
|
|
end,
|
|
on_end = function(object)
|
|
mcl_fovapi.remove_modifier(object, "mcl_potions:blindness")
|
|
if not EF.blindness[object] then return end
|
|
object:hud_remove(EF.blindness[object].vignette)
|
|
end,
|
|
particle_color = "#686868",
|
|
uses_factor = false,
|
|
})
|
|
mcl_fovapi.register_modifier({
|
|
name = "mcl_potions:blindness",
|
|
fov_factor = 0.6,
|
|
time = 1,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "nausea",
|
|
description = S("Nausea"),
|
|
get_tt = function(factor)
|
|
return S("not feeling very well...").."\n"..S("frequency: @1 / 1 s", factor)
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO what should it do for mobs?
|
|
end,
|
|
on_start = function(object, factor)
|
|
object:set_lighting({
|
|
saturation = -1.0,
|
|
})
|
|
end,
|
|
on_hit_timer = function(object, factor, duration)
|
|
if EF.nausea[object].high then
|
|
mcl_fovapi.remove_modifier(object, "mcl_potions:nausea_high", factor)
|
|
mcl_fovapi.apply_modifier(object, "mcl_potions:nausea_low", factor)
|
|
EF.nausea[object].high = false
|
|
else
|
|
mcl_fovapi.apply_modifier(object, "mcl_potions:nausea_high", factor)
|
|
mcl_fovapi.remove_modifier(object, "mcl_potions:nausea_low", factor)
|
|
EF.nausea[object].high = true
|
|
end
|
|
end,
|
|
on_end = function(object)
|
|
object:set_lighting({
|
|
saturation = 1.0,
|
|
})
|
|
mcl_fovapi.remove_modifier(object, "mcl_potions:nausea_high")
|
|
mcl_fovapi.remove_modifier(object, "mcl_potions:nausea_low")
|
|
end,
|
|
particle_color = "#60AA30",
|
|
uses_factor = true,
|
|
lvl1_factor = 2,
|
|
lvl2_factor = 1,
|
|
timer_uses_factor = true,
|
|
})
|
|
mcl_fovapi.register_modifier({
|
|
name = "mcl_potions:nausea_high",
|
|
fov_factor = 2.2,
|
|
time = 1,
|
|
})
|
|
mcl_fovapi.register_modifier({
|
|
name = "mcl_potions:nausea_low",
|
|
fov_factor = 0.2,
|
|
time = 1,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "food_poisoning",
|
|
description = S("Food Poisoning"),
|
|
get_tt = function(factor)
|
|
return S("exhausts by @1 per second", factor)
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO what should it do for mobs?
|
|
end,
|
|
on_start = function(object, factor)
|
|
hb.change_hudbar(object, "hunger", nil, nil, "mcl_hunger_icon_foodpoison.png", nil, "mcl_hunger_bar_foodpoison.png")
|
|
if mcl_hunger.debug then
|
|
hb.change_hudbar(object, "exhaustion", nil, nil, nil, nil, "mcl_hunger_bar_foodpoison.png")
|
|
end
|
|
end,
|
|
on_load = function(object, factor) -- TODO refactor and add hunger bar modifier API
|
|
hb.change_hudbar(object, "hunger", nil, nil, "mcl_hunger_icon_foodpoison.png", nil, "mcl_hunger_bar_foodpoison.png")
|
|
if mcl_hunger.debug then
|
|
hb.change_hudbar(object, "exhaustion", nil, nil, nil, nil, "mcl_hunger_bar_foodpoison.png")
|
|
end
|
|
end,
|
|
on_step = function(dtime, object, factor, duration)
|
|
mcl_hunger.exhaust(object:get_player_name(), dtime*factor)
|
|
end,
|
|
on_end = function(object)
|
|
mcl_hunger.reset_bars_poison_hunger(object)
|
|
end,
|
|
particle_color = "#83A061",
|
|
uses_factor = true,
|
|
lvl1_factor = 100,
|
|
lvl2_factor = 200,
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "saturation",
|
|
description = S("Saturation"),
|
|
get_tt = function(factor)
|
|
return S("saturates by @1 per second", factor)
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO what should it do for mobs?
|
|
end,
|
|
on_step = function(dtime, object, factor, duration)
|
|
mcl_hunger.set_hunger(object, math.min(mcl_hunger.get_hunger(object)+dtime*factor, 20))
|
|
mcl_hunger.saturate(object:get_player_name(), dtime*factor)
|
|
end,
|
|
particle_color = "#CEAE29",
|
|
uses_factor = true,
|
|
})
|
|
|
|
-- constants relevant for effects altering mining and attack speed
|
|
local LONGEST_MINING_TIME = 300
|
|
local LONGEST_PUNCH_INTERVAL = 10
|
|
mcl_potions.LONGEST_MINING_TIME = LONGEST_MINING_TIME
|
|
mcl_potions.LONGEST_PUNCH_INTERVAL = LONGEST_PUNCH_INTERVAL
|
|
|
|
function mcl_potions.apply_haste_fatigue(toolcaps, h_fac, f_fac)
|
|
if f_fac == 0 then
|
|
local fpi = toolcaps.full_punch_interval
|
|
toolcaps.full_punch_interval = fpi > LONGEST_PUNCH_INTERVAL and fpi or LONGEST_PUNCH_INTERVAL
|
|
else
|
|
toolcaps.full_punch_interval = toolcaps.full_punch_interval / (1+h_fac) / f_fac
|
|
end
|
|
for name, group in pairs(toolcaps.groupcaps) do
|
|
local t = group.times
|
|
for i=1, #t do
|
|
if f_fac == 0 then
|
|
t[i] = t[i] > LONGEST_MINING_TIME and t[i] or LONGEST_MINING_TIME
|
|
else
|
|
local old_time = t[i]
|
|
t[i] = t[i] / (1+h_fac) / f_fac
|
|
if old_time < LONGEST_MINING_TIME and t[i] > LONGEST_MINING_TIME then
|
|
t[i] = LONGEST_MINING_TIME
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return toolcaps
|
|
end
|
|
|
|
function mcl_potions.hf_update_internal(hand, object)
|
|
-- TODO add a check for creative mode?
|
|
local meta = hand:get_meta()
|
|
local h_fac = mcl_potions.get_total_haste(object)
|
|
local f_fac = mcl_potions.get_total_fatigue(object)
|
|
local toolcaps = hand:get_tool_capabilities()
|
|
meta:set_tool_capabilities(mcl_potions.apply_haste_fatigue(toolcaps, h_fac, f_fac))
|
|
return hand
|
|
end
|
|
|
|
local function haste_fatigue_hand_update(object)
|
|
local inventory = object:get_inventory()
|
|
if not inventory or inventory:get_size("hand") < 1 then return end
|
|
local hand = inventory:get_stack("hand", 1)
|
|
inventory:set_stack("hand", 1, mcl_potions.hf_update_internal(hand, object))
|
|
end
|
|
|
|
mcl_potions.register_effect({
|
|
name = "haste",
|
|
description = S("Haste"),
|
|
get_tt = function(factor)
|
|
return S("+@1% mining and attack speed", math.floor(factor*100))
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO needs mob API support
|
|
end,
|
|
on_start = haste_fatigue_hand_update,
|
|
after_end = function(object)
|
|
haste_fatigue_hand_update(object)
|
|
mcl_potions._reset_haste_fatigue_item_meta(object)
|
|
end,
|
|
particle_color = "#FFFF00",
|
|
uses_factor = true,
|
|
lvl1_factor = 0.2,
|
|
lvl2_factor = 0.4,
|
|
affects_item_speed = {factor_is_positive = true},
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "fatigue",
|
|
description = S("Fatigue"),
|
|
get_tt = function(factor)
|
|
return S("-@1% mining and attack speed", math.floor((1-factor)*100))
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO needs mob API support
|
|
end,
|
|
on_start = haste_fatigue_hand_update,
|
|
after_end = function(object)
|
|
haste_fatigue_hand_update(object)
|
|
mcl_potions._reset_haste_fatigue_item_meta(object)
|
|
end,
|
|
particle_color = "#64643D",
|
|
uses_factor = true,
|
|
lvl1_factor = 0.3,
|
|
lvl2_factor = 0.09,
|
|
affects_item_speed = {},
|
|
})
|
|
|
|
mcl_potions.register_effect({
|
|
name = "conduit_power",
|
|
description = S("Conduit Power"),
|
|
get_tt = function(factor)
|
|
return S("+@1% mining and attack speed in water").."\n"..S("limitless breathing under water", math.floor(factor*100))
|
|
end,
|
|
res_condition = function(object)
|
|
return (not object:is_player()) -- TODO needs mob API support
|
|
end,
|
|
on_start = haste_fatigue_hand_update,
|
|
on_step = function(dtime, object, factor, duration)
|
|
if not object:is_player() then return end
|
|
local node = minetest.get_node_or_nil(object:get_pos())
|
|
if node and minetest.registered_nodes[node.name]
|
|
and minetest.get_item_group(node.name, "liquid") ~= 0
|
|
and minetest.get_item_group(node.name, "water") ~= 0 then
|
|
EF.conduit_power[object].blocked = nil
|
|
if object:get_breath() then
|
|
hb.hide_hudbar(object, "breath")
|
|
if object:get_breath() < 10 then object:set_breath(10) end
|
|
end
|
|
-- TODO implement improved underwater vision with this effect
|
|
else
|
|
EF.conduit_power[object].blocked = true
|
|
end
|
|
end,
|
|
after_end = function(object)
|
|
haste_fatigue_hand_update(object)
|
|
mcl_potions._reset_haste_fatigue_item_meta(object)
|
|
end,
|
|
particle_color = "#1FB1BA",
|
|
uses_factor = true,
|
|
lvl1_factor = 0.2,
|
|
lvl2_factor = 0.4,
|
|
affects_item_speed = {factor_is_positive = true},
|
|
})
|
|
|
|
-- implementation of haste and fatigue effects
|
|
function mcl_potions.update_haste_and_fatigue(player)
|
|
if mcl_gamemode.get_gamemode(player) == "creative" then return end
|
|
local item = player:get_wielded_item()
|
|
local meta = item:get_meta()
|
|
local item_haste = meta:get_float("mcl_potions:haste")
|
|
local item_fatig = 1 - meta:get_float("mcl_potions:fatigue")
|
|
local h_fac = mcl_potions.get_total_haste(player)
|
|
local f_fac = mcl_potions.get_total_fatigue(player)
|
|
if item_haste ~= h_fac or item_fatig ~= f_fac then
|
|
if h_fac ~= 0 then meta:set_float("mcl_potions:haste", h_fac)
|
|
else meta:set_string("mcl_potions:haste", "") end
|
|
if f_fac ~= 1 then meta:set_float("mcl_potions:fatigue", 1 - f_fac)
|
|
else meta:set_string("mcl_potions:fatigue", "") end
|
|
meta:set_tool_capabilities()
|
|
meta:set_string("groupcaps_hash","")
|
|
mcl_enchanting.update_groupcaps(item)
|
|
if h_fac == 0 and f_fac == 1 then
|
|
player:set_wielded_item(item)
|
|
return
|
|
end
|
|
local toolcaps = item:get_tool_capabilities()
|
|
meta:set_tool_capabilities(mcl_potions.apply_haste_fatigue(toolcaps, h_fac, f_fac))
|
|
player:set_wielded_item(item)
|
|
end
|
|
haste_fatigue_hand_update(player, h_fac, f_fac)
|
|
end
|
|
minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing)
|
|
mcl_potions.update_haste_and_fatigue(puncher)
|
|
end)
|
|
minetest.register_on_punchplayer(function(player, hitter)
|
|
if not hitter:is_player() then return end -- TODO implement haste and fatigue support for mobs?
|
|
mcl_potions.update_haste_and_fatigue(hitter)
|
|
end)
|
|
-- update when hitting mob implemented in mcl_mobs/combat.lua
|
|
|
|
|
|
|
|
-- ██╗░░░██╗██████╗░██████╗░░█████╗░████████╗███████╗
|
|
-- ██║░░░██║██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██╔════╝
|
|
-- ██║░░░██║██████╔╝██║░░██║███████║░░░██║░░░█████╗░░
|
|
-- ██║░░░██║██╔═══╝░██║░░██║██╔══██║░░░██║░░░██╔══╝░░
|
|
-- ╚██████╔╝██║░░░░░██████╦╝██║░░██║░░░██║░░░███████╗
|
|
-- ░╚═════╝░╚═╝░░░░░╚═════╝░╚═╝░░╚═╝░░░╚═╝░░░╚══════╝
|
|
--
|
|
-- ██╗░░██╗██╗░░░██╗██████╗░
|
|
-- ██║░░██║██║░░░██║██╔══██╗
|
|
-- ███████║██║░░░██║██║░░██║
|
|
-- ██╔══██║██║░░░██║██║░░██║
|
|
-- ██║░░██║╚██████╔╝██████╦╝
|
|
-- ╚═╝░░╚═╝░╚═════╝░╚═════╝░
|
|
|
|
hb.register_hudbar("absorption", 0xFFFFFF, S("Absorption"), {bar = "[fill:2x16:#B59500", icon = "mcl_potions_icon_absorb.png"}, 0, 0, 0, false)
|
|
|
|
local hp_hudbar_modifiers = {}
|
|
|
|
-- API - registers a HP hudbar modifier
|
|
-- required parameters in def:
|
|
-- predicate - function(player) - returns true if player fulfills the requirements (eg. has the effects) for the hudbar look
|
|
-- icon - string - name of the icon to which the modifier should change the HP hudbar heart
|
|
-- priority - signed int - lower gets checked first, and first fulfilled predicate applies its modifier
|
|
function mcl_potions.register_hp_hudbar_modifier(def)
|
|
if type(def.predicate) ~= "function" then error("Predicate must be a function") end
|
|
if not def.icon then error("No icon provided") end
|
|
if not def.priority then error("No priority provided") end
|
|
table.insert(hp_hudbar_modifiers, {
|
|
predicate = def.predicate,
|
|
icon = def.icon,
|
|
priority = def.priority,
|
|
})
|
|
table.sort(hp_hudbar_modifiers, function(a, b) return a.priority <= b.priority end)
|
|
end
|
|
|
|
mcl_potions.register_hp_hudbar_modifier({
|
|
predicate = function(player)
|
|
if EF.withering[player] and EF.regeneration[player] then return true end
|
|
end,
|
|
icon = "mcl_potions_icon_regen_wither.png",
|
|
priority = -30,
|
|
})
|
|
|
|
mcl_potions.register_hp_hudbar_modifier({
|
|
predicate = function(player)
|
|
if EF.withering[player] then return true end
|
|
end,
|
|
icon = "mcl_potions_icon_wither.png",
|
|
priority = -20,
|
|
})
|
|
|
|
mcl_potions.register_hp_hudbar_modifier({
|
|
predicate = function(player)
|
|
if EF.poison[player] and EF.regeneration[player] then return true end
|
|
end,
|
|
icon = "hbhunger_icon_regen_poison.png",
|
|
priority = -10,
|
|
})
|
|
|
|
mcl_potions.register_hp_hudbar_modifier({
|
|
predicate = function(player)
|
|
if EF.poison[player] then return true end
|
|
end,
|
|
icon = "hbhunger_icon_health_poison.png",
|
|
priority = 0,
|
|
})
|
|
|
|
mcl_potions.register_hp_hudbar_modifier({
|
|
predicate = function(player)
|
|
if EF.frost[player] and EF.regeneration[player] then return true end
|
|
end,
|
|
icon = "mcl_potions_icon_regen_frost.png",
|
|
priority = 10,
|
|
})
|
|
|
|
mcl_potions.register_hp_hudbar_modifier({
|
|
predicate = function(player)
|
|
if EF.frost[player] then return true end
|
|
end,
|
|
icon = "mcl_potions_icon_frost.png",
|
|
priority = 20,
|
|
})
|
|
|
|
mcl_potions.register_hp_hudbar_modifier({
|
|
predicate = function(player)
|
|
if EF.regeneration[player] then return true end
|
|
end,
|
|
icon = "hudbars_icon_regenerate.png",
|
|
priority = 30,
|
|
})
|
|
|
|
local function potions_set_hudbar(player)
|
|
for _, mod in pairs(hp_hudbar_modifiers) do
|
|
if mod.predicate(player) then
|
|
hb.change_hudbar(player, "health", nil, nil, mod.icon, nil, "hudbars_bar_health.png")
|
|
return
|
|
end
|
|
end
|
|
hb.change_hudbar(player, "health", nil, nil, "hudbars_icon_health.png", nil, "hudbars_bar_health.png")
|
|
end
|
|
|
|
local icon_ids = {}
|
|
|
|
local function potions_init_icons(player)
|
|
local name = player:get_player_name()
|
|
icon_ids[name] = {}
|
|
for e=1, EFFECT_TYPES do
|
|
local x = -52 * e - 2
|
|
local id = {}
|
|
id.img = player:hud_add({
|
|
hud_elem_type = "image",
|
|
text = "blank.png",
|
|
position = { x = 1, y = 0 },
|
|
offset = { x = x, y = 3 },
|
|
scale = { x = 0.375, y = 0.375 },
|
|
alignment = { x = 1, y = 1 },
|
|
z_index = 100,
|
|
})
|
|
id.label = player:hud_add({
|
|
hud_elem_type = "text",
|
|
text = "",
|
|
position = { x = 1, y = 0 },
|
|
offset = { x = x+22, y = 50 },
|
|
scale = { x = 50, y = 15 },
|
|
alignment = { x = 0, y = 1 },
|
|
z_index = 100,
|
|
style = 1,
|
|
number = 0xFFFFFF,
|
|
})
|
|
id.timestamp = player:hud_add({
|
|
hud_elem_type = "text",
|
|
text = "",
|
|
position = { x = 1, y = 0 },
|
|
offset = { x = x+22, y = 65 },
|
|
scale = { x = 50, y = 15 },
|
|
alignment = { x = 0, y = 1 },
|
|
z_index = 100,
|
|
style = 1,
|
|
number = 0xFFFFFF,
|
|
})
|
|
table.insert(icon_ids[name], id)
|
|
end
|
|
|
|
-- Absorption bar in damage disabled server is unneccessary
|
|
if minetest.settings:get_bool("enable_damage") == true then
|
|
hb.init_hudbar(player, "absorption")
|
|
end
|
|
end
|
|
|
|
local function potions_set_icons(player)
|
|
local name = player:get_player_name()
|
|
if not icon_ids[name] then
|
|
return
|
|
end
|
|
local active_effects = {}
|
|
for effect_name, effect in pairs(EF) do
|
|
if effect[player] then
|
|
active_effects[effect_name] = effect[player]
|
|
end
|
|
end
|
|
|
|
local i = 1
|
|
for effect_name, def in pairs(registered_effects) do
|
|
local icon = icon_ids[name][i].img
|
|
local label = icon_ids[name][i].label
|
|
local timestamp = icon_ids[name][i].timestamp
|
|
local vals = active_effects[effect_name]
|
|
if vals then
|
|
player:hud_change(icon, "text", def.icon .. "^[resize:128x128")
|
|
if def.uses_factor then
|
|
local level = def.factor_to_level(vals.factor)
|
|
if level > 3000 or level == math.huge then level = "∞"
|
|
elseif level < 0 then level = "???"
|
|
elseif level == 0 then level = "0"
|
|
else level = mcl_util.to_roman(level) end
|
|
player:hud_change(label, "text", level)
|
|
else
|
|
player:hud_change(label, "text", "")
|
|
end
|
|
if vals.dur == math.huge then
|
|
player:hud_change(timestamp, "text", "∞")
|
|
else
|
|
local dur = math.round(vals.dur-vals.timer)
|
|
player:hud_change(timestamp, "text", math.floor(dur/60)..string.format(":%02d",math.floor(dur % 60)))
|
|
end
|
|
EF[effect_name][player].hud_index = i
|
|
i = i + 1
|
|
end
|
|
end
|
|
while i < EFFECT_TYPES do
|
|
player:hud_change(icon_ids[name][i].img, "text", "blank.png")
|
|
player:hud_change(icon_ids[name][i].label, "text", "")
|
|
player:hud_change(icon_ids[name][i].timestamp, "text", "")
|
|
i = i + 1
|
|
end
|
|
end
|
|
|
|
local function potions_set_hud(player)
|
|
potions_set_hudbar(player)
|
|
potions_set_icons(player)
|
|
end
|
|
|
|
|
|
-- ███╗░░░███╗░█████╗░██╗███╗░░██╗ ███████╗███████╗███████╗███████╗░█████╗░████████╗
|
|
-- ████╗░████║██╔══██╗██║████╗░██║ ██╔════╝██╔════╝██╔════╝██╔════╝██╔══██╗╚══██╔══╝
|
|
-- ██╔████╔██║███████║██║██╔██╗██║ █████╗░░█████╗░░█████╗░░█████╗░░██║░░╚═╝░░░██║░░░
|
|
-- ██║╚██╔╝██║██╔══██║██║██║╚████║ ██╔══╝░░██╔══╝░░██╔══╝░░██╔══╝░░██║░░██╗░░░██║░░░
|
|
-- ██║░╚═╝░██║██║░░██║██║██║░╚███║ ███████╗██║░░░░░██║░░░░░███████╗╚█████╔╝░░░██║░░░
|
|
-- ╚═╝░░░░░╚═╝╚═╝░░╚═╝╚═╝╚═╝░░╚══╝ ╚══════╝╚═╝░░░░░╚═╝░░░░░╚══════╝░╚════╝░░░░╚═╝░░░
|
|
--
|
|
-- ░█████╗░██╗░░██╗███████╗░█████╗░██╗░░██╗███████╗██████╗░
|
|
-- ██╔══██╗██║░░██║██╔════╝██╔══██╗██║░██╔╝██╔════╝██╔══██╗
|
|
-- ██║░░╚═╝███████║█████╗░░██║░░╚═╝█████═╝░█████╗░░██████╔╝
|
|
-- ██║░░██╗██╔══██║██╔══╝░░██║░░██╗██╔═██╗░██╔══╝░░██╔══██╗
|
|
-- ╚█████╔╝██║░░██║███████╗╚█████╔╝██║░╚██╗███████╗██║░░██║
|
|
-- ░╚════╝░╚═╝░░╚═╝╚══════╝░╚════╝░╚═╝░░╚═╝╚══════╝╚═╝░░╚═╝
|
|
|
|
minetest.register_globalstep(function(dtime)
|
|
for name, effect in pairs(registered_effects) do
|
|
for object, vals in pairs(EF[name]) do
|
|
if vals.dur ~= math.huge then EF[name][object].timer = vals.timer + dtime end
|
|
|
|
if object:get_pos() and not vals.no_particles then mcl_potions._add_spawner(object, effect.particle_color) end
|
|
if effect.on_step then effect.on_step(dtime, object, vals.factor, vals.dur) end
|
|
if effect.on_hit_timer then
|
|
EF[name][object].hit_timer = (vals.hit_timer or 0) + dtime
|
|
if EF[name][object].hit_timer >= vals.step then
|
|
effect.on_hit_timer(object, vals.factor, vals.dur)
|
|
if EF[name][object] then EF[name][object].hit_timer = 0 end
|
|
end
|
|
end
|
|
|
|
if not object or not EF[name][object] or EF[name][object].timer >= vals.dur or not object:get_pos() then
|
|
if effect.on_end then effect.on_end(object) end
|
|
EF[name][object] = nil
|
|
if effect.after_end then effect.after_end(object) end
|
|
if object:is_player() then
|
|
meta = object:get_meta()
|
|
meta:set_string("mcl_potions:_EF_"..name, "")
|
|
potions_set_hud(object)
|
|
else
|
|
local ent = object:get_luaentity()
|
|
if ent and ent._mcl_potions then
|
|
ent._mcl_potions["_EF_"..name] = nil
|
|
end
|
|
end
|
|
elseif object:is_player() then
|
|
if vals.dur == math.huge then
|
|
object:hud_change(icon_ids[object:get_player_name()][vals.hud_index].timestamp,
|
|
"text", "∞")
|
|
else
|
|
local dur = math.round(vals.dur-vals.timer)
|
|
object:hud_change(icon_ids[object:get_player_name()][vals.hud_index].timestamp,
|
|
"text", math.floor(dur/60)..string.format(":%02d",math.floor(dur % 60)))
|
|
end
|
|
else
|
|
local ent = object:get_luaentity()
|
|
if ent and ent._mcl_potions then
|
|
ent._mcl_potions["_EF_"..name] = EF[name][object]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
|
|
-- ███████╗███████╗███████╗███████╗░█████╗░████████╗
|
|
-- ██╔════╝██╔════╝██╔════╝██╔════╝██╔══██╗╚══██╔══╝
|
|
-- █████╗░░█████╗░░█████╗░░█████╗░░██║░░╚═╝░░░██║░░░
|
|
-- ██╔══╝░░██╔══╝░░██╔══╝░░██╔══╝░░██║░░██╗░░░██║░░░
|
|
-- ███████╗██║░░░░░██║░░░░░███████╗╚█████╔╝░░░██║░░░
|
|
-- ╚══════╝╚═╝░░░░░╚═╝░░░░░╚══════╝░╚════╝░░░░╚═╝░░░
|
|
--
|
|
-- ██╗░░░░░░█████╗░░█████╗░██████╗░░░░░██╗░██████╗░█████╗░██╗░░░██╗███████╗
|
|
-- ██║░░░░░██╔══██╗██╔══██╗██╔══██╗░░░██╔╝██╔════╝██╔══██╗██║░░░██║██╔════╝
|
|
-- ██║░░░░░██║░░██║███████║██║░░██║░░██╔╝░╚█████╗░███████║╚██╗░██╔╝█████╗░░
|
|
-- ██║░░░░░██║░░██║██╔══██║██║░░██║░██╔╝░░░╚═══██╗██╔══██║░╚████╔╝░██╔══╝░░
|
|
-- ███████╗╚█████╔╝██║░░██║██████╔╝██╔╝░░░██████╔╝██║░░██║░░╚██╔╝░░███████╗
|
|
-- ╚══════╝░╚════╝░╚═╝░░╚═╝╚═════╝░╚═╝░░░░╚═════╝░╚═╝░░╚═╝░░░╚═╝░░░╚══════╝
|
|
|
|
function mcl_potions._reset_haste_fatigue_item_meta(player)
|
|
local inv = player:get_inventory()
|
|
if not inv then return end
|
|
local lists = inv:get_lists()
|
|
for _, list in pairs(lists) do
|
|
for _, item in pairs(list) do
|
|
local meta = item:get_meta()
|
|
meta:set_string("mcl_potions:haste", "")
|
|
meta:set_string("mcl_potions:fatigue", "")
|
|
meta:set_tool_capabilities()
|
|
meta:set_string("groupcaps_hash","")
|
|
mcl_enchanting.update_groupcaps(item)
|
|
end
|
|
end
|
|
inv:set_lists(lists)
|
|
end
|
|
mcl_gamemode.register_on_gamemode_change(mcl_potions._reset_haste_fatigue_item_meta)
|
|
|
|
function mcl_potions._clear_cached_effect_data(object)
|
|
for name, effect in pairs(EF) do
|
|
effect[object] = nil
|
|
end
|
|
if not object:is_player() then return end
|
|
local meta = object:get_meta()
|
|
meta:set_int("night_vision", 0)
|
|
end
|
|
|
|
function mcl_potions._reset_effects(object, set_hud)
|
|
local set_hud = set_hud
|
|
if not object:is_player() then
|
|
set_hud = false
|
|
end
|
|
|
|
local removed_effects = {}
|
|
for name, effect in pairs(registered_effects) do
|
|
if EF[name][object] and effect.on_end then effect.on_end(object) end
|
|
if effect.after_end then table.insert(removed_effects, effect.after_end) end
|
|
end
|
|
|
|
mcl_potions._clear_cached_effect_data(object)
|
|
|
|
for i=1, #removed_effects do
|
|
removed_effects[i](object)
|
|
end
|
|
|
|
if set_hud ~= false then
|
|
potions_set_hud(object)
|
|
end
|
|
end
|
|
|
|
function mcl_potions._save_player_effects(player)
|
|
if not player:is_player() then
|
|
return
|
|
end
|
|
local meta = player:get_meta()
|
|
|
|
for name, effect in pairs(registered_effects) do
|
|
if effect.on_save_effect and EF[name][player] then effect.on_save_effect(player) end
|
|
meta:set_string("mcl_potions:_EF_"..name, minetest.serialize(EF[name][player]))
|
|
end
|
|
end
|
|
|
|
function mcl_potions._load_player_effects(player)
|
|
if not player:is_player() then
|
|
return
|
|
end
|
|
local meta = player:get_meta()
|
|
|
|
-- handle legacy meta strings
|
|
local legacy_invisible = minetest.deserialize(meta:get_string("_is_invisible"))
|
|
local legacy_poisoned = minetest.deserialize(meta:get_string("_is_poisoned"))
|
|
local legacy_regenerating = minetest.deserialize(meta:get_string("_is_regenerating"))
|
|
local legacy_strong = minetest.deserialize(meta:get_string("_is_strong"))
|
|
local legacy_weak = minetest.deserialize(meta:get_string("_is_weak"))
|
|
local legacy_water_breathing = minetest.deserialize(meta:get_string("_is_water_breathing"))
|
|
local legacy_leaping = minetest.deserialize(meta:get_string("_is_leaping"))
|
|
local legacy_swift = minetest.deserialize(meta:get_string("_is_swift"))
|
|
local legacy_night_vision = minetest.deserialize(meta:get_string("_is_cat"))
|
|
local legacy_fireproof = minetest.deserialize(meta:get_string("_is_fire_proof"))
|
|
local legacy_bad_omen = minetest.deserialize(meta:get_string("_has_bad_omen"))
|
|
local legacy_withering = minetest.deserialize(meta:get_string("_is_withering"))
|
|
if legacy_invisible then
|
|
EF.invisibility[player] = legacy_invisible
|
|
meta:set_string("_is_invisible", "")
|
|
end
|
|
if legacy_poisoned then
|
|
EF.poison[player] = legacy_poisoned
|
|
meta:set_string("_is_poisoned", "")
|
|
end
|
|
if legacy_regenerating then
|
|
EF.regeneration[player] = legacy_regenerating
|
|
meta:set_string("_is_regenerating", "")
|
|
end
|
|
if legacy_strong then
|
|
EF.strength[player] = legacy_strong
|
|
meta:set_string("_is_strong", "")
|
|
end
|
|
if legacy_weak then
|
|
EF.weakness[player] = legacy_weak
|
|
meta:set_string("_is_weak", "")
|
|
end
|
|
if legacy_water_breathing then
|
|
EF.water_breathing[player] = legacy_water_breating
|
|
meta:set_string("_is_water_breating", "")
|
|
end
|
|
if legacy_leaping then
|
|
EF.leaping[player] = legacy_leaping
|
|
meta:set_string("_is_leaping", "")
|
|
end
|
|
if legacy_swift then
|
|
EF.swiftness[player] = legacy_swift
|
|
meta:set_string("_is_swift", "")
|
|
end
|
|
if legacy_night_vision then
|
|
EF.night_vision[player] = legacy_night_vision
|
|
meta:set_string("_is_cat", "")
|
|
end
|
|
if legacy_fireproof then
|
|
EF.fire_resistance[player] = legacy_fireproof
|
|
meta:set_string("_is_fire_proof", "")
|
|
end
|
|
if legacy_bad_omen then
|
|
EF.bad_omen[player] = legacy_bad_omen
|
|
meta:set_string("_has_bad_omen", "")
|
|
end
|
|
if legacy_withering then
|
|
EF.withering[player] = legacy_withering
|
|
meta:set_string("_is_withering", "")
|
|
end
|
|
|
|
-- new API effects + on_load for loaded legacy effects
|
|
for name, effect in pairs(registered_effects) do
|
|
local loaded = minetest.deserialize(meta:get_string("mcl_potions:_EF_"..name))
|
|
if loaded then
|
|
EF[name][player] = loaded
|
|
end
|
|
if EF[name][player] then -- this is needed because of legacy effects loaded separately
|
|
if effect.uses_factor and type(EF[name][player].factor) ~= "number" then
|
|
EF[name][player].factor = effect.level_to_factor(1)
|
|
end
|
|
if effect.on_load then
|
|
effect.on_load(player, EF[name][player].factor)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function mcl_potions._load_entity_effects(entity)
|
|
if not entity or not entity._mcl_potions or entity._mcl_potions == {} then
|
|
return
|
|
end
|
|
local object = entity.object
|
|
if not object or not object:get_pos() then return end
|
|
for name, effect in pairs(registered_effects) do
|
|
local loaded = entity._mcl_potions["_EF_"..name]
|
|
if loaded then
|
|
EF[name][object] = loaded
|
|
if effect.uses_factor and not loaded.factor then
|
|
EF[name][object].factor = effect.level_to_factor(1)
|
|
end
|
|
if effect.on_load then
|
|
effect.on_load(object, EF[name][object].factor)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Returns true if object has given effect
|
|
function mcl_potions.has_effect(object, effect_name)
|
|
if not EF[effect_name] then
|
|
return false
|
|
end
|
|
return EF[effect_name][object] ~= nil
|
|
end
|
|
|
|
function mcl_potions.get_effect(object, effect_name)
|
|
if not EF[effect_name] or not EF[effect_name][object] then
|
|
return false
|
|
end
|
|
return EF[effect_name][object]
|
|
end
|
|
|
|
function mcl_potions.get_effect_level(object, effect_name)
|
|
if not EF[effect_name] then return end
|
|
local effect = EF[effect_name][object]
|
|
if not effect then return 0 end
|
|
if not registered_effects[effect_name].uses_factor then return 1 end
|
|
return registered_effects[effect_name].factor_to_level(effect.factor)
|
|
end
|
|
|
|
function mcl_potions.get_total_haste(object)
|
|
local accum_factor = 1
|
|
for name, def in pairs(item_speed_effects) do
|
|
if EF[name][object] and not EF[name][object].blocked then
|
|
local factor = EF[name][object].factor
|
|
if def.factor_is_positive then factor = factor + 1 end
|
|
if factor > 1 then accum_factor = accum_factor * factor end
|
|
end
|
|
end
|
|
return accum_factor - 1
|
|
end
|
|
|
|
function mcl_potions.get_total_fatigue(object)
|
|
local accum_factor = 1
|
|
for name, def in pairs(item_speed_effects) do
|
|
if EF[name][object] and not EF[name][object].blocked then
|
|
local factor = EF[name][object].factor
|
|
if def.factor_is_positive then factor = factor + 1 end
|
|
if factor <= 0 then return 0 end
|
|
if factor < 1 then accum_factor = accum_factor * factor end
|
|
end
|
|
end
|
|
return accum_factor
|
|
end
|
|
|
|
function mcl_potions.clear_effect(object, effect)
|
|
if not EF[effect] then
|
|
minetest.log("warning", "[mcl_potions] Tried to remove an effect that is not registered: " .. dump(effect))
|
|
return false
|
|
end
|
|
local def = registered_effects[effect]
|
|
if EF[effect][object] then
|
|
if def.on_end then def.on_end(object) end
|
|
EF[effect][object] = nil
|
|
if def.after_end then def.after_end(object) end
|
|
end
|
|
if not object:is_player() then return end
|
|
potions_set_hud(object)
|
|
end
|
|
|
|
minetest.register_on_leaveplayer( function(player)
|
|
mcl_potions._save_player_effects(player)
|
|
mcl_potions._clear_cached_effect_data(player) -- clear the buffer to prevent looking for a player not there
|
|
icon_ids[player:get_player_name()] = nil
|
|
end)
|
|
|
|
minetest.register_on_dieplayer( function(player)
|
|
mcl_potions._reset_effects(player)
|
|
potions_set_hud(player)
|
|
end)
|
|
|
|
minetest.register_on_joinplayer( function(player)
|
|
mcl_potions._reset_effects(player, false) -- make sure there are no weird holdover effects
|
|
mcl_potions._load_player_effects(player)
|
|
mcl_potions._reset_haste_fatigue_item_meta(player)
|
|
potions_init_icons(player)
|
|
potions_set_hud(player)
|
|
end)
|
|
|
|
minetest.register_on_shutdown(function()
|
|
-- save player effects on server shutdown
|
|
for _,player in pairs(minetest.get_connected_players()) do
|
|
mcl_potions._save_player_effects(player)
|
|
end
|
|
end)
|
|
|
|
-- ░██████╗██╗░░░██╗██████╗░██████╗░░█████╗░██████╗░████████╗██╗███╗░░██╗░██████╗░
|
|
-- ██╔════╝██║░░░██║██╔══██╗██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██║████╗░██║██╔════╝░
|
|
-- ╚█████╗░██║░░░██║██████╔╝██████╔╝██║░░██║██████╔╝░░░██║░░░██║██╔██╗██║██║░░██╗░
|
|
-- ░╚═══██╗██║░░░██║██╔═══╝░██╔═══╝░██║░░██║██╔══██╗░░░██║░░░██║██║╚████║██║░░╚██╗
|
|
-- ██████╔╝╚██████╔╝██║░░░░░██║░░░░░╚█████╔╝██║░░██║░░░██║░░░██║██║░╚███║╚██████╔╝
|
|
-- ╚═════╝░░╚═════╝░╚═╝░░░░░╚═╝░░░░░░╚════╝░╚═╝░░╚═╝░░░╚═╝░░░╚═╝╚═╝░░╚══╝░╚═════╝░
|
|
--
|
|
-- ███████╗██╗░░░██╗███╗░░██╗░█████╗░████████╗██╗░█████╗░███╗░░██╗░██████╗
|
|
-- ██╔════╝██║░░░██║████╗░██║██╔══██╗╚══██╔══╝██║██╔══██╗████╗░██║██╔════╝
|
|
-- █████╗░░██║░░░██║██╔██╗██║██║░░╚═╝░░░██║░░░██║██║░░██║██╔██╗██║╚█████╗░
|
|
-- ██╔══╝░░██║░░░██║██║╚████║██║░░██╗░░░██║░░░██║██║░░██║██║╚████║░╚═══██╗
|
|
-- ██║░░░░░╚██████╔╝██║░╚███║╚█████╔╝░░░██║░░░██║╚█████╔╝██║░╚███║██████╔╝
|
|
-- ╚═╝░░░░░░╚═════╝░╚═╝░░╚══╝░╚════╝░░░░╚═╝░░░╚═╝░╚════╝░╚═╝░░╚══╝╚═════╝░
|
|
|
|
function mcl_potions.is_obj_hit(self, pos)
|
|
|
|
local entity
|
|
for _,object in pairs(minetest.get_objects_inside_radius(pos, 1.1)) do
|
|
|
|
entity = object:get_luaentity()
|
|
|
|
if entity and entity.name ~= self.object:get_luaentity().name then
|
|
|
|
if entity.is_mob then
|
|
return true
|
|
end
|
|
|
|
elseif object:is_player() and self._thrower ~= object:get_player_name() then
|
|
return true
|
|
end
|
|
|
|
end
|
|
return false
|
|
end
|
|
|
|
|
|
function mcl_potions.make_invisible(obj_ref, hide)
|
|
if obj_ref:is_player() then
|
|
if hide then
|
|
mcl_player.player_set_visibility(obj_ref, false)
|
|
obj_ref:set_nametag_attributes({ color = { a = 0 } })
|
|
else
|
|
mcl_player.player_set_visibility(obj_ref, true)
|
|
obj_ref:set_nametag_attributes({ color = { r = 255, g = 255, b = 255, a = 255 } })
|
|
end
|
|
else
|
|
if hide then
|
|
local luaentity = obj_ref:get_luaentity()
|
|
EF.invisibility[obj_ref].old_size = luaentity.visual_size
|
|
obj_ref:set_properties({ visual_size = { x = 0, y = 0 } })
|
|
else
|
|
obj_ref:set_properties({ visual_size = EF.invisibility[obj_ref].old_size })
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function mcl_potions._use_potion(obj, color)
|
|
local d = 0.1
|
|
local pos = obj:get_pos()
|
|
minetest.sound_play("mcl_potions_drinking", {pos = pos, max_hear_distance = 6, gain = 1})
|
|
minetest.add_particlespawner({
|
|
amount = 25,
|
|
time = 1,
|
|
minpos = {x=pos.x-d, y=pos.y+1, z=pos.z-d},
|
|
maxpos = {x=pos.x+d, y=pos.y+2, z=pos.z+d},
|
|
minvel = {x=-0.1, y=0, z=-0.1},
|
|
maxvel = {x=0.1, y=0.1, z=0.1},
|
|
minacc = {x=-0.1, y=0, z=-0.1},
|
|
maxacc = {x=0.1, y=.1, z=0.1},
|
|
minexptime = 1,
|
|
maxexptime = 5,
|
|
minsize = 0.5,
|
|
maxsize = 1,
|
|
collisiondetection = true,
|
|
vertical = false,
|
|
texture = "mcl_particles_effect.png^[colorize:"..color..":127",
|
|
})
|
|
end
|
|
|
|
|
|
function mcl_potions._add_spawner(obj, color)
|
|
local d = 0.2
|
|
local pos = obj:get_pos()
|
|
minetest.add_particlespawner({
|
|
amount = 1,
|
|
time = 1,
|
|
minpos = {x=pos.x-d, y=pos.y+1, z=pos.z-d},
|
|
maxpos = {x=pos.x+d, y=pos.y+2, z=pos.z+d},
|
|
minvel = {x=-0.1, y=0, z=-0.1},
|
|
maxvel = {x=0.1, y=0.1, z=0.1},
|
|
minacc = {x=-0.1, y=0, z=-0.1},
|
|
maxacc = {x=0.1, y=.1, z=0.1},
|
|
minexptime = 0.5,
|
|
maxexptime = 1,
|
|
minsize = 0.5,
|
|
maxsize = 1,
|
|
collisiondetection = false,
|
|
vertical = false,
|
|
texture = "mcl_particles_effect.png^[colorize:"..color..":127",
|
|
})
|
|
end
|
|
|
|
|
|
|
|
-- ██████╗░░█████╗░░██████╗███████╗ ██████╗░░█████╗░████████╗██╗░█████╗░███╗░░██╗
|
|
-- ██╔══██╗██╔══██╗██╔════╝██╔════╝ ██╔══██╗██╔══██╗╚══██╔══╝██║██╔══██╗████╗░██║
|
|
-- ██████╦╝███████║╚█████╗░█████╗░░ ██████╔╝██║░░██║░░░██║░░░██║██║░░██║██╔██╗██║
|
|
-- ██╔══██╗██╔══██║░╚═══██╗██╔══╝░░ ██╔═══╝░██║░░██║░░░██║░░░██║██║░░██║██║╚████║
|
|
-- ██████╦╝██║░░██║██████╔╝███████╗ ██║░░░░░╚█████╔╝░░░██║░░░██║╚█████╔╝██║░╚███║
|
|
-- ╚═════╝░╚═╝░░╚═╝╚═════╝░╚══════╝ ╚═╝░░░░░░╚════╝░░░░╚═╝░░░╚═╝░╚════╝░╚═╝░░╚══╝
|
|
--
|
|
-- ███████╗███████╗███████╗███████╗░█████╗░████████╗
|
|
-- ██╔════╝██╔════╝██╔════╝██╔════╝██╔══██╗╚══██╔══╝
|
|
-- █████╗░░█████╗░░█████╗░░█████╗░░██║░░╚═╝░░░██║░░░
|
|
-- ██╔══╝░░██╔══╝░░██╔══╝░░██╔══╝░░██║░░██╗░░░██║░░░
|
|
-- ███████╗██║░░░░░██║░░░░░███████╗╚█████╔╝░░░██║░░░
|
|
-- ╚══════╝╚═╝░░░░░╚═╝░░░░░╚══════╝░╚════╝░░░░╚═╝░░░
|
|
--
|
|
-- ███████╗██╗░░░██╗███╗░░██╗░█████╗░████████╗██╗░█████╗░███╗░░██╗░██████╗
|
|
-- ██╔════╝██║░░░██║████╗░██║██╔══██╗╚══██╔══╝██║██╔══██╗████╗░██║██╔════╝
|
|
-- █████╗░░██║░░░██║██╔██╗██║██║░░╚═╝░░░██║░░░██║██║░░██║██╔██╗██║╚█████╗░
|
|
-- ██╔══╝░░██║░░░██║██║╚████║██║░░██╗░░░██║░░░██║██║░░██║██║╚████║░╚═══██╗
|
|
-- ██║░░░░░╚██████╔╝██║░╚███║╚█████╔╝░░░██║░░░██║╚█████╔╝██║░╚███║██████╔╝
|
|
-- ╚═╝░░░░░░╚═════╝░╚═╝░░╚══╝░╚════╝░░░░╚═╝░░░╚═╝░╚════╝░╚═╝░░╚══╝╚═════╝░
|
|
|
|
local registered_res_predicates = {}
|
|
-- API
|
|
-- This is supposed to add custom resistance functions independent of effects
|
|
-- E.g. some entity could be resistant to all (or some) effects under specific conditions
|
|
-- predicate - function(object, effect_name) - return true if resists effect
|
|
function mcl_potions.register_generic_resistance_predicate(predicate)
|
|
if type(predicate) == "function" then
|
|
table.insert(registered_res_predicates, predicate)
|
|
else
|
|
error("Attempted to register non-function as a predicate")
|
|
end
|
|
end
|
|
|
|
local function target_valid(object, name)
|
|
if not object or object:get_hp() <= 0 then return false end
|
|
|
|
-- Don't apply effects to anything other than players and entities that have mcl_potions support
|
|
-- but are not bosses
|
|
local entity = object:get_luaentity()
|
|
if not object:is_player() and (not entity or entity.is_boss or not entity._mcl_potions) then
|
|
return false
|
|
end
|
|
|
|
-- Check resistances
|
|
for i=1, #registered_res_predicates do
|
|
if registered_res_predicates[i](object, name) then return false end
|
|
end
|
|
|
|
if not (registered_effects[name].res_condition
|
|
and registered_effects[name].res_condition(object)) then return true end
|
|
end
|
|
|
|
function mcl_potions.give_effect(name, object, factor, duration, no_particles)
|
|
local edef = registered_effects[name]
|
|
if not edef or not target_valid(object, name) then return false end
|
|
if not EF[name][object] then
|
|
local vals = {dur = duration, timer = 0, no_particles = no_particles}
|
|
if edef.uses_factor then vals.factor = factor end
|
|
if edef.on_hit_timer then
|
|
if edef.timer_uses_factor then vals.step = factor
|
|
else vals.step = edef.hit_timer_step end
|
|
end
|
|
if duration == "INF" then
|
|
vals.dur = math.huge
|
|
end
|
|
EF[name][object] = vals
|
|
if edef.on_start then edef.on_start(object, factor) end
|
|
else
|
|
local present = EF[name][object]
|
|
present.no_particles = no_particles
|
|
if not edef.uses_factor or (edef.uses_factor and
|
|
(not edef.inv_factor and factor >= present.factor
|
|
or edef.inv_factor and factor <= present.factor)) then
|
|
present.dur = math.max(duration, present.dur - present.timer)
|
|
present.timer = 0
|
|
if edef.uses_factor then
|
|
present.factor = factor
|
|
if edef.timer_uses_factor then present.step = factor end
|
|
if edef.on_start then edef.on_start(object, factor) end
|
|
end
|
|
if duration == "INF" then
|
|
present.dur = math.huge
|
|
end
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
if object:is_player() then potions_set_hud(object) end
|
|
|
|
return true
|
|
end
|
|
|
|
function mcl_potions.give_effect_by_level(name, object, level, duration, no_particles)
|
|
if level == 0 then return false end
|
|
if not registered_effects[name] then
|
|
minetest.log("warning", "[mcl_potions] Trying to give unknown effect "..tostring(name))
|
|
return false
|
|
end
|
|
if not registered_effects[name].uses_factor then
|
|
return mcl_potions.give_effect(name, object, 0, duration, no_particles)
|
|
end
|
|
local factor = registered_effects[name].level_to_factor(level)
|
|
return mcl_potions.give_effect(name, object, factor, duration, no_particles)
|
|
end
|
|
|
|
function mcl_potions.healing_func(object, hp)
|
|
if not object or object:get_hp() <= 0 then return false end
|
|
local ent = object:get_luaentity()
|
|
|
|
if ent and ent.harmed_by_heal then hp = -hp end
|
|
|
|
if hp > 0 then
|
|
-- at least 1 HP
|
|
if hp < 1 then
|
|
hp = 1
|
|
end
|
|
|
|
if ent and ent.is_mob then
|
|
ent.health = math.min(ent.health + hp, ent.hp_max)
|
|
elseif object:is_player() then
|
|
object:set_hp(math.min(object:get_hp() + hp, object:get_properties().hp_max), { type = "set_hp", other = "healing" })
|
|
end
|
|
|
|
elseif hp < 0 then
|
|
if hp > -1 then
|
|
hp = -1
|
|
end
|
|
|
|
mcl_util.deal_damage(object, -hp, {type = "magic"})
|
|
end
|
|
end
|
|
|
|
function mcl_potions.strength_func(object, factor, duration)
|
|
return mcl_potions.give_effect("strength", object, factor, duration)
|
|
end
|
|
function mcl_potions.leaping_func(object, factor, duration)
|
|
return mcl_potions.give_effect("leaping", object, factor, duration)
|
|
end
|
|
function mcl_potions.weakness_func(object, factor, duration)
|
|
return mcl_potions.give_effect("weakness", object, factor, duration)
|
|
end
|
|
function mcl_potions.swiftness_func(object, factor, duration)
|
|
return mcl_potions.give_effect("swiftness", object, factor, duration)
|
|
end
|
|
function mcl_potions.slowness_func(object, factor, duration)
|
|
return mcl_potions.give_effect("slowness", object, factor, duration)
|
|
end
|
|
|
|
function mcl_potions.withering_func(object, factor, duration)
|
|
return mcl_potions.give_effect("withering", object, factor, duration)
|
|
end
|
|
|
|
|
|
function mcl_potions.poison_func(object, factor, duration)
|
|
return mcl_potions.give_effect("poison", object, factor, duration)
|
|
end
|
|
|
|
|
|
function mcl_potions.regeneration_func(object, factor, duration)
|
|
return mcl_potions.give_effect("regeneration", object, factor, duration)
|
|
end
|
|
|
|
|
|
function mcl_potions.invisiblility_func(object, null, duration)
|
|
return mcl_potions.give_effect("invisibility", object, null, duration)
|
|
end
|
|
|
|
function mcl_potions.water_breathing_func(object, null, duration)
|
|
return mcl_potions.give_effect("water_breathing", object, null, duration)
|
|
end
|
|
|
|
|
|
function mcl_potions.fire_resistance_func(object, null, duration)
|
|
return mcl_potions.give_effect("fire_resistance", object, null, duration)
|
|
end
|
|
|
|
|
|
function mcl_potions.night_vision_func(object, null, duration)
|
|
return mcl_potions.give_effect("night_vision", object, null, duration)
|
|
end
|
|
|
|
function mcl_potions._extinguish_nearby_fire(pos, radius)
|
|
local epos = {x=pos.x, y=pos.y+0.5, z=pos.z}
|
|
local dnode = minetest.get_node({x=pos.x,y=pos.y-0.5,z=pos.z})
|
|
if minetest.get_item_group(dnode.name, "fire") ~= 0 or minetest.get_item_group(dnode.name, "lit_campfire") ~= 0 then
|
|
epos.y = pos.y - 0.5
|
|
end
|
|
local exting = false
|
|
-- No radius: Splash, extinguish epos and 4 nodes around
|
|
if not radius then
|
|
local dirs = {
|
|
{x=0,y=0,z=0},
|
|
{x=0,y=0,z=-1},
|
|
{x=0,y=0,z=1},
|
|
{x=-1,y=0,z=0},
|
|
{x=1,y=0,z=0},
|
|
}
|
|
for d=1, #dirs do
|
|
local tpos = vector.add(epos, dirs[d])
|
|
local node = minetest.get_node(tpos)
|
|
if minetest.get_item_group(node.name, "fire") ~= 0 then
|
|
minetest.sound_play("fire_extinguish_flame", {pos = tpos, gain = 0.25, max_hear_distance = 16}, true)
|
|
minetest.remove_node(tpos)
|
|
exting = true
|
|
elseif minetest.get_item_group(node.name, "lit_campfire") ~= 0 then
|
|
minetest.sound_play("fire_extinguish_flame", {pos = tpos, gain = 0.25, max_hear_distance = 16}, true)
|
|
local def = minetest.registered_nodes[node.name]
|
|
minetest.set_node(tpos, {name = def._mcl_campfires_smothered_form, param2 = node.param2})
|
|
exting = true
|
|
end
|
|
end
|
|
-- Has radius: lingering, extinguish all nodes in area
|
|
else
|
|
local nodes = minetest.find_nodes_in_area(
|
|
{x=epos.x-radius,y=epos.y,z=epos.z-radius},
|
|
{x=epos.x+radius,y=epos.y,z=epos.z+radius},
|
|
{"group:fire", "group:lit_campfire"})
|
|
for n=1, #nodes do
|
|
local node = minetest.get_node(nodes[n])
|
|
minetest.sound_play("fire_extinguish_flame", {pos = nodes[n], gain = 0.25, max_hear_distance = 16}, true)
|
|
if minetest.get_item_group(node.name, "fire") ~= 0 then
|
|
minetest.remove_node(nodes[n])
|
|
elseif minetest.get_item_group(node.name, "lit_campfire") ~= 0 then
|
|
local def = minetest.registered_nodes[node.name]
|
|
minetest.set_node(nodes[n], {name = def._mcl_campfires_smothered_form, param2 = node.param2})
|
|
end
|
|
exting = true
|
|
end
|
|
end
|
|
return exting
|
|
end
|
|
|
|
function mcl_potions.bad_omen_func(object, factor, duration)
|
|
mcl_potions.give_effect("bad_omen", object, factor, duration)
|
|
end
|