366 lines
12 KiB
Lua
Executable File
366 lines
12 KiB
Lua
Executable File
cursed_world = {}
|
|
cursed_world.portals = {}
|
|
cursed_world.location_y = -4800;
|
|
cursed_world.dimension_y = 250; --"thikness" of cursed_world
|
|
cursed_world.lastplayername =""
|
|
cursed_world.filename = minetest.get_worldpath() .. "/cursed_world_portals.txt"
|
|
|
|
function cursed_world:save()
|
|
local datastring = minetest.serialize(self.portals)
|
|
if not datastring then
|
|
return
|
|
end
|
|
local file, err = io.open(self.filename, "w")
|
|
if err then
|
|
return
|
|
end
|
|
file:write(datastring)
|
|
file:close()
|
|
end
|
|
function cursed_world:load()
|
|
local file, err = io.open(self.filename, "r")
|
|
if err then
|
|
self.portals = {}
|
|
return
|
|
end
|
|
self.portals = minetest.deserialize(file:read("*all"))
|
|
if type(self.portals) ~= "table" then
|
|
self.portals = {}
|
|
end
|
|
file:close()
|
|
end
|
|
|
|
function cursed_world.animate1(pos)
|
|
local xd = 1;
|
|
if pos.x%1 == 0 then
|
|
xd = 0.5;
|
|
end
|
|
local zd = 1;
|
|
if pos.z%1 == 0 then
|
|
zd = 0.5;
|
|
end
|
|
minetest.add_particlespawner({
|
|
amount = 80,
|
|
time = 4,
|
|
minpos = {x=pos.x-xd, y=pos.y-1.5, z=pos.z-zd},
|
|
maxpos = {x=pos.x+xd, y=pos.y+1.5, z=pos.z+zd},
|
|
minvel = {x=0, y=-1, z=0},
|
|
maxvel = {x=0, y=1, z=0},
|
|
minacc = {x=0, y=-1, z=0},
|
|
maxacc = {x=0, y=1, z=0},
|
|
minexptime = 1,
|
|
maxexptime = 1,
|
|
minsize = 0.5,
|
|
maxsize = 2,
|
|
collisiondetection = false,
|
|
vertical = true,
|
|
texture = "default_coal_lump.png",
|
|
})
|
|
end
|
|
function cursed_world.animate2(pos)
|
|
local xd = 0.5;
|
|
if pos.x%1 == 0 then
|
|
xd = 0.25;
|
|
end
|
|
local zd = 0.5;
|
|
if pos.z%1 == 0 then
|
|
zd = 0.25;
|
|
end
|
|
minetest.add_particlespawner({
|
|
amount = 80,
|
|
time = 10,
|
|
minpos = {x=pos.x-xd, y=pos.y-0.5, z=pos.z-zd},
|
|
maxpos = {x=pos.x+xd, y=pos.y+0.5, z=pos.z+zd},
|
|
minvel = {x=-1, y=-1, z=-1},
|
|
maxvel = {x=1, y=1, z=1},
|
|
minacc = {x=-1, y=-1, z=-1},
|
|
maxacc = {x=1, y=1, z=1},
|
|
minexptime = 1,
|
|
maxexptime = 1,
|
|
minsize = 0.5,
|
|
maxsize = 2,
|
|
collisiondetection = false,
|
|
texture = "default_mese_crystal.png",
|
|
})
|
|
end
|
|
|
|
function cursed_world.teleportate(parameters)
|
|
local pos1,pos2,playername,portal_id = parameters[1],parameters[2],parameters[3],parameters[4]
|
|
local player = minetest.get_player_by_name(playername)
|
|
if player and player:is_player() and playername~=cursed_world.lastplayername then
|
|
local pos = player:get_pos()
|
|
if vector.distance(pos, {x=pos1.x,y=pos1.y-1,z=pos1.z}) < 1.7 then
|
|
if math.random(1, 100) > 5 then
|
|
cursed_world.lastplayername = playername
|
|
player:set_pos({x=pos2.x,y=pos2.y+0.5,z=pos2.z})
|
|
else
|
|
player:set_pos({x=pos2.x-5+math.random(1, 10),y=pos2.y+3,z=pos2.z-5+math.random(1, 10)})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
function cursed_world.start_teleporting(pos1, pos2, playername, portal_id)
|
|
cursed_world.animate2(pos1, playername)
|
|
minetest.after(3.0, cursed_world.teleportate, {pos1, pos2, playername, portal_id})
|
|
end
|
|
|
|
function cursed_world.shatter_portal(id)
|
|
if cursed_world.portals[id] then
|
|
local pos = cursed_world.portals[id].pos;
|
|
--blast would look good here... TODO
|
|
blocksfound = minetest.find_nodes_in_area(
|
|
{x=pos.x-1.5, y=pos.y-3, z=pos.z-1.5},
|
|
{x=pos.x+1.5, y=pos.y+3, z=pos.z+1.5},
|
|
{"cursed_world:cursed_stone", "default:obsidian"});
|
|
local num = #blocksfound;
|
|
if num > 0 then
|
|
local del = math.random(1, #blocksfound);
|
|
local delpos = blocksfound[del]
|
|
if fire then
|
|
minetest.set_node(delpos, {name="fire:basic_flame"})
|
|
else
|
|
minetest.remove_node(delpos);
|
|
end
|
|
end
|
|
if num < 12 then
|
|
table.remove(cursed_world.portals, id)
|
|
cursed_world:save();
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
cursed_world:load()
|
|
|
|
|
|
cursed_world.portals_working = function()
|
|
for id, portal in ipairs(cursed_world.portals) do
|
|
local pos = portal.pos;
|
|
local pos_target = portal.target;
|
|
cursed_world.animate1(pos);
|
|
|
|
local objectsnear=minetest.get_objects_inside_radius({x=pos.x,y=pos.y-1,z=pos.z}, 1.7);
|
|
if #objectsnear>0 then
|
|
local player = objectsnear[1];
|
|
-- check only first two objekts then give up
|
|
if #objectsnear>1 and not player:is_player() then
|
|
player = objectsnear[2];
|
|
end
|
|
if player:is_player() and player:get_player_name()~=cursed_world.lastplayername then
|
|
cursed_world.start_teleporting(pos, pos_target, player:get_player_name(), id)
|
|
if player:get_player_name() ~= portal.owner then
|
|
cursed_world.shatter_portal(id)
|
|
end
|
|
else
|
|
cursed_world.lastplayername = ""
|
|
end
|
|
end
|
|
end
|
|
|
|
--recursion to itself
|
|
minetest.after(4, function()
|
|
cursed_world.portals_working()
|
|
end)
|
|
end
|
|
|
|
minetest.after(10, function()
|
|
cursed_world.portals_working()
|
|
end)
|
|
|
|
--determine portal position and rediness
|
|
cursed_world.on_place = function(itemstack, placer, pointed_thing)
|
|
if not placer:is_player() then
|
|
return minetest.item_place(itemstack, placer, pointed_thing)
|
|
end
|
|
|
|
local pos = pointed_thing.above
|
|
|
|
for _, portal in ipairs(cursed_world.portals) do
|
|
local portal_pos = portal.pos;
|
|
if vector.distance(pos, portal_pos) < 4 then
|
|
return minetest.item_place(itemstack, placer, pointed_thing);
|
|
end
|
|
end
|
|
|
|
|
|
local center_x = nil;
|
|
local center_y = nil;
|
|
local center_z = nil;
|
|
local portal_ready = false;
|
|
local positions = {
|
|
{x=pos.x-3, y=pos.y, z=pos.z},
|
|
{x=pos.x+3, y=pos.y, z=pos.z},
|
|
{x=pos.x, y=pos.y, z=pos.z-3},
|
|
{x=pos.x, y=pos.y, z=pos.z+3},
|
|
{x=pos.x-1, y=pos.y, z=pos.z},
|
|
{x=pos.x+1, y=pos.y, z=pos.z},
|
|
{x=pos.x, y=pos.y, z=pos.z-1},
|
|
{x=pos.x, y=pos.y, z=pos.z+1},
|
|
};
|
|
local pos_b = nil;
|
|
for _, p in ipairs(positions) do
|
|
if minetest.get_node(p).name == itemstack:get_name() then
|
|
pos_b = p;
|
|
end
|
|
end
|
|
|
|
if pos_b then
|
|
if pos.x == pos_b.x then
|
|
center_x = pos.x;
|
|
center_z = pos.z + (pos_b.z - pos.z)/2;
|
|
local n = 0;
|
|
local m = 4;
|
|
local blocksfound = {};
|
|
while n < 5 and #blocksfound < 12
|
|
do
|
|
blocksfound = minetest.find_nodes_in_area(
|
|
{x=center_x, y=pos.y-m, z=center_z-1.5},
|
|
{x=center_x, y=pos.y+n, z=center_z+1.5},
|
|
{"cursed_world:cursed_stone", "default:obsidian"});
|
|
n = n + 1;
|
|
m = m - 1;
|
|
end
|
|
if #blocksfound >= 12 then
|
|
center_y = pos.y + n - 2;
|
|
portal_ready = true;
|
|
end
|
|
else
|
|
center_x = pos.x + (pos_b.x - pos.x)/2;
|
|
center_z = pos.z;
|
|
local n = 0;
|
|
local m = 4;
|
|
local blocksfound = {};
|
|
while n < 5 and #blocksfound < 11
|
|
do
|
|
blocksfound = minetest.find_nodes_in_area(
|
|
{x=center_x-1.5, y=pos.y-m, z=center_z},
|
|
{x=center_x+1.5, y=pos.y+n, z=center_z},
|
|
{"cursed_world:cursed_stone", "default:obsidian"});
|
|
n = n + 1;
|
|
m = m - 1;
|
|
end
|
|
if #blocksfound >= 11 then
|
|
center_y = pos.y + n - 3;
|
|
portal_ready = true;
|
|
end
|
|
end
|
|
end
|
|
if portal_ready then
|
|
cursed_world.animate1({x=center_x, y=center_y, z=center_z});
|
|
local owner = placer:get_player_name();
|
|
cursed_world.slovly_search_target_location({x=center_x, y=center_y, z=center_z}, owner);
|
|
|
|
minetest.chat_send_player(placer:get_player_name(), "Portal is complete and charging...");
|
|
end
|
|
return minetest.item_place(itemstack, placer, pointed_thing)
|
|
end
|
|
|
|
--Forceload far blocks, wait, then check content and unload.
|
|
--recursion
|
|
cursed_world.search_better_place_after_forceload = function(parameters)
|
|
local pos, owner, pos_target, test_pos, n = parameters[1], parameters[2], parameters[3], parameters[4], parameters[5];
|
|
|
|
local minp = vector.multiply(vector.round(vector.divide(test_pos, 16)), 16);
|
|
local maxp = {x=minp.x+16, y=minp.y+16, z=minp.z+16}
|
|
|
|
local some_name = minetest.get_node(test_pos).name;
|
|
local good_places = minetest.find_nodes_in_area(minp, maxp, {"air"});
|
|
--minetest.chat_send_all("X"..n)
|
|
--try read again, i got impression, that this may help.
|
|
if some_name == "undefined" and #good_places == 0 and math.random(1, 100) > 33 then
|
|
--i suspecting, that this code never executes
|
|
minetest.after(1.0, cursed_world.search_better_place_after_forceload, {pos, owner, pos_target, test_pos, n});
|
|
return
|
|
end
|
|
--minetest.chat_send_all("Search..."..n..':'..minetest.get_node(test_pos).name..'?'..#good_places)
|
|
minetest.forceload_free_block(test_pos, true);
|
|
|
|
if #good_places > 32 and #good_places < 3500 then
|
|
pos_target = good_places[16];
|
|
|
|
table.insert(cursed_world.portals, {
|
|
pos=pos, owner=owner, target=pos_target,
|
|
});
|
|
cursed_world:save();
|
|
minetest.chat_send_player(owner, "Portal is finished!");
|
|
else
|
|
if n < 10 then
|
|
n = n + 1;
|
|
local test_pos = {
|
|
x=pos_target.x + math.random(-128, 128),
|
|
y=pos_target.y + math.random(-32, 32),
|
|
z=pos_target.z + math.random(-128, 128)};
|
|
if minetest.forceload_block(test_pos, true) then
|
|
minetest.after(2.0, cursed_world.search_better_place_after_forceload, {pos, owner, pos_target, test_pos, n});
|
|
else
|
|
minetest.chat_send_player(owner, "Portal failed!");
|
|
end
|
|
else
|
|
table.insert(cursed_world.portals, {
|
|
pos=pos, owner=owner, target=pos_target,
|
|
});
|
|
cursed_world:save();
|
|
minetest.chat_send_player(owner, "Portal is ready!");
|
|
end
|
|
end
|
|
end
|
|
--start recursion from here
|
|
cursed_world.slovly_search_target_location = function(pos, owner)
|
|
local pos_target = {x=pos.x, y=cursed_world.location_y, z=pos.z};
|
|
--if already in cursed_world, then make portal to surface
|
|
if math.abs(pos.y - cursed_world.location_y) < cursed_world.dimension_y then
|
|
pos_target.y = pos.y - cursed_world.location_y;
|
|
end
|
|
|
|
local n = 0;
|
|
local test_pos = {
|
|
x=pos_target.x + math.random(-128, 128),
|
|
y=pos_target.y + math.random(-32, 32),
|
|
z=pos_target.z + math.random(-128, 128)};
|
|
--forceload blocks and check nodes after some delay
|
|
if minetest.forceload_block(test_pos, true) then
|
|
minetest.after(5.0, cursed_world.search_better_place_after_forceload, {pos, owner, pos_target, test_pos, n});
|
|
end
|
|
end
|
|
|
|
minetest.register_node("cursed_world:cursed_stone", {
|
|
description = "Cursed stone",
|
|
tiles = {
|
|
"cursed_stone_top.png",
|
|
"cursed_stone_bottom.png",
|
|
"cursed_stone.png",
|
|
"cursed_stone.png",
|
|
"cursed_stone.png",
|
|
"cursed_stone.png"
|
|
},
|
|
is_ground_content = false,
|
|
groups = {cracky=1, level=2},
|
|
drop = 'default:goldblock',
|
|
sounds = default.node_sound_stone_defaults(),
|
|
on_place = cursed_world.on_place
|
|
})
|
|
|
|
|
|
minetest.register_craft({
|
|
output = 'cursed_world:cursed_stone',
|
|
recipe = {
|
|
{'default:obsidian', 'default:obsidian', 'default:obsidian'},
|
|
{'default:obsidian', 'default:goldblock', 'default:obsidian'},
|
|
{'default:obsidian', 'default:obsidian', 'default:obsidian'},
|
|
}
|
|
})
|
|
|
|
--depending on what "mobs" mod version is used, mob name will be different
|
|
if _G['mobs'] then --check global table for mobs mod
|
|
mobs:register_spawn("mobs:oerkki", "cursed_world:cursed_stone", 4, -1, 2, 12);
|
|
end
|
|
|
|
--convert all old nodes
|
|
minetest.register_lbm({
|
|
name = "cursed_world:convert_cursed_stone",
|
|
nodenames = {"mobs:cursed_stone"},
|
|
action = function(pos, node)
|
|
minetest.set_node(pos, {name = "cursed_world:cursed_stone"})
|
|
end,
|
|
})
|