562 lines
17 KiB
Lua
562 lines
17 KiB
Lua
--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
|