From 52b20925d55ed71fdfbfa0d5db176559b29370d9 Mon Sep 17 00:00:00 2001 From: Treer Date: Wed, 17 Jul 2019 23:07:05 +1000 Subject: [PATCH] Unlinks target portal if it's linked to a different portal This is effectively a missing part to the change that made locate_or_build_portal() enforce that portals only link together in mutual pairs. As target portals that didn't need to be located or built we not being updated to link back to where the player travelled from. Also * implements events: on_run_wormhole, on_extinguish * adds a lot of debug info --- portal_api.lua | 44 ++++++++++++++++++++++++++++++++++++++++---- portal_api.txt | 14 +++++++++----- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/portal_api.lua b/portal_api.lua index 95f55e7..46dbe5f 100644 --- a/portal_api.lua +++ b/portal_api.lua @@ -361,8 +361,11 @@ local function list_closest_portals(portal_definition, anchorPos, distance_limit end +-- Note: will extinguish any portal using the same nodes that are being set local function set_portal_metadata(portal_definition, anchorPos, orientation, destination_wormholePos, ignite) + if DEBUG then minetest.chat_send_all("set_portal_metadata(ignite=" .. tostring(ignite) .. ") at " .. minetest.pos_to_string(anchorPos) .. " orient " .. orientation .. ", setting to target " .. minetest.pos_to_string(destination_wormholePos)) end + -- Portal position is stored in metadata as p1 and p2 to keep maps compatible with earlier versions of this mod. -- p1 is the bottom/west/south corner of the portal, and p2 is the opposite corner, together -- they define the bounding volume for the portal. @@ -385,11 +388,11 @@ local function set_portal_metadata(portal_definition, anchorPos, orientation, de local existing_p1 = meta:get_string("p1") if existing_p1 ~= "" then local existing_p2 = meta:get_string("p2") - if DEBUG then minetest.chat_send_all("existing_p1 " .. existing_p1 .. ", existing_p2" .. existing_p2 .. ", p1 " .. p1_string .. ", p2 " .. p2_string) end if existing_p1 ~= p1_string or existing_p2 ~= p2_string then + if DEBUG then minetest.chat_send_all("set_portal_metadata() found existing metadata from another portal: existing_p1 " .. existing_p1 .. ", existing_p2" .. existing_p2 .. ", p1 " .. p1_string .. ", p2 " .. p2_string .. ", will existinguish existing portal...") end -- this node is already part of another portal, so extinguish that, because nodes only -- contain a link in the metadata to one portal, and being part of two allows a slew of bugs - extinguish_portal(pos, node_name) + extinguish_portal(pos, node_name, false) -- clear the metadata to avoid causing a loop if extinguish_portal() fails on this node (e.g. it only works on frame nodes) meta:set_string("p1", nil) @@ -727,6 +730,7 @@ end local function ignite_portal(ignition_pos, ignition_node_name) if ignition_node_name == nil then ignition_node_name = minetest.get_node(ignition_pos).name end + if DEBUG then minetest.chat_send_all("IGNITE the " .. ignition_node_name .. " at " .. minetest.pos_to_string(ignition_pos)) end -- find which sort of portals are made from the node that was clicked on local portal_definition_list = list_portal_definitions_for_frame_node(ignition_node_name) @@ -760,7 +764,7 @@ local function ignite_portal(ignition_pos, ignition_node_name) if destination_orientation == nil then destination_orientation = orientation end local destination_wormholePos = portal_definition.shape.get_wormholePos_from_anchorPos(destination_anchorPos, destination_orientation) - if DEBUG then minetest.chat_send_all("Destinaton set to " .. minetest.pos_to_string(destination_anchorPos)) end + if DEBUG then minetest.chat_send_all("Destination set to " .. minetest.pos_to_string(destination_anchorPos)) end -- ignition/BURN_BABY_BURN set_portal_metadata_and_ignite(portal_definition, anchorPos, orientation, destination_wormholePos) @@ -782,6 +786,7 @@ extinguish_portal = function(pos, node_name, frame_was_destroyed) -- assigned ra local p2 = minetest.string_to_pos(meta:get_string("p2")) local target = minetest.string_to_pos(meta:get_string("target")) if p1 == nil or p2 == nil then + if DEBUG then minetest.chat_send_all(" no active portal found to extinguish") end return end @@ -822,7 +827,14 @@ extinguish_portal = function(pos, node_name, frame_was_destroyed) -- assigned ra end end - if target ~= nil then extinguish_portal(target, node_name) end + if target ~= nil then + if DEBUG then minetest.chat_send_all(" attempting to also extinguish target with wormholePos " .. minetest.pos_to_string(target)) end + extinguish_portal(target, node_name) + end + + if portal_definition.on_extinguish ~= nil then + portal_definition.on_extinguish(portal_definition, anchorPos, orientation) + end end @@ -859,6 +871,23 @@ local function ensure_remote_portal_then_teleport(player, portal_definition, loc local destination_orientation = get_orientation_from_param2(dest_wormhole_node.param2) portal_definition.shape.disable_portal_trap(destination_anchorPos, destination_orientation) + -- if the portal is already linked to a different portal then extinguish the other portal and + -- update the target portal to point back at this one + local remoteMeta = minetest.get_meta(destination_wormholePos) + local remoteTarget = minetest.string_to_pos(remoteMeta:get_string("target")) + if remoteTarget == nil then + if DEBUG then minetest.chat_send_all("Failed to test whether target portal links back to this one") end + elseif not vector.equals(remoteTarget, local_wormholePos) then + if DEBUG then minetest.chat_send_all("Target portal is already linked, extinguishing then relighting to point back at this one") end + extinguish_portal(remoteTarget, portal_definition.frame_node_name, false) + set_portal_metadata_and_ignite( + portal_definition, + destination_anchorPos, + destination_orientation, + local_wormholePos + ) + end + -- rotate the player if the destination portal is a different orientation local rotation_angle = math.rad(destination_orientation - local_orientation) local offset = vector.subtract(playerPos, local_wormholePos) -- preserve player's position in the portal @@ -965,6 +994,10 @@ function run_wormhole(pos, time_elapsed) else local anchorPos, orientation = portal_definition.shape.get_anchorPos_and_orientation_from_p1_and_p2(p1, p2) portal_definition.shape.apply_func_to_wormhole_nodes(anchorPos, orientation, run_wormhole_node_func) + + if portal_definition.on_run_wormhole ~= nil then + portal_definition.on_run_wormhole(portal_definition, anchorPos, orientation) + end end end end @@ -1068,14 +1101,17 @@ function register_frame_node(frame_node_name) extended_node_def.replaced_by_portalapi.mesecons = extended_node_def.mesecons extended_node_def.mesecons = {effector = { action_on = function (pos, node) + if DEBUG then minetest.chat_send_all("portal frame material: mesecons action ON") end ignite_portal(pos, node.name) end, action_off = function (pos, node) + if DEBUG then minetest.chat_send_all("portal frame material: mesecons action OFF") end extinguish_portal(pos, node.name, false) end }} extended_node_def.replaced_by_portalapi.on_destruct = extended_node_def.on_destruct extended_node_def.on_destruct = function(pos) + if DEBUG then minetest.chat_send_all("portal frame material: destruct") end extinguish_portal(pos, frame_node_name, true) end extended_node_def.replaced_by_portalapi.on_timer = extended_node_def.on_timer diff --git a/portal_api.txt b/portal_api.txt index 1e6bbcb..06a8164 100644 --- a/portal_api.txt +++ b/portal_api.txt @@ -96,9 +96,13 @@ Used by `nether.register_portal`. -- If orientation is not specified then the orientation of the realm -- portal will be used. - on_run_wormhole, - on_ignite, - on_extinguish, - on_player_teleported, - on_created + on_run_wormhole = function(portalDef, anochorPos, orientation) + -- invoked once per second per portal + on_extinguish = function(portalDef, anochorPos, orientation) + -- invoked when a portal is extinguished, including when the portal + -- it connected to was extinguished. + + todo: on_ignite = function(portalDef, anochorPos, orientation) + todo: on_player_teleported = function(portalDef, oldPos, newPos) + todo: on_created = function(portalDef, anochorPos, orientation) }