Rewrite burning API
This commit is contained in:
parent
a03e7f6f3a
commit
9eba2add3f
|
@ -188,7 +188,7 @@ function boat.on_punch(self, puncher, time_from_last_punch, tool_capabilities, d
|
|||
end
|
||||
|
||||
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)
|
||||
local v_factor = 1
|
||||
|
|
|
@ -1,132 +1,52 @@
|
|||
local S = minetest.get_translator("mcl_burning")
|
||||
|
||||
function mcl_burning.get_default(datatype)
|
||||
local default_table = {string = "", float = 0.0, int = 0, bool = false}
|
||||
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
|
||||
function mcl_burning.get_storage(obj)
|
||||
return obj:is_player() and mcl_burning.storage[obj] or obj:get_luaentity()
|
||||
end
|
||||
|
||||
function mcl_burning.is_burning(obj)
|
||||
return mcl_burning.get(obj, "float", "burn_time") > 0
|
||||
return mcl_burning.get_storage(obj).burn_time
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
function mcl_burning.get_collisionbox(obj, smaller)
|
||||
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])
|
||||
if smaller then
|
||||
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 minp, maxp = vector.new(box[1], box[2], box[3]), vector.new(box[4], box[5], box[6])
|
||||
local s_vec = vector.new(0.1, 0.1, 0.1)
|
||||
minp = vector.add(minp, s_vec)
|
||||
maxp = vector.subtract(maxp, s_vec)
|
||||
local s_minp = vector.add(minp, s_vec)
|
||||
local s_maxp = vector.subtract(maxp, s_vec)
|
||||
storage.collisionbox_cache = {{minp, maxp}, {s_minp, s_maxp}}
|
||||
return minp, maxp
|
||||
end
|
||||
return minp, maxp
|
||||
end
|
||||
|
||||
function mcl_burning.get_touching_nodes(obj, nodenames)
|
||||
function mcl_burning.get_touching_nodes(obj, nodenames, storage)
|
||||
local pos = obj:get_pos()
|
||||
local box = obj:get_properties().collisionbox
|
||||
local minp, maxp = mcl_burning.get_collisionbox(obj, true)
|
||||
local minp, maxp = mcl_burning.get_collisionbox(obj, true, storage)
|
||||
local nodes = minetest.find_nodes_in_area(vector.add(pos, minp), vector.add(pos, maxp), nodenames)
|
||||
return nodes
|
||||
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()
|
||||
armor.last_damage_types[name] = "fire"
|
||||
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
|
||||
local new_hp = hp - 1
|
||||
if health then
|
||||
luaentity.health = new_hp
|
||||
else
|
||||
obj:set_hp(new_hp)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_burning.set_on_fire(obj, burn_time, reason)
|
||||
if obj:get_hp() < 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local storage = mcl_burning.get_storage(obj)
|
||||
|
||||
local luaentity = obj:get_luaentity()
|
||||
if luaentity and luaentity.fire_resistant then
|
||||
return
|
||||
end
|
||||
|
||||
local old_burn_time = mcl_burning.get(obj, "float", "burn_time")
|
||||
local max_fire_prot_lvl = 0
|
||||
|
||||
if obj:is_player() then
|
||||
|
@ -148,37 +68,22 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
|
|||
burn_time = burn_time - math.floor(burn_time * max_fire_prot_lvl * 0.15)
|
||||
end
|
||||
|
||||
if old_burn_time <= burn_time then
|
||||
--[[local sound_id = mcl_burning.get(obj, "int", "sound_id")
|
||||
if sound_id == 0 then
|
||||
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",
|
||||
position = {x = 0.5, y = 0.5},
|
||||
scale = {x = -100, y = -100},
|
||||
text = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. 1,
|
||||
z_index = 1000,
|
||||
}) + 1
|
||||
end
|
||||
if not storage.burn_time or burn_time >= storage.burn_time then
|
||||
if obj:is_player() and not storage.fire_hud_id then
|
||||
storage.fire_hud_id = obj:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 0.5, y = 0.5},
|
||||
scale = {x = -100, y = -100},
|
||||
text = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. 1,
|
||||
z_index = 1000,
|
||||
})
|
||||
end
|
||||
mcl_burning.set(obj, "float", "burn_time", burn_time)
|
||||
mcl_burning.set(obj, "string", "reason", reason)
|
||||
mcl_burning.set(obj, "int", "hud_id", hud_id)
|
||||
--mcl_burning.set(obj, "int", "sound_id", sound_id)
|
||||
storage.burn_time = burn_time
|
||||
storage.burn_reason = reason
|
||||
storage.fire_damage_timer = 0
|
||||
|
||||
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 vertical_grow_factor = 1.2
|
||||
|
@ -192,111 +97,81 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
|
|||
|
||||
fire_entity:set_properties({visual_size = size})
|
||||
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
|
||||
|
||||
function mcl_burning.extinguish(obj)
|
||||
if mcl_burning.is_burning(obj) then
|
||||
--local sound_id = mcl_burning.get(obj, "int", "sound_id") - 1
|
||||
--minetest.sound_stop(sound_id)
|
||||
|
||||
local storage = mcl_burning.get_storage(obj)
|
||||
if obj:is_player() then
|
||||
local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1
|
||||
obj:hud_remove(hud_id)
|
||||
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
|
||||
local set_on_fire_value = mcl_burning.get_highest_group_value(obj, "set_on_fire")
|
||||
|
||||
if set_on_fire_value > 0 then
|
||||
mcl_burning.set_on_fire(obj, set_on_fire_value)
|
||||
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)
|
||||
local burn_time = mcl_burning.get(obj, "float", "burn_time") - dtime
|
||||
function mcl_burning.tick(obj, dtime, storage)
|
||||
if storage.burn_time then
|
||||
storage.burn_time = storage.burn_time - dtime
|
||||
|
||||
if burn_time <= 0 then
|
||||
mcl_burning.extinguish(obj)
|
||||
else
|
||||
mcl_burning.set(obj, "float", "burn_time", burn_time)
|
||||
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
|
||||
|
||||
local damage_timer = mcl_burning.get(obj, "float", "damage_timer") + dtime
|
||||
if storage.fire_damage_timer >= 1 then
|
||||
storage.fire_damage_timer = 0
|
||||
|
||||
if damage_timer >= 1 then
|
||||
damage_timer = 0
|
||||
mcl_burning.damage(obj)
|
||||
end
|
||||
local luaentity = obj:get_luaentity()
|
||||
local is_mob = luaentity and luaentity._cmi_is_mob
|
||||
local hp = is_mob and luaentity.health or obj:get_hp()
|
||||
|
||||
mcl_burning.set(obj, "float", "damage_timer", damage_timer)
|
||||
end
|
||||
if hp > 0 then
|
||||
local do_damage = true
|
||||
|
||||
mcl_burning.catch_fire_tick(obj, dtime)
|
||||
end
|
||||
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()
|
||||
armor.last_damage_types[name] = "fire"
|
||||
local deathmsg = S("@1 burned to death.", name)
|
||||
if storage.reason then
|
||||
deathmsg = S("@1 was burned by @2.", name, storage.reason)
|
||||
end
|
||||
mcl_death_messages.player_damage(obj, deathmsg)
|
||||
end
|
||||
elseif luaentity.fire_damage_resistant then
|
||||
do_damage = false
|
||||
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
|
||||
if do_damage then
|
||||
local new_hp = hp - 1
|
||||
if is_mob then
|
||||
luaentity.health = new_hp
|
||||
else
|
||||
obj:set_hp(new_hp)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.doing_step = false
|
||||
|
||||
if do_remove then
|
||||
self.removed = true
|
||||
obj:remove()
|
||||
return
|
||||
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
|
||||
mcl_burning.update_animation_frame(parent, obj, animation_frame)
|
||||
self.animation_frame = animation_frame
|
||||
end
|
||||
self.animation_timer = animation_timer
|
||||
end
|
||||
|
|
|
@ -2,11 +2,65 @@ local S = minetest.get_translator("mcl_burning")
|
|||
local modpath = minetest.get_modpath("mcl_burning")
|
||||
|
||||
mcl_burning = {
|
||||
storage = {},
|
||||
animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8
|
||||
}
|
||||
|
||||
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", {
|
||||
initial_properties = {
|
||||
physical = false,
|
||||
|
@ -18,19 +72,45 @@ minetest.register_entity("mcl_burning:fire", {
|
|||
|
||||
animation_frame = 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
|
||||
mcl_burning.tick(player, dtime)
|
||||
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)
|
||||
|
|
|
@ -3454,8 +3454,8 @@ end
|
|||
-- main mob function
|
||||
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
|
||||
mcl_burning.tick(self.object, dtime)
|
||||
if not self.fire_resistant then
|
||||
mcl_burning.tick(self.object, dtime, self)
|
||||
end
|
||||
|
||||
if use_cmi then
|
||||
|
|
|
@ -108,7 +108,7 @@ local damage_particles = function(pos, is_critical)
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
|
|
Loading…
Reference in New Issue