hell/portal.lua

562 lines
17 KiB
Lua
Raw Normal View History

2020-03-06 11:52:34 +01:00
--code copied from Pilzadam's nether mod and edited
local S = hell.get_translator
-- kills the player if he uses PilzAdam portal
local portal_target = hell.buildings+1
local hell_prisons = minetest.settings:get_bool("enable_damage")
local obsidian_portal_kills = hell_prisons and true
local mclike_portal = false
local save_path = minetest.get_worldpath() .. "/hell_players"
local players_in_hell = {}
-- only get info from file if hell prisons
if hell_prisons then
local file = io.open(save_path, "r")
if file then
local contents = file:read("*all")
io.close(file)
if contents then
local playernames = string.split(contents, " ")
for i = 1,#playernames do
players_in_hell[playernames[i]] = true
end
end
end
end
local function save_hell_players()
local playernames,n = {},1
for name in pairs(players_in_hell) do
playernames[n] = name
n = n+1
end
local f = io.open(save_path, "w")
assert(f, "Could not open hell_players file for writing.")
f:write(table.concat(playernames, " "))
io.close(f)
end
local update_background
if hell_prisons then
function update_background(player, down)
if down then
player:set_sky({r=15, g=0, b=0}, "plain")
else
player:set_sky(nil, "regular")
end
end
else
function update_background() end
end
-- returns nodename if area is generated, else calls generation function
local function generated_or_generate(pos)
local node = minetest.get_node_or_nil(pos)
if node then
return node.name
end
minetest.get_voxel_manip():read_from_map(pos, pos)
node = minetest.get_node_or_nil(pos)
if not node then
minetest.emerge_area(vector.subtract(pos, 80), vector.add(pos, 80))
return false
end
return node.name
end
-- where the player appears after dying
local function get_player_died_target(player)
local target = vector.add(player:get_pos(),
{x=math.random(-100,100), y=0, z=math.random(-100,100)})
target.y = portal_target + math.random(4)
return target
end
-- used for obsidian portal
local function obsidian_teleport(player, pname, target)
minetest.chat_send_player(pname, S("For any reason you arrived here. Type /hell_help to find out things like craft recipes."))
players_in_hell[pname] = true
save_hell_players()
update_background(player, true)
if target then
player:set_pos(target)
else
player:set_hp(0)
end
end
-- teleports players to hell or helps it
local function player_to_hell(player, pos)
local pname = player:get_player_name()
if players_in_hell[pname] then
return
end
players_in_hell[pname] = true
save_hell_players()
update_background(player, true)
if pos then
player:set_pos(pos)
return
end
minetest.chat_send_player(pname, S("For any reason you arrived here. Type /hell_help to find out things like craft recipes."))
player:set_hp(0)
if hell_prisons then
player:set_pos(get_player_died_target(player))
end
end
local function player_from_hell(player, pos)
local pname = player:get_player_name()
if players_in_hell[pname] then
players_in_hell[pname] = nil
save_hell_players()
end
update_background(player)
player:set_pos(pos)
end
local function is_player_connected(name)
for _,player in pairs(minetest.get_connected_players()) do
if player:get_player_name() == name then
return true
end
end
return false
end
-- Chatcommands (edited) written by sss
minetest.register_chatcommand("to_hell", {
params = "[player_name]",
description = "Send someone to hell",
func = function(name, pname)
if not minetest.check_player_privs(name, {hell=true}) then
return false,
"You need the hell privilege to execute this chatcommand."
end
if not is_player_connected(pname) then
pname = name
end
local player = minetest.get_player_by_name(pname)
if not player then
return false, "Something went wrong."
end
minetest.chat_send_player(pname, "Go to hell !!!")
player_to_hell(player)
return true, pname.." is now in the hell."
end
})
minetest.register_chatcommand("from_hell", {
params = "[player_name]",
description = "Extract from hell",
func = function(name, pname)
if not minetest.check_player_privs(name, {hell=true}) then
return false,
"You need the hell priv to execute this chatcommand."
end
if not is_player_connected(pname) then
pname = name
end
local player = minetest.get_player_by_name(pname)
if not player then
return false, "Something went wrong."
end
minetest.chat_send_player(pname, "You are free now")
local pos = player:getpos()
player_from_hell(player, {x=pos.x, y=100, z=pos.z})
return true, pname.." is now out of the hell."
end
})
if hell_prisons then
-- randomly set player position when he/she dies in hell
minetest.register_on_respawnplayer(function(player)
local pname = player:get_player_name()
if not players_in_hell[pname] then
return
end
local target = get_player_died_target(player)
player:set_pos(target)
minetest.after(0, function(pname, target)
-- fixes respawn bug
local player = minetest.get_player_by_name(pname)
if player then
player:moveto(target)
end
end, pname, target)
return true
end)
-- override set_pos etc. to disallow player teleportion by e.g. travelnet
local function can_teleport(player, pos)
if not player:is_player() then
-- the same metatable is used for entities
return true
end
local pname = player:get_player_name()
local in_hell = players_in_hell[pname]
-- test if the target is valid
if pos.y < hell.start and pos.y > hell.DEPTH-510 then
if in_hell then
return true
end
elseif not in_hell then
return true
end
-- test if the current position is valid
local current_pos = player:get_pos()
local now_in_hell = current_pos.y < hell.start and current_pos.y > hell.DEPTH-510
if now_in_hell ~= in_hell then
if in_hell then
minetest.log("action", "Player \"" .. pname ..
"\" has to be in the hell, teleporting it!")
update_background(player, true)
current_pos.y = portal_target
player:set_pos(current_pos)
else
minetest.log("action", "Player \"" .. pname ..
"\" must not be in the hell, teleporting it!")
update_background(player)
current_pos.y = 20
player:set_pos(current_pos)
end
return false
end
minetest.chat_send_player(pname,
"You can not simply teleport to or from the hell!")
minetest.log("action", "Player \"" .. pname ..
"\" attempted to teleport from or to the hell, ignoring.")
return false
end
local methods = {"set_pos", "move_to", "setpos", "moveto"}
local metatable_overridden
minetest.register_on_joinplayer(function(player)
-- set the background when the player joins
local y = player:get_pos().y
if y < hell.start and y > hell.DEPTH-510 then
update_background(player, true)
end
-- overide set_pos etc. if not yet done
if metatable_overridden then
return
end
metatable_overridden = true
local mt = getmetatable(player)
for i = 1,#methods do
local methodname = methods[i]
local origfunc = mt[methodname]
mt[methodname] = function(...)
if can_teleport(...) then
origfunc(...)
end
end
end
end)
else
-- test if player is in hell when he/she joins
minetest.register_on_joinplayer(function(player)
local y = player:get_pos().y
players_in_hell[player:get_player_name()] =
y < hell.start and y > hell.DEPTH-510 or nil
end)
end
-- teleports player to hell (obsidian portal)
local function obsi_teleport_player(player, pos, target)
local pname = player:get_player_name()
if players_in_hell[pname] then
return
end
local objpos = player:get_pos()
objpos.y = objpos.y+0.1 -- Fix some glitches at -8000
if minetest.get_node(vector.round(objpos)).name ~= "hell:portal" then
return
end
local has_teleported
if obsidian_portal_kills then
obsidian_teleport(player, pname)
has_teleported = true
elseif not mclike_portal then
local target = vector.round(get_player_died_target(player))
if generated_or_generate(target) then
obsidian_teleport(player, pname, target)
has_teleported = true
end
end
if not has_teleported then
-- e.g. ungenerated area
return
end
minetest.sound_play("hell_portal_usual", {to_player=pname, gain=1})
end
-- a not filled square
vector.square = vector.square or
function(r)
local tab, n = {}, 1
for i = -r+1, r do
for j = -1, 1, 2 do
local a, b = r*j, i*j
tab[n] = {a, b}
tab[n+1] = {b, a}
n=n+2
end
end
return tab
end
local function is_hellportal(pos)
local x, y, z = pos.x, pos.y, pos.z
for _,i in pairs({-1, 3}) do
if minetest.get_node({x=x, y=y+i, z=z}).name ~= "hell:white" then
return
end
end
for _,sn in pairs(vector.square(1)) do
if minetest.get_node({x=x+sn[1], y=y-1, z=z+sn[2]}).name ~= "hell:hellrack"
or minetest.get_node({x=x+sn[1], y=y+3, z=z+sn[2]}).name ~= "hell:blood_cooked" then
return
end
end
for _,sn in pairs(vector.square(2)) do
if minetest.get_node({x=x+sn[1], y=y-1, z=z+sn[2]}).name ~= "hell:hellrack_black"
or minetest.get_node({x=x+sn[1], y=y+3, z=z+sn[2]}).name ~= "hell:wood_empty" then
return
end
end
for i = -1,1,2 do
for j = -1,1,2 do
if minetest.get_node({x=x+i, y=y+2, z=z+j}).name ~= "hell:apple" then
return
end
end
end
for i = -2,2,4 do
for j = 0,2 do
for k = -2,2,4 do
if minetest.get_node({x=x+i, y=y+j, z=z+k}).name ~= "hell:hellrack_brick_blue" then
return
end
end
end
end
for i = -1,1 do
for j = -1,1 do
if minetest.get_node({x=x+i, y=y+4, z=z+j}).name ~= "hell:wood_empty" then
return
end
end
end
return true
end
-- cache known portals
local known_portals_d = {}
local known_portals_u = {}
local function get_portal(t, z,x)
return t[z] and t[z][x]
end
local function set_portal(t, z,x, y)
t[z] = t[z] or {}
t[z][x] = y
end
-- used when a player eats hell:apple in a portal
local function hell_port(player, pos)
if not player
or not pos
or not pos.x then
minetest.log("error", "[hell] hell_port: something failed.")
return
end
if not is_hellportal(pos) then
return
end
minetest.sound_play("hell_teleporter", {pos=pos})
local meta = minetest.get_meta({x=pos.x, y=pos.y-1, z=pos.z})
if pos.y < hell.start then
set_portal(known_portals_d, pos.z,pos.x, pos.y)
local my = tonumber(meta:get_string("y"))
local y = get_portal(known_portals_u, pos.z,pos.x)
if y then
if y ~= my then
meta:set_string("y", y)
end
else
y = my or 100
end
pos.y = y - 0.3
player_from_hell(player, pos)
else
set_portal(known_portals_u, pos.z,pos.x, pos.y)
local my = tonumber(meta:get_string("y"))
local y = get_portal(known_portals_d, pos.z,pos.x)
if y then
if y ~= my then
meta:set_string("y", y)
end
else
y = my or portal_target+math.random(4)
end
pos.y = y - 0.3
player_to_hell(player, pos)
end
minetest.sound_play("hell_teleporter", {pos=pos})
return true
end
minetest.register_on_item_eat(function(_, _, itemstack, user, _)
if not user or itemstack:is_empty() then
return
end
local inv = user:get_inventory()
if not inv then
return
end
if itemstack:get_name() == "hell:apple" then
itemstack:take_item()
if hell_port(user, vector.round(user:get_pos())) then
return itemstack
end
end
end)
if hell.HELL_REALM_ENABLED then
-- Use the Portal API to add a portal type which goes to the Hell
-- See portal_api.txt for documentation
nether.register_portal("hell_gate", {
shape = nether.PortalShape_Circular,
frame_node_name = "nether:brick",
wormhole_node_color = 5, -- 5 is red
title = S("Hell Gate"),
particle_texture = {
name = "nether_particle_anim2.png",
animation = {
type = "vertical_frames",
aspect_w = 7,
aspect_h = 7,
length = 1,
},
scale = 1.5
},
book_of_portals_pagetext = S("Construction requires 16 blocks of ??. The finished frame must be in the shape of a circle and laid vertically, like a door.").."\n\n"..S("However, I never knew what material to use for building this door despite my many attempts.").."\n\n"..S("One day, however, I met an old lady who frankly looked like a witch. In the course of our discussion we came to talk about the Hell Gate. This old lady confessed to me that in her family there was a legend that one of her ancestors had managed to activate the portal. But that the poor man was instantly killed as he passed through it. The gate was destroyed and several years passed. But since a certain night, the poor widow of the deceased began to rave that her Husband had come back from the dead to take her also to hell because he said that he had made this world a wonderful place and that after several years, he would have found a way to come back and return to it as he wished without causing death. A few days later, the widow disappeared without a trace. Since then the house was destroyed in a fire with all the secrets it could hold. Only an old cauldron remained amidst the ashes.").."\n\n"..S("When I wanted to ask the old lady with what material the gate was made, she stared at me with eyes so black that I had the impression that the Nether wanted to take over me, and I did not insist any further."),
is_within_realm = function(pos) -- return true if pos is inside the Hell
return pos.y < hell.DEPTH and pos.y > hell.DEPTH-510
end,
find_realm_anchorPos = function(surface_anchorPos)
-- divide x and z by a factor of 8 to implement Hell fast-travel
local destination_pos = vector.divide(surface_anchorPos, hell.FASTTRAVEL_FACTOR)
destination_pos.x = math.floor(0.5 + destination_pos.x) -- round to int
destination_pos.z = math.floor(0.5 + destination_pos.z) -- round to int
destination_pos.y = hell.DEPTH-390 -- temp value so find_nearest_working_portal() returns hell portals
-- a y_factor of 0 makes the search ignore the altitude of the portals (as long as they are in the Hell)
local existing_portal_location, existing_portal_orientation =
nether.find_nearest_working_portal("hell_gate", destination_pos, 8, 0)
if existing_portal_location ~= nil then
return existing_portal_location, existing_portal_orientation
else
destination_pos.y = hell.DEPTH-390 + math.random(0, 30)
return destination_pos
end
end,
find_surface_anchorPos = function(realm_anchorPos)
-- A portal definition doesn't normally need to provide a find_surface_anchorPos() function,
-- since find_surface_target_y() will be used by default, but Hell portals also scale position
-- to create fast-travel.
-- Defining a custom function also means we can look for existing nearby portals.
-- Multiply x and z by a factor of 8 to implement Hell fast-travel
local destination_pos = vector.multiply(realm_anchorPos, hell.FASTTRAVEL_FACTOR)
destination_pos.x = math.min(30900, math.max(-30900, destination_pos.x)) -- clip to world boundary
destination_pos.z = math.min(30900, math.max(-30900, destination_pos.z)) -- clip to world boundary
destination_pos.y = 0 -- temp value so find_nearest_working_portal() doesn't return hell portals
-- a y_factor of 0 makes the search ignore the altitude of the portals (as long as they are outside the Hell)
local existing_portal_location, existing_portal_orientation =
nether.find_nearest_working_portal("hell_gate", destination_pos, 8 * hell.FASTTRAVEL_FACTOR, 0)
if existing_portal_location ~= nil then
return existing_portal_location, existing_portal_orientation
else
destination_pos.y = nether.find_surface_target_y(destination_pos.x, destination_pos.z, "hell_gate")
return destination_pos
end
end,
on_ignite = function(portalDef, anchorPos, orientation)
-- make some sparks fly
local p1, p2 = portalDef.shape:get_p1_and_p2_from_anchorPos(anchorPos, orientation)
local pos = vector.divide(vector.add(p1, p2), 2)
local textureName = portalDef.particle_texture
if type(textureName) == "table" then textureName = textureName.name end
minetest.add_particlespawner({
amount = 110,
time = 0.1,
minpos = {x = pos.x - 0.5, y = pos.y - 1.2, z = pos.z - 0.5},
maxpos = {x = pos.x + 0.5, y = pos.y + 1.2, z = pos.z + 0.5},
minvel = {x = -5, y = -1, z = -5},
maxvel = {x = 5, y = 1, z = 5},
minacc = {x = 0, y = 0, z = 0},
maxacc = {x = 0, y = 0, z = 0},
minexptime = 0.1,
maxexptime = 0.5,
minsize = 0.2 * portalDef.particle_texture_scale,
maxsize = 0.8 * portalDef.particle_texture_scale,
collisiondetection = false,
texture = textureName .. "^[colorize:#F00:alpha",
animation = portalDef.particle_texture_animation,
glow = 8
})
end,
on_player_teleported = function(portal_definition, player, playerPos, new_playerPos)
local pname = player:get_player_name()
if players_in_hell[pname] then
return
end
obsidian_teleport(player, pname)
end,
})
end
-- Pour éviter de pouvoir aller en enfer depuis le nether avec un portail du nether construit depuis l'enfer
if nether.NETHER_REALM_ENABLED then
nether.registered_portals["nether_portal"].on_player_teleported =
function(portal_definition, player, playerPos, new_playerPos)
local new_y = new_playerPos.y
if new_y > hell.DEPTH-510 and new_y < hell.DEPTH then
minetest.after(
0,function(player, target)
-- fixes respawn bug
if player then
player:moveto(target)
end
end, player, playerPos)
minetest.chat_send_player(player:get_player_name(),
S("You shall not pass!"))
end
end
end