123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- obsidian_gateway = obsidian_gateway or {}
- obsidian_gateway.modpath = minetest.get_modpath("obsidian_gateway")
- -- Localize for performance.
- local vector_distance = vector.distance
- local vector_round = vector.round
- local math_random = math.random
- -- Gateway schematic.
- local o = {"default:obsidian", "griefer:grieferstone", "cavestuff:dark_obsidian", "cavestuff:glow_obsidian"}
- local a = function(name)
- -- Bones check needed here to prevent players from blocking a gate by dying in
- -- it. E.g., bones never decay while a hacker is online.
- if name == "air" or name =="bones:bones" then
- return true
- end
- end
- local gate_northsouth = {
- data = {
- {p={x=0, y=0, z=0}, n=o},
- {p={x=1, y=0, z=0}, n=o},
- {p={x=2, y=0, z=0}, n=o},
- {p={x=3, y=0, z=0}, n=o},
- {p={x=0, y=1, z=0}, n=o},
- {p={x=0, y=2, z=0}, n=o},
- {p={x=0, y=3, z=0}, n=o},
- {p={x=0, y=4, z=0}, n=o},
- {p={x=1, y=4, z=0}, n=o},
- {p={x=2, y=4, z=0}, n=o},
- {p={x=3, y=4, z=0}, n=o},
- {p={x=3, y=1, z=0}, n=o},
- {p={x=3, y=2, z=0}, n=o},
- {p={x=3, y=3, z=0}, n=o},
- {p={x=1, y=1, z=0}, n=a},
- {p={x=2, y=1, z=0}, n=a},
- {p={x=1, y=2, z=0}, n=a},
- {p={x=2, y=2, z=0}, n=a},
- {p={x=1, y=3, z=0}, n=a},
- {p={x=2, y=3, z=0}, n=a},
- },
- minp = {x=0, y=0, z=0},
- maxp = {x=3, y=4, z=0},
- }
- local gate_eastwest = {
- data = {
- {p={x=0, y=0, z=0}, n=o},
- {p={x=0, y=0, z=1}, n=o},
- {p={x=0, y=0, z=2}, n=o},
- {p={x=0, y=0, z=3}, n=o},
- {p={x=0, y=1, z=0}, n=o},
- {p={x=0, y=2, z=0}, n=o},
- {p={x=0, y=3, z=0}, n=o},
- {p={x=0, y=4, z=0}, n=o},
- {p={x=0, y=4, z=1}, n=o},
- {p={x=0, y=4, z=2}, n=o},
- {p={x=0, y=4, z=3}, n=o},
- {p={x=0, y=1, z=3}, n=o},
- {p={x=0, y=2, z=3}, n=o},
- {p={x=0, y=3, z=3}, n=o},
- {p={x=0, y=1, z=1}, n=a},
- {p={x=0, y=1, z=2}, n=a},
- {p={x=0, y=2, z=1}, n=a},
- {p={x=0, y=2, z=2}, n=a},
- {p={x=0, y=3, z=1}, n=a},
- {p={x=0, y=3, z=2}, n=a},
- },
- minp = {x=0, y=0, z=0},
- maxp = {x=0, y=4, z=3},
- }
- obsidian_gateway.gate_ns_data = gate_northsouth
- obsidian_gateway.gate_ew_data = gate_eastwest
- -- Quickly check for protection in an area.
- local function check_protection(pos, radius)
- -- How much beyond the radius to check for protections.
- local e = 3
- local minp = vector.new(pos.x-(radius+e), pos.y-(radius+e), pos.z-(radius+e))
- local maxp = vector.new(pos.x+(radius+e), pos.y+(radius+e), pos.z+(radius+e))
- -- Step size, to avoid checking every single node.
- -- This assumes protections cannot be smaller than this size.
- local ss = 3
- local check = minetest.test_protection
- for x=minp.x, maxp.x, ss do
- for y=minp.y, maxp.y, ss do
- for z=minp.z, maxp.z, ss do
- if check({x=x, y=y, z=z}, "") then
- -- Protections are present.
- return true
- end
- end
- end
- end
- -- Nothing in the area is protected.
- return false
- end
- function obsidian_gateway.find_gate(pos)
- local result
- local points
- local counts
- local origin
- local northsouth
- local ns_key
- local playerorigin
- -- Find the gateway (threshold under player)!
- result, points, counts, origin =
- schematic_find.detect_schematic(pos, gate_northsouth)
- northsouth = true
- ns_key = "ns"
- if result then
- playerorigin = vector.add(origin, {x=1, y=1, z=0})
- end
- if not result then
- -- Couldn't find northsouth gateway, so try to find eastwest.
- result, points, counts, origin =
- schematic_find.detect_schematic(pos, gate_eastwest)
- northsouth = false
- ns_key = "ew"
- if result then
- playerorigin = vector.add(origin, {x=0, y=1, z=1})
- end
- end
- -- Debugging.
- if not result then
- --minetest.chat_send_player(pname, "# Server: Bad gateway.")
- return
- end
- -- Store locations of air inside the portal gateway.
- local airpoints = {}
- if result then
- for k, v in ipairs(points) do
- if minetest.get_node(v).name == "air" then
- airpoints[#airpoints+1] = v
- end
- end
- end
- -- Did we find a working gateway?
- local yes = false
- if result then
- local o = counts["default:obsidian"] or 0
- local d = counts["cavestuff:dark_obsidian"] or 0
- local c = counts["cavestuff:glow_obsidian"] or 0
- local g = counts["griefer:grieferstone"] or 0
- if (o + d + c) == 12 and g == 2 then
- yes = true
- end
- end
- if yes then
- return true, origin, airpoints, northsouth, ns_key, playerorigin
- end
- end
- function obsidian_gateway.attempt_activation(pos, player)
- local pname = player:get_player_name()
- local ppos = vector_round(player:get_pos())
- local under = utility.node_under_pos(player:get_pos())
- local inside = vector.add(under, {x=0, y=1, z=0})
- local nodeunder = minetest.get_node(under).name
- -- Player must be standing on one of these.
- if nodeunder ~= "default:obsidian" and
- nodeunder ~= "griefer:grieferstone" and
- nodeunder ~= "cavestuff:dark_obsidian" then
- -- This triggers when other types of portals are used, so is incorrect to display this chat.
- --minetest.chat_send_player(pname, "# Server: You need to be standing in the gateway for it to work!")
- return
- end
- local success
- local origin
- local northsouth
- local ns_key
- local playerorigin
- local airpoints
- success, origin, airpoints, northsouth, ns_key, playerorigin =
- obsidian_gateway.find_gate(pos)
- if not success then
- return
- end
- -- Add/update sound beacon.
- ambiance.spawn_sound_beacon("soundbeacon:gate", origin, 20, 1)
- ambiance.replay_nearby_sound_beacons(origin, 6)
- if sheriff.is_cheater(pname) then
- if sheriff.punish_probability(pname) then
- sheriff.punish_player(pname)
- return
- end
- end
- minetest.log("action", pname .. " activated gateway @ " .. minetest.pos_to_string(pos))
- local target
- local meta = minetest.get_meta(origin)
- -- By spliting the key names by ns/ew, I ensure connected portals don't
- -- stomp on each other's data.
- target = minetest.string_to_pos(meta:get_string("obsidian_gateway_destination_" .. ns_key))
- --if not target then
- -- minetest.chat_send_player(pname, "# Server: Gateway has no destination! Aborting.")
- -- return
- --end
- -- Enable this if any serious problems occur.
- --if pname ~= "MustTest" then
- -- minetest.chat_send_player(pname, "# Server: Safety abort! Gateways are locked until further notice due to an error in the code.")
- -- return
- --end
- local isreturngate = (meta:get_int("obsidian_gateway_return_gate_" .. ns_key) == 1)
- local actual_owner = meta:get_string("obsidian_gateway_owner_" .. ns_key)
- local isowner = (actual_owner == pname)
- local first_time_init = false
- -- Initialize gateway for the first time.
- if not target or (meta:get_string("obsidian_gateway_success_" .. ns_key) ~= "yes" and not isreturngate) then
- -- Target is valid then this could be an OLD gate with old metadata.
- if target and not isreturngate and meta:get_string("obsidian_gateway_success_" .. ns_key) == "" then
- minetest.chat_send_player(pname, "# Server: It looks like this could possibly be an OLD gate! Aborting for safety reasons.")
- minetest.chat_send_player(pname, "# Server: If this Gateway was previously functioning normally, please mail the admin with the coordinates.")
- minetest.chat_send_player(pname, "# Server: If this is a Gate that you have just constructed, you can safely ignore this message.")
- minetest.chat_send_player(pname, "# Server: The Gateway's EXIT location is @ " .. rc.pos_to_namestr(target) .. ".")
- minetest.after(1.5, function() easyvend.sound_error(pname) end)
- return
- end
- -- Algorithm for locating the destination.
- -- Get a potential gate location.
- target = rc.get_random_realm_gate_position(pname, origin)
- -- Is target outside bounds?
- local bad = function(target, origin)
- -- Handle nil.
- if not target then
- return true
- end
- -- Don't allow exit points near the colonies.
- if vector_distance(target, {x=0, y=0, z=0}) < 2000 or
- vector_distance(target, {x=0, y=-30790, z=0}) < 2000 then
- return true
- end
- -- Exit must not be too close to start.
- if vector_distance(target, origin) < 500 then
- return true
- end
- -- Or too far.
- -- This causes too many failures.
- -- Note: this is now handled by the 'rc' mod.
- --if vector_distance(target, origin) > 7000 then
- -- return true
- --end
- if not rc.is_valid_gateway_region(target) then
- return true
- end
- end
- -- Keep trying until the target is within bounds.
- local num_tries = 0
- while bad(target, origin) do
- target = rc.get_random_realm_gate_position(pname, origin)
- num_tries = num_tries + 1
- -- Max 3 tries.
- if num_tries >= 2 then
- ---[[
- minetest.after(0, function()
- -- Detonate some TNT!
- tnt.boom(vector.add(ppos, {x=math_random(-3, 3), y=0, z=math_random(-3, 3)}), {
- radius = 3,
- ignore_protection = false,
- ignore_on_blast = false,
- damage_radius = 5,
- disable_drops = true,
- })
- end)
- --]]
- return
- end
- end
- meta:set_string("obsidian_gateway_destination_" .. ns_key, minetest.pos_to_string(target))
- meta:set_string("obsidian_gateway_owner_" .. ns_key, pname)
- first_time_init = true
- isowner = true
- end
- --minetest.chat_send_player(pname, "# Server: Safety ABORT #2.")
- --do return end
- if gdac.player_is_admin(pname) then
- isowner = true
- end
- -- Let everyone use gates owned by the admin.
- if actual_owner == "MustTest" then
- isowner = true
- end
- -- Slightly randomize player's exit coordinates.
- -- Without changing the coordinates of the gateway.
- local pdest
- if northsouth then
- pdest = vector.add(target, {x=math_random(0, 1), y=0, z=0})
- else
- pdest = vector.add(target, {x=0, y=0, z=math_random(0, 1)})
- end
- -- Collect any friends to bring along.
- local friendstobring = {}
- local allplayers = minetest.get_connected_players()
- for k, v in ipairs(allplayers) do
- if v:get_player_name() ~= pname then
- if vector_distance(v:get_pos(), player:get_pos()) < 3 then
- friendstobring[#friendstobring+1] = v:get_player_name()
- end
- end
- end
- -- Create a gateway at the player's destination.
- -- This gateway links back to the first.
- -- If it is destroyed, the player is stuck!
- preload_tp.execute({
- player_name = pname,
- target_position = pdest,
- emerge_radius = 32,
- particle_effects = true,
- pre_teleport_callback = function()
- if not isowner then
- -- Grief portal if used by someone other than owner.
- local plava = airpoints[math_random(1, #airpoints)]
- --minetest.chat_send_all("# Server: Attempting to grief gateway @ " .. minetest.pos_to_string(plava) .. "!")
- if minetest.get_node(plava).name == "air" then
- if plava.y < -10 then
- minetest.add_node(plava, {name="default:lava_source"})
- else
- minetest.add_node(plava, {name="fire:basic_flame"})
- end
- end
- end
- -- Don't build return portal on top of someone's protected stuff.
- if first_time_init then
- if check_protection(vector.add(target, {x=0, y=3, z=0}), 5) then
- minetest.chat_send_player(pname, "# Server: Return-gate construction FAILED due to protection near " .. rc.pos_to_namestr(target) .. ".")
- -- Clear data for the initial gate. This will permit the player to retry without tearing everything down and building it again.
- local meta = minetest.get_meta(origin)
- meta:set_string("obsidian_gateway_success_" .. ns_key, "")
- meta:set_string("obsidian_gateway_destination_" .. ns_key, "")
- meta:set_string("obsidian_gateway_owner_" .. ns_key, "")
- -- Cancel transport.
- return true
- end
- end
- -- Build return portal (only if not already using a return portal).
- -- Also, only build return portal on first use of the initial portal.
- if not isreturngate and first_time_init then
- if northsouth then
- -- Place northsouth gateway.
- local path = obsidian_gateway.modpath .. "/obsidian_gateway_northsouth.mts"
- local gpos = vector.add(target, {x=-1, y=-1, z=0})
- minetest.place_schematic(gpos, path, "0", nil, true)
- local meta = minetest.get_meta(gpos)
- meta:set_string("obsidian_gateway_destination_" .. ns_key, minetest.pos_to_string(playerorigin))
- meta:set_string("obsidian_gateway_owner_" .. ns_key, pname)
- meta:set_int("obsidian_gateway_return_gate_" .. ns_key, 1)
- else
- -- Place eastwest gateway.
- local path = obsidian_gateway.modpath .. "/obsidian_gateway_eastwest.mts"
- local gpos = vector.add(target, {x=0, y=-1, z=-1})
- minetest.place_schematic(gpos, path, "0", nil, true)
- local meta = minetest.get_meta(gpos)
- meta:set_string("obsidian_gateway_destination_" .. ns_key, minetest.pos_to_string(playerorigin))
- meta:set_string("obsidian_gateway_owner_" .. ns_key, pname)
- meta:set_int("obsidian_gateway_return_gate_" .. ns_key, 1)
- end
- end
- -- Mark the initial gate as success.
- -- If this is not done, then gate will assume it is not initialized
- -- the next time it is used. This fixes a bug where the return gate is
- -- not properly constructed if the player moves during transport
- -- (because this callback function doesn't get called).
- if not isreturngate and first_time_init then
- local meta = minetest.get_meta(origin)
- meta:set_string("obsidian_gateway_success_" .. ns_key, "yes")
- meta:mark_as_private("obsidian_gateway_success_" .. ns_key)
- end
- -- If the destination is the Abyss, then kill player first.
- -- This helps to prevent player from bringing any foreign items into this realm.
- -- Note: this relies on the teleport code already checking all other preconditions
- -- first. I.e., if this callback returns 'false', then the player absolutely
- -- will be teleported.
- if rc.current_realm_at_pos(pdest) == "abyss" then
- -- Dump player bones, as if they died.
- -- This should behave exactly as if the player died, with the exception of
- -- setting the player's health to 0.
- bones.dump_bones(pname)
- bones.last_known_death_locations[pname] = nil -- Fake death.
- local pref = minetest.get_player_by_name(pname)
- pref:set_hp(pref:get_properties().hp_max)
- give_initial_stuff.give(pref)
- end
- end,
- post_teleport_callback = function()
- for k, v in ipairs(friendstobring) do
- local friend = minetest.get_player_by_name(v)
- if friend then
- local fname = friend:get_player_name()
- preload_tp.execute({
- player_name = fname,
- target_position = pdest,
- particle_effects = true,
- pre_teleport_callback = function()
- -- If the destination is the Abyss, then kill player first.
- -- This helps to prevent player from bringing any foreign items into this realm.
- -- Note: this relies on the teleport code already checking all other preconditions
- -- first. I.e., if this callback returns 'false', then the player absolutely
- -- will be teleported.
- if rc.current_realm_at_pos(pdest) == "abyss" then
- -- Dump player bones, as if they died.
- -- This should behave exactly as if the player died, with the exception of
- -- setting the player's health to 0.
- bones.dump_bones(fname)
- bones.last_known_death_locations[fname] = nil -- Fake death.
- local pref = minetest.get_player_by_name(fname)
- pref:set_hp(pref:get_properties().hp_max)
- give_initial_stuff.give(pref)
- end
- end,
- force_teleport = true,
- send_blocks = true,
- })
- portal_sickness.on_use_portal(fname)
- end
- end
- -- Update liquids around on first init.
- if first_time_init then
- minetest.after(2, function()
- mapfix.execute(target, 10)
- end)
- end
- ambiance.spawn_sound_beacon("soundbeacon:gate", target, 20, 1)
- ambiance.replay_nearby_sound_beacons(target, 6)
- portal_sickness.on_use_portal(pname)
- end,
- teleport_sound = "nether_portal_usual",
- send_blocks = true,
- })
- end
- if not obsidian_gateway.run_once then
- local c = "obsidian_gateway:core"
- local f = obsidian_gateway.modpath .. "/init.lua"
- reload.register_file(c, f, false)
- obsidian_gateway.run_once = true
- end
|