Improved API mob support

* various API functions now work with mobs properly
* the following effects don't work with mobs at all:
  water breathing, dolphin's grace, leaping, swiftness,
  slowness, slow falling, night vision, darkness, frost,
  health boost, absorption, fire resistance, resistance,
  luck, bad luck, blindness, nausea, hunger, saturation,
  haste, fatigue, conduit power
* the following effects should work with mobs:
  invisibility, regeneration, poison, withering,
  strength, weakness, levitation, glowing
* the following effects have no effect on mobs
  (but can be applied with the API):
  bad omen, hero of the village
This commit is contained in:
the-real-herowl 2024-04-14 07:45:39 +02:00
parent 9b501bfa7e
commit 70d8dfe558
5 changed files with 62 additions and 39 deletions

@ -380,7 +380,7 @@ minetest.register_globalstep(function(dtime)
etime = 0 etime = 0
for _,pl in pairs(minetest.get_connected_players()) do for _,pl in pairs(minetest.get_connected_players()) do
local armor_feet = pl:get_inventory():get_stack("armor", 5) local armor_feet = pl:get_inventory():get_stack("armor", 5)
if pl and pl:get_player_control().sneak or (minetest.global_exists("mcl_enchanting") and mcl_enchanting.has_enchantment(armor_feet, "frost_walker")) or (minetest.global_exists("mcl_potions") and mcl_potions.has_effect(pl, "fire_proof")) then if pl and pl:get_player_control().sneak or (minetest.global_exists("mcl_enchanting") and mcl_enchanting.has_enchantment(armor_feet, "frost_walker")) or (minetest.global_exists("mcl_potions") and mcl_potions.has_effect(pl, "fire_resistance")) then
return return
end end
burn_in_campfire(pl) burn_in_campfire(pl)

@ -154,7 +154,7 @@ local function drink_milk_delayed(itemstack, player, pointed_thing)
) then ) then
mcl_hunger.stop_poison(player) mcl_hunger.stop_poison(player)
end end
mcl_potions._reset_player_effects(player) mcl_potions._reset_effects(player)
end end
-- Wrapper for handling mcl_hunger delayed eating -- Wrapper for handling mcl_hunger delayed eating

@ -135,7 +135,7 @@ minetest.register_node("mcl_nether:magma", {
-- From walkover mod -- From walkover mod
on_walk_over = function(loc, nodeiamon, player) on_walk_over = function(loc, nodeiamon, player)
local armor_feet = player:get_inventory():get_stack("armor", 5) local armor_feet = player:get_inventory():get_stack("armor", 5)
if player and player:get_player_control().sneak or (minetest.global_exists("mcl_enchanting") and mcl_enchanting.has_enchantment(armor_feet, "frost_walker")) or (minetest.global_exists("mcl_potions") and mcl_potions.has_effect(player, "fire_proof")) then if player and player:get_player_control().sneak or (minetest.global_exists("mcl_enchanting") and mcl_enchanting.has_enchantment(armor_feet, "frost_walker")) or (minetest.global_exists("mcl_potions") and mcl_potions.has_effect(player, "fire_resistance")) then
return return
end end
-- Hurt players standing on top of this block -- Hurt players standing on top of this block

@ -44,7 +44,7 @@ minetest.register_chatcommand("effect",{
end end
end end
elseif P[1] == "clear" then elseif P[1] == "clear" then
mcl_potions._reset_player_effects(minetest.get_player_by_name(name)) mcl_potions._reset_effects(minetest.get_player_by_name(name))
return true, S("Effects cleared for player @1", name) return true, S("Effects cleared for player @1", name)
elseif P[1] == "remove" then elseif P[1] == "remove" then
if not P[2] then if not P[2] then

@ -300,8 +300,10 @@ mcl_potions.register_effect({
get_tt = function(factor) get_tt = function(factor)
return S("limitless breathing under water") return S("limitless breathing under water")
end, 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) on_step = function(dtime, object, factor, duration)
if not object:is_player() then return end
if object:get_breath() then if object:get_breath() then
hb.hide_hudbar(object, "breath") hb.hide_hudbar(object, "breath")
if object:get_breath() < 10 then object:set_breath(10) end if object:get_breath() < 10 then object:set_breath(10) end
@ -317,6 +319,9 @@ mcl_potions.register_effect({
get_tt = function(factor) get_tt = function(factor)
return S("swimming gracefully") return S("swimming gracefully")
end, end,
res_condition = function(object)
return (not object:is_player()) -- TODO needs mob physics factor API
end,
on_hit_timer = function(object, factor, duration) on_hit_timer = function(object, factor, duration)
local node = minetest.get_node_or_nil(object:get_pos()) local node = minetest.get_node_or_nil(object:get_pos())
if node and minetest.registered_nodes[node.name] if node and minetest.registered_nodes[node.name]
@ -340,7 +345,7 @@ mcl_potions.register_effect({
return S("-@1% jumping power", math.floor(-factor*100)) return S("-@1% jumping power", math.floor(-factor*100))
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO needs mob physics factor API
end, end,
on_start = function(object, factor) on_start = function(object, factor)
playerphysics.add_physics_factor(object, "jump", "mcl_potions:leaping", 1+factor) playerphysics.add_physics_factor(object, "jump", "mcl_potions:leaping", 1+factor)
@ -361,7 +366,7 @@ mcl_potions.register_effect({
return S("decreases gravity effects") return S("decreases gravity effects")
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO needs mob physics factor API
end, end,
on_start = function(object, factor) on_start = function(object, factor)
playerphysics.add_physics_factor(object, "gravity", "mcl_potions:slow_falling", 0.5) playerphysics.add_physics_factor(object, "gravity", "mcl_potions:slow_falling", 0.5)
@ -383,7 +388,7 @@ mcl_potions.register_effect({
return S("+@1% running speed", math.floor(factor*100)) return S("+@1% running speed", math.floor(factor*100))
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO needs mob physics factor API
end, end,
on_start = function(object, factor) on_start = function(object, factor)
playerphysics.add_physics_factor(object, "speed", "mcl_potions:swiftness", 1+factor) playerphysics.add_physics_factor(object, "speed", "mcl_potions:swiftness", 1+factor)
@ -404,7 +409,7 @@ mcl_potions.register_effect({
return S("-@1% running speed", math.floor(factor*100)) return S("-@1% running speed", math.floor(factor*100))
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO needs mob physics factor API
end, end,
on_start = function(object, factor) on_start = function(object, factor)
playerphysics.add_physics_factor(object, "speed", "mcl_potions:slowness", 1-factor) playerphysics.add_physics_factor(object, "speed", "mcl_potions:slowness", 1-factor)
@ -441,7 +446,7 @@ mcl_potions.register_effect({
return S("improved vision during the night") return S("improved vision during the night")
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO what should it do for mobs?
end, end,
on_start = function(object, factor) on_start = function(object, factor)
object:get_meta():set_int("night_vision", 1) object:get_meta():set_int("night_vision", 1)
@ -466,7 +471,7 @@ mcl_potions.register_effect({
return S("surrounded by darkness").."\n"..S("not seeing anything beyond @1 nodes", factor) return S("surrounded by darkness").."\n"..S("not seeing anything beyond @1 nodes", factor)
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO what should it do for mobs?
end, end,
on_start = function(object, factor) on_start = function(object, factor)
object:get_meta():set_int("darkness", 1) object:get_meta():set_int("darkness", 1)
@ -580,7 +585,7 @@ mcl_potions.register_effect({
return S("HP increased by @1", factor) return S("HP increased by @1", factor)
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO needs mob HP modifier API?
end, end,
on_start = function(object, factor) on_start = function(object, factor)
object:set_properties({hp_max = minetest.PLAYER_MAX_HP_DEFAULT+factor}) object:set_properties({hp_max = minetest.PLAYER_MAX_HP_DEFAULT+factor})
@ -601,7 +606,7 @@ mcl_potions.register_effect({
return S("absorbs up to @1 incoming damage", factor) return S("absorbs up to @1 incoming damage", factor)
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO dmg modifiers don't work for mobs
end, end,
on_start = function(object, factor) on_start = function(object, factor)
hb.change_hudbar(object, "absorption", factor, (math.floor(factor/20-0.05)+1)*20) hb.change_hudbar(object, "absorption", factor, (math.floor(factor/20-0.05)+1)*20)
@ -641,7 +646,7 @@ mcl_potions.register_effect({
return S("resistance to fire damage") return S("resistance to fire damage")
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO dmg modifiers don't work for mobs
end, end,
particle_color = "#E49A3A", particle_color = "#E49A3A",
uses_factor = false, uses_factor = false,
@ -655,7 +660,7 @@ mcl_potions.register_effect({
return S("resist @1% of incoming damage", math.floor(factor*100)) return S("resist @1% of incoming damage", math.floor(factor*100))
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO dmg modifiers don't work for mobs
end, end,
particle_color = "#2552A5", particle_color = "#2552A5",
uses_factor = true, uses_factor = true,
@ -672,7 +677,7 @@ mcl_potions.register_effect({
description = S("Luck"), description = S("Luck"),
particle_color = "#7BFF42", particle_color = "#7BFF42",
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO what should it do for mobs?
end, end,
on_start = function(object, factor) on_start = function(object, factor)
mcl_luck.apply_luck_modifier(object:get_player_name(), "mcl_potions:luck", factor) mcl_luck.apply_luck_modifier(object:get_player_name(), "mcl_potions:luck", factor)
@ -691,7 +696,7 @@ mcl_potions.register_effect({
description = S("Bad Luck"), description = S("Bad Luck"),
particle_color = "#887343", particle_color = "#887343",
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO what should it do for mobs?
end, end,
on_start = function(object, factor) on_start = function(object, factor)
mcl_luck.apply_luck_modifier(object:get_player_name(), "mcl_potions:bad_luck", -factor) mcl_luck.apply_luck_modifier(object:get_player_name(), "mcl_potions:bad_luck", -factor)
@ -746,7 +751,7 @@ mcl_potions.register_effect({
return S("-1 HP / 1 s, can kill, -@1% running speed", math.floor(factor*100)) return S("-1 HP / 1 s, can kill, -@1% running speed", math.floor(factor*100))
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO needs mob physics factor API
end, end,
on_start = function(object, factor) on_start = function(object, factor)
mcl_burning.extinguish(object) mcl_burning.extinguish(object)
@ -799,7 +804,7 @@ mcl_potions.register_effect({
return S("impaired sight") return S("impaired sight")
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO what should it do for mobs?
end, end,
on_start = function(object, factor) on_start = function(object, factor)
EF.blindness[object].vignette = object:hud_add({ EF.blindness[object].vignette = object:hud_add({
@ -842,7 +847,7 @@ mcl_potions.register_effect({
return S("not feeling very well...").."\n"..S("frequency: @1 / 1 s", factor) return S("not feeling very well...").."\n"..S("frequency: @1 / 1 s", factor)
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO what should it do for mobs?
end, end,
on_start = function(object, factor) on_start = function(object, factor)
object:set_lighting({ object:set_lighting({
@ -891,7 +896,7 @@ mcl_potions.register_effect({
return S("exhausts by @1 per second", factor) return S("exhausts by @1 per second", factor)
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO what should it do for mobs?
end, end,
on_start = function(object, factor) on_start = function(object, factor)
hb.change_hudbar(object, "hunger", nil, nil, "mcl_hunger_icon_foodpoison.png", nil, "mcl_hunger_bar_foodpoison.png") hb.change_hudbar(object, "hunger", nil, nil, "mcl_hunger_icon_foodpoison.png", nil, "mcl_hunger_bar_foodpoison.png")
@ -924,7 +929,7 @@ mcl_potions.register_effect({
return S("saturates by @1 per second", factor) return S("saturates by @1 per second", factor)
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO what should it do for mobs?
end, end,
on_step = function(dtime, object, factor, duration) on_step = function(dtime, object, factor, duration)
mcl_hunger.set_hunger(object, math.min(mcl_hunger.get_hunger(object)+dtime*factor, 20)) mcl_hunger.set_hunger(object, math.min(mcl_hunger.get_hunger(object)+dtime*factor, 20))
@ -988,7 +993,7 @@ mcl_potions.register_effect({
return S("+@1% mining and attack speed", math.floor(factor*100)) return S("+@1% mining and attack speed", math.floor(factor*100))
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO needs mob API support
end, end,
on_start = haste_fatigue_hand_update, on_start = haste_fatigue_hand_update,
after_end = function(object) after_end = function(object)
@ -1009,7 +1014,7 @@ mcl_potions.register_effect({
return S("-@1% mining and attack speed", math.floor((1-factor)*100)) return S("-@1% mining and attack speed", math.floor((1-factor)*100))
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO needs mob API support
end, end,
on_start = haste_fatigue_hand_update, on_start = haste_fatigue_hand_update,
after_end = function(object) after_end = function(object)
@ -1030,7 +1035,7 @@ mcl_potions.register_effect({
return S("+@1% mining and attack speed in water").."\n"..S("limitless breathing under water", math.floor(factor*100)) return S("+@1% mining and attack speed in water").."\n"..S("limitless breathing under water", math.floor(factor*100))
end, end,
res_condition = function(object) res_condition = function(object)
return (not object:is_player()) return (not object:is_player()) -- TODO needs mob API support
end, end,
on_start = haste_fatigue_hand_update, on_start = haste_fatigue_hand_update,
on_step = function(dtime, object, factor, duration) on_step = function(dtime, object, factor, duration)
@ -1380,34 +1385,35 @@ function mcl_potions._reset_haste_fatigue_item_meta(player)
end end
mcl_gamemode.register_on_gamemode_change(mcl_potions._reset_haste_fatigue_item_meta) mcl_gamemode.register_on_gamemode_change(mcl_potions._reset_haste_fatigue_item_meta)
function mcl_potions._clear_cached_player_data(player) function mcl_potions._clear_cached_effect_data(object)
for name, effect in pairs(EF) do for name, effect in pairs(EF) do
effect[player] = nil effect[object] = nil
end end
if not object:is_player() then return end
local meta = player:get_meta() local meta = object:get_meta()
meta:set_int("night_vision", 0) meta:set_int("night_vision", 0)
end end
function mcl_potions._reset_player_effects(player, set_hud) function mcl_potions._reset_effects(object, set_hud)
if not player:is_player() then local set_hud = set_hud
return if not object:is_player() then
set_hud = false
end end
local removed_effects = {} local removed_effects = {}
for name, effect in pairs(registered_effects) do for name, effect in pairs(registered_effects) do
if EF[name][player] and effect.on_end then effect.on_end(player) end 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 if effect.after_end then table.insert(removed_effects, effect.after_end) end
end end
mcl_potions._clear_cached_player_data(player) mcl_potions._clear_cached_effect_data(object)
for i=1, #removed_effects do for i=1, #removed_effects do
removed_effects[i](player) removed_effects[i](object)
end end
if set_hud ~= false then if set_hud ~= false then
potions_set_hud(player) potions_set_hud(object)
end end
end end
@ -1566,17 +1572,17 @@ end
minetest.register_on_leaveplayer( function(player) minetest.register_on_leaveplayer( function(player)
mcl_potions._save_player_effects(player) mcl_potions._save_player_effects(player)
mcl_potions._clear_cached_player_data(player) -- clear the buffer to prevent looking for a player not there 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 icon_ids[player:get_player_name()] = nil
end) end)
minetest.register_on_dieplayer( function(player) minetest.register_on_dieplayer( function(player)
mcl_potions._reset_player_effects(player) mcl_potions._reset_effects(player)
potions_set_hud(player) potions_set_hud(player)
end) end)
minetest.register_on_joinplayer( function(player) minetest.register_on_joinplayer( function(player)
mcl_potions._reset_player_effects(player, false) -- make sure there are no weird holdover effects mcl_potions._reset_effects(player, false) -- make sure there are no weird holdover effects
mcl_potions._load_player_effects(player) mcl_potions._load_player_effects(player)
mcl_potions._reset_haste_fatigue_item_meta(player) mcl_potions._reset_haste_fatigue_item_meta(player)
potions_init_icons(player) potions_init_icons(player)
@ -1716,12 +1722,29 @@ 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) local function target_valid(object, name)
if not object or object:get_hp() <= 0 then return false end if not object or object:get_hp() <= 0 then return false end
local entity = object:get_luaentity() local entity = object:get_luaentity()
if entity and entity.is_boss then return false end if entity and entity.is_boss then return false end
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 if not (registered_effects[name].res_condition
and registered_effects[name].res_condition(object)) then return true end and registered_effects[name].res_condition(object)) then return true end
end end