minesfence_game/mods/default/firmness.lua

483 lines
18 KiB
Lua

--
-- Firmness nodes
--
-- functions for manipulate with nodes which have defined firmness and resilience.
--
-- firmness mean number of steps under where should be any type of prop to prvent falling
-- resilience mean number of step after prop is out of use, where increase probability of start falling
function default.is_should_fall(pos, node, only_firmness, to_stable)
--minetest.log("warning", "default.is_should_fall called.");
local node_def = minetest.registered_nodes[node.name];
local firmness = node_def.groups["firmness"];
local resilience = node_def.groups["resilience"];
local is_should_fall = true;
if (not(firmness)) then firmness = 0; end
if (not(resilience)) then resilience = 0; end
if ((firmness==0) and (resilience==0)) then
--minetest.log("warning", "Fall of "..node.name.."?");
-- if node is falling group, fall will be caused by game engine
return false;
end
--minetest.log("warning", "Is_should_fall node "..node.name.." firmness: "..tostring(firmness).." resiliance: "..tostring(resilience));
local pos_find = table.copy(pos);
pos_find.y = pos.y - 1;
local pos_distance = table.copy(pos_find);
if (firmness>0) then
for x_diff = -firmness,firmness,1 do
for z_diff = -firmness,firmness,1 do
pos_find.x = pos.x + x_diff;
pos_find.z = pos.z + z_diff;
--minetest.log("warning", "x_diff: "..tostring(x_diff).." z_diff: "..tostring(z_diff))
--minetest.log("warning", "pos: "..dump(pos).." pos_find: "..dump(pos_find).."pos_distance: "..dump(pos_distance))
local distance = vector.distance(pos_distance, pos_find);
--minetest.log("warning", "pos: "..dump(pos).." pos_find: "..dump(pos_find).."pos_distance: "..dump(pos_distance))
if (distance<=firmness) then
local check_node = minetest.get_node(pos_find);
if (check_node.name~="ignore") then
--minetest.log("warning", "Node "..check_node.name.." on y: "..tostring(pos_find.y).." x: "..tostring(pos_find.x).." z:"..tostring(pos_find.z).." distance: "..tostring(distance).." x_diff: "..tostring(x_diff).." z_diff: "..tostring(z_diff))
local node_def = minetest.registered_nodes[check_node.name];
local node_walkable = node_def.walkable;
local falling_node = node_def.groups["falling_node"];
if (not(falling_node)) then falling_node = 0; end
if ((node_walkable==true) and (falling_node==0)) then
--minetest.log("warning", "Walkable on "..dump(pos_find).." for "..dump(pos))
is_should_fall = false;
if (to_stable==true) then
node.name = default.firmness_node_to_stable[node.name];
if (node.name) then
--minetest.log("warning", "To stable "..dump(pos))
minetest.swap_node(pos, node);
end
end
return false;
end
end
end
end
if (is_should_fall==false) then break; end
end
end
if ((is_should_fall==true) and (resilience>0) and (only_firmness==false)) then
local fall_chance = 1.0;
local max_distance = firmness + resilience;
-- calculate begining fail_chance
pos_find.y = pos.y + 1;
local check_node = minetest.get_node(pos_find);
if (check_node.name~="ignore") then
local node_def = minetest.registered_nodes[check_node.name];
local node_walkable = node_def.walkable;
if (node_walkable==true) then
fall_chance = fall_chance - 0.02;
end
end
pos_find.y = pos.y;
for x_diff = -1,1,2 do
for z_diff = -1,1,2 do
pos_find.x = pos.x + x_diff;
pos_find.z = pos.z + z_diff;
local check_node = minetest.get_node(pos_find);
if (check_node.name~="ignore") then
local node_def = minetest.registered_nodes[check_node.name];
local node_walkable = node_def.walkable;
local falling_node = node_def.groups["falling_node"];
if (not(falling_node)) then falling_node = 0; end
if ((node_walkable==true) and (falling_node==0)) then
fall_chance = fall_chance - 0.07;
end
end
end
end
pos_find.y = pos.y - 1;
local chance = default.random_generator:next(0, 16777215)/16777215.0;
if (chance<=fall_chance) then
for x_diff = -max_distance,max_distance,1 do
for z_diff = -max_distance,max_distance,1 do
pos_find.x = pos.x + x_diff;
pos_find.z = pos.z + z_diff;
local distance = vector.distance(pos_distance, pos_find);
if ((distance>firmness) and (distance<=max_distance)) then
local check_node = minetest.get_node(pos_find);
if (check_node.name~="ignore") then
--minetest.log("warning", "Node "..check_node.name.." on y: "..tostring(pos_find.y).." x: "..tostring(pos_find.x).." z:"..tostring(pos_find.z).." distance: "..tostring(distance).." x_diff: "..tostring(x_diff).." z_diff: "..tostring(z_diff))
local node_def = minetest.registered_nodes[check_node.name];
local node_walkable = node_def.walkable;
local falling_node = node_def.groups["falling_node"];
if (not(falling_node)) then falling_node = 0; end
if ((node_walkable==true) and (falling_node==0)) then
--minetest.log("warning", "Walkable on "..dump(pos_find).." for "..dump(pos))
local risk_prevent_power = (distance-firmness)/resilience;
fall_chance = fall_chance * risk_prevent_power;
--minetest.log("warning", "Risk_prevent_power: "..tostring(risk_prevent_power))
end
end
end
end
end
end
if (chance>fall_chance) then
is_should_fall = false;
end
--minetest.log("warning", "random chance: "..tostring(chance).." fall chance:"..tostring(fall_chance))
end
--minetest.log("warning", "Returning value: "..tostring(is_should_fall))
return is_should_fall;
end
function default.check_neighbour_for_fall(pos)
--minetest.log("warning", "Check neighbour.");
--minetest.log("warning", "Check neighbour on: "..dump(pos));
local check_pos = table.copy(pos);
for y_diff = 0,1,1 do
check_pos.y = pos.y + y_diff;
for x_diff = -1,1,1 do
check_pos.x = pos.x + x_diff;
for z_diff = -1,1,1 do
check_pos.z = pos.z + z_diff;
--minetest.log("warning", "After destruct x_diff: "..tostring(x_diff).." z_diff: "..tostring(z_diff).." y_diff: "..tostring(y_diff));
local check_node = minetest.get_node(check_pos);
if (check_node.name~="ignore") then
local fall_it = default.is_should_fall(check_pos, check_node, true, false);
if (fall_it==true) then
default.fall_stable_node(check_pos, check_node, true);
if (y_diff>0) then
--minetest.log("warning", "Near refall check of "..check_node.name);
minetest.after(1, default.check_neighbour_for_fall, table.copy(check_pos));
end
end
end
end
end
end
end
-- when some node starts to fall, it can create cave-in effect.
-- use group cavein with values 1-100 to set probability of canein effect
function default.check_for_cavein(pos)
local node = minetest.get_node(pos);
local node_def = minetest.registered_nodes[node.name];
local cavein = node_def.groups["cavein"];
minetest.log("warning", "Node: "..node.name.." Cavein: "..tostring(cavein))
if ((cavein) and (cavein>0)) then
local check_pos = table.copy(pos);
check_pos.y = pos.y -1;
local check_node = minetest.get_node(check_pos);
if (check_node.name=="ignore") then
return;
end
local node_def = minetest.registered_nodes[check_node.name];
local node_walkable = node_def.walkable;
if (node_walkable==true) then
return;
end
local no_cavein_points = 0;
local cavein_points = 0;
local recall_pos = {};
for y_diff = -1,1,1 do
check_pos.y = pos.y + y_diff;
local y_factor = (y_diff/2.0)+1.0; -- 0.5 to 1.5
for x_diff = -1,1,1 do
check_pos.x = pos.x + x_diff;
for z_diff = -1,1,1 do
check_pos.z = pos.z + z_diff;
if ((y_diff~=0) or (x_diff~=0) or (z_diff~=0)) then
local check_node = minetest.get_node(check_pos);
if (check_node.name~="ignore") then
local node_def = minetest.registered_nodes[check_node.name];
local node_walkable = node_def.walkable;
local distance = vector.distance(pos, check_pos);
if (node_walkable==false) then
-- remove stability
cavein_points = cavein_points + (cavein/(y_factor*distance));
else
-- add stability
no_cavein_points = no_cavein_points + (100/(y_factor*distance));
table.insert(recall_pos, table.copy(check_pos));
end
minetest.log("warning", "CaveIn points: "..tostring(cavein_points).."No cavin points:"..tostring(no_cavein_points).." cavein: "..tostring(cavein).." y_factor: "..tostring(y_factor).." distance:"..tostring(distance).." node_walkable: "..tostring(node_walkable));
end
end
end
end
end
local chance = (default.random_generator:next(0, 65535)/65535.0);
local no_cavein_chance = no_cavein_points/(no_cavein_points+cavein_points);
minetest.log("warning", "Chance: "..tostring(chance).." No_cavein_chance: "..tostring(no_cavein_chance));
if (chance>no_cavein_chance) then
default.fall_stable_node(pos, node, false);
for key, recall_pos in pairs(recall_pos) do
minetest.after(0.4, default.check_for_cavein, recall_pos);
end
end
end
end
-- if falling done fall to another node, and there is free space in another node level, it can happen to fall into that area
-- use group landslide with values 1-100 to set probability of landslide happen
function default.check_for_landslide(pos)
local node = minetest.get_node(pos);
local node_def = minetest.registered_nodes[node.name];
local landslide = node_def.groups["landslide"];
--minetest.log("warning", "Landslide: "..tostring(landslide));
if ((landslide) and (landslide>0)) then
local check_pos = table.copy(pos);
check_pos.y = pos.y -1;
local check_node = minetest.get_node(check_pos);
if (check_node.name=="ignore") then
return;
end
local node_def = minetest.registered_nodes[check_node.name];
local node_falling = node_def.groups["falling_node"];
if (not(node_falling) or (node_falling<=0)) then
return;
end
--minetest.log("warning", "Find positions of posible landslide,");
local landslide_pos = {};
for x_diff = -1,1,1 do
check_pos.x = pos.x + x_diff;
for z_diff = -1,1,1 do
check_pos.z = pos.z + z_diff;
if ((x_diff~=0)~=(z_diff~=0)) then -- xor
check_pos.y = pos.y;
local check_node = minetest.get_node(check_pos);
if (check_node.name~="ignore") then
local node_def = minetest.registered_nodes[check_node.name];
if (node_def.buildable_to==true) then
check_pos.y = pos.y - 1;
local check_node = minetest.get_node(check_pos);
if (check_node.name~="ignore") then
local node_def = minetest.registered_nodes[check_node.name];
if (node_def.buildable_to==true) then
table.insert(landslide_pos, table.copy(check_pos))
end
end
end
end
end
end
end
--minetest.log("warning", dump(landslide_pos));
local positions = #landslide_pos;
if (positions>0) then
local chance = (default.random_generator:next(0, 65535)/65535.0)*100;
--minetest.log("warning", "Chance: "..tostring(chance).." Landslide: "..tostring(landslide));
if (chance<=landslide) then
local move_pos = {};
if (positions>1) then
local index = default.random_generator:next(1,#landslide_pos)
move_pos = landslide_pos[index];
else
move_pos = landslide_pos[1];
end
minetest.set_node(move_pos, node);
minetest.remove_node(pos);
minetest.check_single_for_falling(move_pos);
end
end
end
end
-- should be done for neightbour of stable or normal destroyed node
-- default neightbour is 8 ( expect sum of firmness and resilience is not bigger then 8)
function default.neighbour_stable_to_normal(pos)
local pos1 = {x = pos.x-8, y = pos.y-8, z = pos.z-8};
local pos2 = {x = pos.x+8, y = pos.y+8, z = pos.z+8};
local positions = minetest.find_nodes_in_area(pos1, pos2, default.firmness_node_stable_names);
-- minetest.log("warning", "Area "..dump(pos1).." to "..dump(pos2).." found "..tostring(#positions));
if (positions) then
for i, find_pos in ipairs(positions) do
local find_node = minetest.get_node(find_pos);
find_node.name = default.firmness_node_from_stable[find_node.name];
if (find_node.name) then
minetest.swap_node(find_pos, find_node);
end
end
end
end
default.firmness_node_to_falling = {};
default.firmness_node_falling_names = {};
default.firmness_node_to_stable = {};
default.firmness_node_from_stable = {};
default.firmness_node_stable_names = {};
function default.fall_stable_node(pos, node, check_cavein)
--minetest.log("warning", "fall_stable_node.");
local new_node_name = default.firmness_node_to_falling[node.name];
if (new_node_name~=nil) then
--minetest.log("warning", "Falling 296: "..dump(pos));
minetest.swap_node(pos,{name=new_node_name});
else
minetest.spawn_falling_node(pos);
end
minetest.check_for_falling(pos);
default.neighbour_stable_to_normal(pos);
if (check_cavein==true) then
local check_pos = table.copy(pos);
for x_diff = -1,1,1 do
check_pos.x = pos.x + x_diff;
for z_diff = -1,1,1 do
check_pos.z = pos.z + z_diff;
for y_diff = -1,1,1 do
check_pos.y = pos.y + y_diff;
if ((x_diff~=0) or (y_diff~=0) or (z_diff~=0)) then
--minetest.log("warning", "X: "..tostring(check_pos.x).." Y:"..tostring(check_pos.y).." Z: "..tostring(check_pos.z));
default.check_for_cavein(check_pos);
end
end
end
end
end
end
function default.register_firmness_node_change(node_name, falling_node_name, stable_node_name)
if (falling_node_name) then
default.firmness_node_to_falling[node_name] = falling_node_name;
table.insert(default.firmness_node_falling_names, falling_node_name);
end
if (stable_node_name) then
default.firmness_node_to_stable[node_name] = stable_node_name;
default.firmness_node_from_stable[stable_node_name] = node_name;
table.insert(default.firmness_node_stable_names, stable_node_name);
end
end
function default.firmness_abm_action(pos, node)
local fall_it = default.is_should_fall(pos, node, false, true);
--minetest.log("warning", "Node on pos "..dump(pos).." fall it is "..tostring(fall_it));
if (fall_it==true) then
default.fall_stable_node(pos, node, true);
--default.firmness_after_destruct(pos, node);
--minetest.after(1, default.check_neighbour_for_fall, pos);
end
end
function default.firmness_after_destruct(pos, oldnode)
minetest.log("warning", "after_destruct");
default.neighbour_stable_to_normal(pos);
--default.check_neighbour_for_fall(pos);
minetest.after(1, default.check_neighbour_for_fall, pos);
end
function default.firmness_preserve_metadata(pos, oldnode, oldmeta, drops)
minetest.log("warning", "preverse_metadata");
default.neighbour_stable_to_normal(pos);
--default.check_neighbour_for_fall(pos);
minetest.after(1, default.check_neighbour_for_fall, pos);
end
-- settings should include firmness, resilience, names
function default.register_node_with_firmness(node_def, settings)
if (setting.normal_node) then
normal_def = table.copy(node_def);
normal_def.groups.firmness = setting.firmness;
normal_def.groups.resilience = setting.resilience;
normal_def.after_destruct = default.firmness_after_destruct;
normal_def.preserve_metadata = default.firmness_preserve_metadata;
minetest.register_node(setting.normal_node, normal_def);
end
if (setting.stable_node) then
stable_def = table.copy(node_def);
if not(stable_def.drops) then
stable_def.drops = setting.normal_node;
end
stable_def.groups.not_in_creative_inventory = 1;
stable_def.after_destruct = default.firmness_after_destruct;
minetest.register_node(setting.stable_node, stable_def);
default.register_firmness_node_change(setting.normal_node, nil, setting.stable_node);
end
if (setting.falling_node) then
falling_def = table.copy(node_def);
if not(falling_def.drops) then
falling_def.drops = setting.normal_node;
end
falling_def.groups.falling_node = 1;
falling_def.groups.not_in_creative_inventory = 1;
falling_def.after_destruct = default.firmness_after_destruct,
minetest.register_node(setting.falling_node, stable_def);
default.register_firmness_node_change(setting.normal_node, setting.falling_node, nil);
end
end