Factorio: prevent players from getting stuck from Teleport Traps (#4537)

This commit is contained in:
Fabian Dill
2025-02-20 00:17:19 +01:00
committed by GitHub
parent 91a8fc91d6
commit 11fa43f0a4
3 changed files with 77 additions and 6 deletions

View File

@@ -263,7 +263,8 @@ class AttackTrapCount(TrapCount):
class TeleportTrapCount(TrapCount):
"""Trap items that when received trigger a random teleport."""
"""Trap items that when received trigger a random teleport.
It is ensured the player can walk back to where they got teleported from."""
display_name = "Teleport Traps"

View File

@@ -49,6 +49,73 @@ function fire_entity_at_entities(entity_name, entities, speed)
end
end
local teleport_requests = {}
local teleport_attempts = {}
local max_attempts = 100
function attempt_teleport_player(player, attempt)
-- global attempt storage as metadata can't be stored
if attempt == nil then
attempt = teleport_attempts[player.index]
else
teleport_attempts[player.index] = attempt
end
if attempt > max_attempts then
player.print("Teleport failed: No valid position found after " .. max_attempts .. " attempts!")
teleport_attempts[player.index] = 0
return
end
local surface = player.character.surface
local prototype_name = player.character.prototype.name
local original_position = player.character.position
local candidate_position = random_offset_position(original_position, 1024)
local non_colliding_position = surface.find_non_colliding_position(
prototype_name, candidate_position, 0, 1
)
if non_colliding_position then
-- Request pathfinding asynchronously
local path_id = surface.request_path{
bounding_box = player.character.prototype.collision_box,
collision_mask = { layers = { ["player"] = true } },
start = original_position,
goal = non_colliding_position,
force = player.force.name,
radius = 1,
pathfind_flags = {cache = true, low_priority = true, allow_paths_through_own_entities = true},
}
-- Store the request with the player index as the key
teleport_requests[player.index] = path_id
else
attempt_teleport_player(player, attempt + 1)
end
end
function handle_teleport_attempt(event)
for player_index, path_id in pairs(teleport_requests) do
-- Check if the event matches the stored path_id
if path_id == event.id then
local player = game.players[player_index]
if event.path then
if player.character then
player.character.teleport(event.path[#event.path].position) -- Teleport to the last point in the path
-- Clear the attempts for this player
teleport_attempts[player_index] = 0
return
end
return
end
attempt_teleport_player(player, nil)
break
end
end
end
function spill_character_inventory(character)
if not (character and character.valid) then
return false

View File

@@ -134,6 +134,9 @@ end
script.on_event(defines.events.on_player_changed_position, on_player_changed_position)
{% endif %}
-- Handle the pathfinding result of teleport traps
script.on_event(defines.events.on_script_path_request_finished, handle_teleport_attempt)
function count_energy_bridges()
local count = 0
for i, bridge in pairs(storage.energy_link_bridges) do
@@ -143,9 +146,11 @@ function count_energy_bridges()
end
return count
end
function get_energy_increment(bridge)
return ENERGY_INCREMENT + (ENERGY_INCREMENT * 0.3 * bridge.quality.level)
end
function on_check_energy_link(event)
--- assuming 1 MJ increment and 5MJ battery:
--- first 2 MJ request fill, last 2 MJ push energy, middle 1 MJ does nothing
@@ -722,12 +727,10 @@ end,
game.forces["enemy"].set_evolution_factor(new_factor, "nauvis")
game.print({"", "New evolution factor:", new_factor})
end,
["Teleport Trap"] = function ()
["Teleport Trap"] = function()
for _, player in ipairs(game.forces["player"].players) do
current_character = player.character
if current_character ~= nil then
current_character.teleport(current_character.surface.find_non_colliding_position(
current_character.prototype.name, random_offset_position(current_character.position, 1024), 0, 1))
if player.character then
attempt_teleport_player(player, 1)
end
end
end,