Merge branch 'master' into damage

This commit is contained in:
Elias Fleckenstein 2021-04-25 13:29:07 +02:00
commit 58d67aace6
8 changed files with 220 additions and 251 deletions

View file

@ -188,7 +188,7 @@ function boat.on_punch(self, puncher, time_from_last_punch, tool_capabilities, d
end end
function boat.on_step(self, dtime, moveresult) function boat.on_step(self, dtime, moveresult)
mcl_burning.tick(self.object, dtime) mcl_burning.tick(self.object, dtime, self)
self._v = get_v(self.object:get_velocity()) * get_sign(self._v) self._v = get_v(self.object:get_velocity()) * get_sign(self._v)
local v_factor = 1 local v_factor = 1

View file

@ -1,178 +1,89 @@
local S = minetest.get_translator("mcl_burning") local S = minetest.get_translator("mcl_burning")
function mcl_burning.get_default(datatype) function mcl_burning.get_storage(obj)
local default_table = {string = "", float = 0.0, int = 0, bool = false} return obj:is_player() and mcl_burning.storage[obj] or obj:get_luaentity()
return default_table[datatype]
end
function mcl_burning.get(obj, datatype, name)
local key
if obj:is_player() then
local meta = obj:get_meta()
return meta["get_" .. datatype](meta, "mcl_burning:" .. name)
else
local luaentity = obj:get_luaentity()
return luaentity and luaentity["mcl_burning_" .. name] or mcl_burning.get_default(datatype)
end
end
function mcl_burning.set(obj, datatype, name, value)
if obj:is_player() then
local meta = obj:get_meta()
meta["set_" .. datatype](meta, "mcl_burning:" .. name, value or mcl_burning.get_default(datatype))
else
local luaentity = obj:get_luaentity()
if mcl_burning.get_default(datatype) == value then
value = nil
end
luaentity["mcl_burning_" .. name] = value
end
end end
function mcl_burning.is_burning(obj) function mcl_burning.is_burning(obj)
return mcl_burning.get(obj, "float", "burn_time") > 0 return mcl_burning.get_storage(obj).burn_time
end end
function mcl_burning.is_affected_by_rain(obj) function mcl_burning.is_affected_by_rain(obj)
return mcl_weather and mcl_weather.get_weather() == "rain" and mcl_weather.is_outdoor(obj:get_pos()) return mcl_weather.get_weather() == "rain" and mcl_weather.is_outdoor(obj:get_pos())
end end
function mcl_burning.get_collisionbox(obj, smaller) function mcl_burning.get_collisionbox(obj, smaller, storage)
local cache = storage.collisionbox_cache
if cache then
local box = cache[smaller and 2 or 1]
return box[1], box[2]
else
local box = obj:get_properties().collisionbox local box = obj:get_properties().collisionbox
local minp, maxp = vector.new(box[1], box[2], box[3]), vector.new(box[4], box[5], box[6]) local minp, maxp = vector.new(box[1], box[2], box[3]), vector.new(box[4], box[5], box[6])
if smaller then
local s_vec = vector.new(0.1, 0.1, 0.1) local s_vec = vector.new(0.1, 0.1, 0.1)
minp = vector.add(minp, s_vec) local s_minp = vector.add(minp, s_vec)
maxp = vector.subtract(maxp, s_vec) local s_maxp = vector.subtract(maxp, s_vec)
end storage.collisionbox_cache = {{minp, maxp}, {s_minp, s_maxp}}
return minp, maxp return minp, maxp
end end
end
function mcl_burning.get_touching_nodes(obj, nodenames) function mcl_burning.get_touching_nodes(obj, nodenames, storage)
local pos = obj:get_pos() local pos = obj:get_pos()
local box = obj:get_properties().collisionbox local minp, maxp = mcl_burning.get_collisionbox(obj, true, storage)
local minp, maxp = mcl_burning.get_collisionbox(obj, true)
local nodes = minetest.find_nodes_in_area(vector.add(pos, minp), vector.add(pos, maxp), nodenames) local nodes = minetest.find_nodes_in_area(vector.add(pos, minp), vector.add(pos, maxp), nodenames)
return nodes return nodes
end end
function mcl_burning.get_highest_group_value(obj, groupname)
local nodes = mcl_burning.get_touching_nodes(obj, "group:" .. groupname, true)
local highest_group_value = 0
for _, pos in pairs(nodes) do
local node = minetest.get_node(pos)
local group_value = minetest.get_item_group(node.name, groupname)
if group_value > highest_group_value then
highest_group_value = group_value
end
end
return highest_group_value
end
function mcl_burning.damage(obj)
local luaentity = obj:get_luaentity()
local health
if luaentity then
health = luaentity.health
end
local hp = health or obj:get_hp()
if hp <= 0 then
return
end
local do_damage = true
if obj:is_player() then
if mcl_potions.player_has_effect(obj, "fire_proof") then
do_damage = false
else
local name = obj:get_player_name()
local deathmsg = S("@1 burned to death.", name)
local reason = mcl_burning.get(obj, "string", "reason")
if reason ~= "" then
deathmsg = S("@1 was burned by @2.", name, reason)
end
mcl_death_messages.player_damage(obj, deathmsg)
end
else
if luaentity.fire_damage_resistant then
do_damage = false
end
end
if do_damage then
mcl_util.deal_damage(obj, 1, {type = "on_fire"})
end
end
function mcl_burning.set_on_fire(obj, burn_time, reason) function mcl_burning.set_on_fire(obj, burn_time, reason)
if obj:get_hp() < 0 then if obj:get_hp() < 0 then
return return
end end
local storage = mcl_burning.get_storage(obj)
local luaentity = obj:get_luaentity() local luaentity = obj:get_luaentity()
if luaentity and luaentity.fire_resistant then if luaentity and luaentity.fire_resistant then
return return
end end
local old_burn_time = mcl_burning.get(obj, "float", "burn_time") if obj:is_player() and minetest.is_creative_enabled(obj:get_player_name()) then
burn_time = 0
else
local max_fire_prot_lvl = 0 local max_fire_prot_lvl = 0
local inv = mcl_util.get_inventory(obj)
local armor_list = inv and inv:get_list("armor")
if obj:is_player() then if armor_list then
if minetest.is_creative_enabled(obj:get_player_name()) then for _, stack in pairs(armor_list) do
burn_time = burn_time / 100
end
local inv = obj:get_inventory()
for i = 2, 5 do
local stack = inv:get_stack("armor", i)
local fire_prot_lvl = mcl_enchanting.get_enchantment(stack, "fire_protection") local fire_prot_lvl = mcl_enchanting.get_enchantment(stack, "fire_protection")
max_fire_prot_lvl = math.max(max_fire_prot_lvl, fire_prot_lvl) if fire_prot_lvl > max_fire_prot_lvl then
max_fire_prot_lvl = fire_prot_lvl
end
end end
end end
if max_fire_prot_lvl > 0 then if max_fire_prot_lvl > 0 then
burn_time = burn_time - math.floor(burn_time * max_fire_prot_lvl * 0.15) burn_time = burn_time - math.floor(burn_time * max_fire_prot_lvl * 0.15)
end end
end
if old_burn_time <= burn_time then if not storage.burn_time or burn_time >= storage.burn_time then
--[[local sound_id = mcl_burning.get(obj, "int", "sound_id") if obj:is_player() and not storage.fire_hud_id then
if sound_id == 0 then storage.fire_hud_id = obj:hud_add({
sound_id = minetest.sound_play("fire_fire", {
object = obj,
gain = 0.18,
max_hear_distance = 16,
loop = true,
}) + 1
end]]--
local hud_id
if obj:is_player() then
hud_id = mcl_burning.get(obj, "int", "hud_id")
if hud_id == 0 then
hud_id = obj:hud_add({
hud_elem_type = "image", hud_elem_type = "image",
position = {x = 0.5, y = 0.5}, position = {x = 0.5, y = 0.5},
scale = {x = -100, y = -100}, scale = {x = -100, y = -100},
text = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. 1, text = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. 1,
z_index = 1000, z_index = 1000,
}) + 1 })
end end
end storage.burn_time = burn_time
mcl_burning.set(obj, "float", "burn_time", burn_time) storage.burn_reason = reason
mcl_burning.set(obj, "string", "reason", reason) storage.fire_damage_timer = 0
mcl_burning.set(obj, "int", "hud_id", hud_id)
--mcl_burning.set(obj, "int", "sound_id", sound_id)
local fire_entity = minetest.add_entity(obj:get_pos(), "mcl_burning:fire") local fire_entity = minetest.add_entity(obj:get_pos(), "mcl_burning:fire")
local minp, maxp = mcl_burning.get_collisionbox(obj) local minp, maxp = mcl_burning.get_collisionbox(obj, false, storage)
local obj_size = obj:get_properties().visual_size local obj_size = obj:get_properties().visual_size
local vertical_grow_factor = 1.2 local vertical_grow_factor = 1.2
@ -186,111 +97,73 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
fire_entity:set_properties({visual_size = size}) fire_entity:set_properties({visual_size = size})
fire_entity:set_attach(obj, "", offset, {x = 0, y = 0, z = 0}) fire_entity:set_attach(obj, "", offset, {x = 0, y = 0, z = 0})
mcl_burning.update_animation_frame(obj, fire_entity, 0) local fire_luaentity = fire_entity:get_luaentity()
fire_luaentity:update_frame(obj, storage)
for _, other in pairs(minetest.get_objects_inside_radius(fire_entity:get_pos(), 0)) do
local other_luaentity = other:get_luaentity()
if other_luaentity and other_luaentity.name == "mcl_burning:fire" and other_luaentity ~= fire_luaentity then
other:remove()
break
end
end
end end
end end
function mcl_burning.extinguish(obj) function mcl_burning.extinguish(obj)
if mcl_burning.is_burning(obj) then if mcl_burning.is_burning(obj) then
--local sound_id = mcl_burning.get(obj, "int", "sound_id") - 1 local storage = mcl_burning.get_storage(obj)
--minetest.sound_stop(sound_id) if obj:is_player() then
if storage.fire_hud_id then
obj:hud_remove(storage.fire_hud_id)
end
mcl_burning.storage[obj] = {}
else
storage.burn_time = nil
storage.burn_reason = nil
storage.fire_damage_timer = nil
end
end
end
function mcl_burning.tick(obj, dtime, storage)
if storage.burn_time then
storage.burn_time = storage.burn_time - dtime
if storage.burn_time <= 0 or mcl_burning.is_affected_by_rain(obj) or #mcl_burning.get_touching_nodes(obj, "group:puts_out_fire", storage) > 0 then
mcl_burning.extinguish(obj)
return true
else
storage.fire_damage_timer = storage.fire_damage_timer + dtime
if storage.fire_damage_timer >= 1 then
storage.fire_damage_timer = 0
local hp = mcl_util.get_hp(obj)
if hp > 0 then
local do_damage = true
if obj:is_player() then if obj:is_player() then
local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1 if mcl_potions.player_has_effect(obj, "fire_proof") then
obj:hud_remove(hud_id) do_damage = false
end
mcl_burning.set(obj, "string", "reason")
mcl_burning.set(obj, "float", "burn_time")
mcl_burning.set(obj, "float", "damage_timer")
mcl_burning.set(obj, "int", "hud_id")
--mcl_burning.set(obj, "int", "sound_id")
end
end
function mcl_burning.catch_fire_tick(obj, dtime)
if mcl_burning.is_affected_by_rain(obj) or #mcl_burning.get_touching_nodes(obj, "group:puts_out_fire") > 0 then
mcl_burning.extinguish(obj)
else else
local set_on_fire_value = mcl_burning.get_highest_group_value(obj, "set_on_fire") local name = obj:get_player_name()
local deathmsg = S("@1 burned to death.", name)
if set_on_fire_value > 0 then if storage.reason then
mcl_burning.set_on_fire(obj, set_on_fire_value) deathmsg = S("@1 was burned by @2.", name, storage.reason)
end end
mcl_death_messages.player_damage(obj, deathmsg)
end end
elseif obj:get_luaentity().fire_damage_resistant then
do_damage = false
end end
function mcl_burning.tick(obj, dtime) if do_damage then
local burn_time = mcl_burning.get(obj, "float", "burn_time") - dtime mcl_util.deal_damage(obj, 1, {reason = "on_fire"})
if burn_time <= 0 then
mcl_burning.extinguish(obj)
else
mcl_burning.set(obj, "float", "burn_time", burn_time)
local damage_timer = mcl_burning.get(obj, "float", "damage_timer") + dtime
if damage_timer >= 1 then
damage_timer = 0
mcl_burning.damage(obj)
end
mcl_burning.set(obj, "float", "damage_timer", damage_timer)
end
mcl_burning.catch_fire_tick(obj, dtime)
end
function mcl_burning.update_animation_frame(obj, fire_entity, animation_frame)
local fire_texture = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. animation_frame
local fire_HUD_texture = "mcl_burning_hud_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. animation_frame
fire_entity:set_properties({textures = {"blank.png", "blank.png", fire_texture, fire_texture, fire_texture, fire_texture}})
if obj:is_player() then
local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1
obj:hud_change(hud_id, "text", fire_HUD_texture)
end
end
function mcl_burning.fire_entity_step(self, dtime)
if self.removed then
return
end
local obj = self.object
local parent = obj:get_attach()
local do_remove
self.doing_step = true
if not parent or not mcl_burning.is_burning(parent) then
do_remove = true
else
for _, other in pairs(minetest.get_objects_inside_radius(obj:get_pos(), 0)) do
local luaentity = obj:get_luaentity()
if luaentity and luaentity.name == "mcl_burning:fire" and not luaentity.doing_step and not luaentity.removed then
do_remove = true
break
end end
end end
end end
self.doing_step = false
if do_remove then
self.removed = true
obj:remove()
return
end end
local animation_timer = self.animation_timer + dtime
if animation_timer >= 0.015 then
animation_timer = 0
local animation_frame = self.animation_frame + 1
if animation_frame > mcl_burning.animation_frames - 1 then
animation_frame = 0
end end
mcl_burning.update_animation_frame(parent, obj, animation_frame)
self.animation_frame = animation_frame
end
self.animation_timer = animation_timer
end end

View file

@ -2,11 +2,65 @@ local S = minetest.get_translator("mcl_burning")
local modpath = minetest.get_modpath("mcl_burning") local modpath = minetest.get_modpath("mcl_burning")
mcl_burning = { mcl_burning = {
storage = {},
animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8 animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8
} }
dofile(modpath .. "/api.lua") dofile(modpath .. "/api.lua")
minetest.register_globalstep(function(dtime)
for _, player in pairs(minetest.get_connected_players()) do
local storage = mcl_burning.storage[player]
if not mcl_burning.tick(player, dtime, storage) and not mcl_burning.is_affected_by_rain(player) then
local nodes = mcl_burning.get_touching_nodes(player, {"group:puts_out_fire", "group:set_on_fire"}, storage)
local burn_time = 0
for _, pos in pairs(nodes) do
local node = minetest.get_node(pos)
if minetest.get_item_group(node.name, "puts_out_fire") > 0 then
burn_time = 0
break
end
local value = minetest.get_item_group(node.name, "set_on_fire")
if value > burn_time then
burn_time = value
end
end
if burn_time > 0 then
mcl_burning.set_on_fire(player, burn_time)
end
end
end
end)
minetest.register_on_respawnplayer(function(player)
mcl_burning.extinguish(player)
end)
minetest.register_on_joinplayer(function(player)
local storage
local burn_data = player:get_meta():get_string("mcl_burning:data")
if burn_data == "" then
storage = {}
else
storage = minetest.deserialize(burn_data)
end
mcl_burning.storage[player] = storage
end)
minetest.register_on_leaveplayer(function(player)
local storage = mcl_burning.storage[player]
storage.fire_hud_id = nil
player:get_meta():set_string("mcl_burning:data", minetest.serialize(storage))
mcl_burning.storage[player] = nil
end)
minetest.register_entity("mcl_burning:fire", { minetest.register_entity("mcl_burning:fire", {
initial_properties = { initial_properties = {
physical = false, physical = false,
@ -18,21 +72,45 @@ minetest.register_entity("mcl_burning:fire", {
animation_frame = 0, animation_frame = 0,
animation_timer = 0, animation_timer = 0,
on_step = mcl_burning.fire_entity_step,
on_step = function(self, dtime)
local parent, storage = self:sanity_check()
if parent then
self.animation_timer = self.animation_timer + dtime
if self.animation_timer >= 0.1 then
self.animation_timer = 0
self.animation_frame = self.animation_frame + 1
if self.animation_frame > mcl_burning.animation_frames - 1 then
self.animation_frame = 0
end
self:update_frame(parent, storage)
end
else
self.object:remove()
end
end,
sanity_check = function(self)
local parent = self.object:get_attach()
if not parent then
return
end
local storage = mcl_burning.get_storage(parent)
if not storage or not storage.burn_time then
return
end
return parent, storage
end,
update_frame = function(self, parent, storage)
local frame_overlay = "^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. self.animation_frame
local fire_texture = "mcl_burning_entity_flame_animated.png" .. frame_overlay
self.object:set_properties({textures = {"blank.png", "blank.png", fire_texture, fire_texture, fire_texture, fire_texture}})
if parent:is_player() then
parent:hud_change(storage.fire_hud_id, "text", "mcl_burning_hud_flame_animated.png" .. frame_overlay)
end
end,
}) })
minetest.register_globalstep(function(dtime)
for _, player in pairs(minetest.get_connected_players()) do
if player:get_meta():get_float("mcl_burning:burn_time") > 0 then
mcl_burning.tick(player, dtime)
end
end
end)
minetest.register_on_respawnplayer(function(player)
mcl_burning.extinguish(player)
end)
minetest.register_on_leaveplayer(function(player)
mcl_burning.set(player, "int", "hud_id")
end)

View file

@ -3455,8 +3455,8 @@ end
-- main mob function -- main mob function
local mob_step = function(self, dtime) local mob_step = function(self, dtime)
if not self.fire_resistant and self.mcl_burning_burn_time and self.mcl_burning_burn_time > 0 then if not self.fire_resistant then
mcl_burning.tick(self.object, dtime) mcl_burning.tick(self.object, dtime, self)
end end
if use_cmi then if use_cmi then

View file

@ -109,7 +109,7 @@ local damage_particles = function(pos, is_critical)
end end
ARROW_ENTITY.on_step = function(self, dtime) ARROW_ENTITY.on_step = function(self, dtime)
mcl_burning.tick(self.object, dtime) mcl_burning.tick(self.object, dtime, self)
self._time_in_air = self._time_in_air + .001 self._time_in_air = self._time_in_air + .001

View file

@ -61,7 +61,7 @@ function mcl_enchanting.update_groupcaps(itemstack)
if not hash or hash ~= groupcaps.hash then if not hash or hash ~= groupcaps.hash then
local tool_capabilities = itemstack:get_tool_capabilities() local tool_capabilities = itemstack:get_tool_capabilities()
tool_capabilities.groupcaps = groupcaps.values tool_capabilities.groupcaps = table.copy(groupcaps.values)
-- Increase the number of uses depending on the unbreaking level -- Increase the number of uses depending on the unbreaking level
-- of the tool. -- of the tool.

View file

@ -7,8 +7,8 @@ Drop registered inventories on player death.
* function(player): must return inventory * function(player): must return inventory
* listname: string * listname: string
* drop: bool * drop: bool
* true: the entire list will be dropped * true: the list will be dropped
* false: items with curse_of_vanishing enchantement will be broken. * false: the list will only be cleared
## mcl_death_drop.registered_dropped_lists ## mcl_death_drop.registered_dropped_lists
Table containing dropped list inventory, name and drop state. Table containing dropped list inventory, name and drop state.

View file

@ -185,6 +185,24 @@ minetest.register_globalstep(function(dtime)
if vector.length(player_velocity) < 40 then if vector.length(player_velocity) < 40 then
local add_velocity = player.add_velocity or player.add_player_velocity local add_velocity = player.add_velocity or player.add_player_velocity
add_velocity(player, vector.multiply(player:get_look_dir(), 4)) add_velocity(player, vector.multiply(player:get_look_dir(), 4))
minetest.add_particlespawner({
amount = 1,
time = 0.1,
minpos = fly_pos,
maxpos = fly_pos,
minvel = {x = 0, y = 0, z = 0},
maxvel = {x = 0, y = 0, z = 0},
minacc = {x = 0, y = 0, z = 0},
maxacc = {x = 0, y = 0, z = 0},
minexptime = 0.3,
maxexptime = 0.5,
minsize = 1,
maxsize = 2.5,
collisiondetection = false,
vertical = false,
texture = "mcl_particles_crit.png^[colorize:#bc7a57:127",
glow = 5,
})
end end
end end
else else