late/conditions.lua
2018-11-19 09:18:14 +01:00

148 lines
4.3 KiB
Lua

--[[
Late library for Minetest - Library adding temporary effects.
(c) Pierre-Yves Rollo
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation, either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
--]]
-- Interval in seconds of ABM checking for targets being near nodes with effect
-- TODO:Move into a mod settings
local abm_interval = 1
-- Conditions registry
----------------------
local condition_types = { }
function late.register_condition_type(name, definition)
local def = table.copy(definition)
def.name = name
condition_types[name] = def
end
function late.get_condition_type(name)
return condition_types[name]
end
local function call_condition_function(function_name, type_name, data, target, effect)
local condition_type = condition_types[type_name]
if condition_type and condition_type[function_name] and
type(condition_type[function_name]) == "function"
then
return condition_type[function_name](data, target, effect)
else
return nil
end
end
function late.condition_check(type_name, data, target, effect)
return call_condition_function('check', type_name, data, target, effect)
or false -- unknown condition always false
end
function late.condition_step(type_name, data, target, effect)
call_condition_function('step', type_name, data, target, effect)
end
-- Base conditions
------------------
late.register_condition_type('duration', {
check = function(data, target, effect)
return effect.elapsed_time < data
end,
})
late.register_condition_type('equiped_with', {
check = function(data, target, effect)
-- Is the target equiped with item_name?
-- Check wielded item
local stack = target:get_wielded_item()
return (stack and stack:get_name() == data) or false
end,
})
-- ABM to detect if player gets nearby a nodes with effect (belonging to
-- group:effect_trigger and having an effect in node definition)
minetest.register_abm({
label = "late player detection",
nodenames = "group:effect_trigger",
interval = abm_interval,
chance = 1,
catch_up = true,
action = function(pos, node)
local ndef = minetest.registered_nodes[node.name]
local effect_def = ndef.effect_near
if effect_def then
for _, target in pairs(minetest.get_objects_inside_radius(
pos, (effect_def.distance or 0) + (effect_def.spread or 0))) do
effect_def.id = 'near:'..node.name
local effect = late.get_effect_by_id(target, effect_def.id)
if effect == nil then
effect = late.new_effect(target, effect_def)
if effect == nil then return end
effect:set_conditions({ near_node = {
node_name = node.name,
radius = effect_def.distance,
spread = effect_def.spread,
active_pos = {}
} } )
end
-- Register node position as an active position
effect.conditions.near_node
.active_pos[minetest.hash_node_position(pos)] = true
-- Restart effect in case it was in fall phase
effect:restart()
end
end
end,
})
late.register_condition_type('near_node', {
check = function(data, target, effect)
return effect.distance_intensity ~= nil
end,
step = function(data, target, effect)
-- Discard too far or not uptodate nodes from near_nodes list and compute min
-- distance and intensity according to it
local distance, min_distance
for hash, _ in pairs(data.active_pos) do
local pos = minetest.get_position_from_hash(hash)
distance = vector.distance(target:get_pos(), pos)
if distance < (data.radius or 0) + (data.spread or 0) and
minetest.get_node(pos).name == (data.node_name or "")
then
min_distance = math.min(min_distance or distance, distance)
else
data.active_pos[hash] = nil
end
end
if min_distance == nil then effect.distance_intensity = nil
else
--
effect.distance_intensity = data.spread and math.min(1, ((data.radius
or 0) + data.spread - min_distance) / data.spread) or 1
end
end,
})