mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-11 15:54:52 +09:00
1252 lines
50 KiB
Lua
1252 lines
50 KiB
Lua
--[[-- Gui - Warp List
|
|
Adds a list of warp points which players can travel between, add, and edit
|
|
]]
|
|
|
|
local Gui = require("modules/exp_gui")
|
|
local Roles = require("modules/exp_roles")
|
|
local Colors = require("modules/exp_util/include/color")
|
|
local Feature = require("modules/exp_scenario/features")
|
|
|
|
local ExpUtil = require("modules/exp_util")
|
|
local format_time = ExpUtil.format_time_factory_locale{ format = "short", hours = true, minutes = true }
|
|
|
|
local floor = math.floor
|
|
local ceil = math.ceil
|
|
|
|
--- @class ExpGui_WarpList.elements
|
|
local Elements = {}
|
|
|
|
local Styles = {
|
|
sprite22 = { height = 22, width = 22, padding = -2 },
|
|
sprite32 = { height = 32, width = 32, left_margin = 1 },
|
|
}
|
|
|
|
--- Rich text icons shown next to a warp name to describe why it can or can not be used
|
|
local status_icons = {
|
|
cooldown = "[img=utility/multiplayer_waiting_icon]",
|
|
not_available = "[img=utility/set_bar_slot]",
|
|
bypass = "[img=utility/side_menu_bonus_icon]",
|
|
current = "[img=utility/side_menu_map_icon]",
|
|
connected = "[img=utility/logistic_network_panel_white]",
|
|
different = "[img=utility/warning_white]",
|
|
}
|
|
|
|
local feature, config = Feature.register("warp_list", {
|
|
minimum_distance = 100, -- the minimum distance that is allowed between warps on the same force
|
|
cooldown_duration = 60, -- the duration of the warp cooldown in seconds
|
|
standard_proximity_radius = 4, -- the minimum distance a player is allowed to be to a warp in order to use it
|
|
spawn_proximity_radius = 20, -- the minimum distance a player is allowed to be from their spawn point to use warps
|
|
user_can_edit_own_warps = false, -- the player who made a warp can edit it without the edit permission
|
|
})
|
|
|
|
--- Ticks between updates of the cooldown timers
|
|
local update_interval = 6
|
|
|
|
--- The default icon of new warps
|
|
local default_icon = { type = "item", name = "discharge-defense-equipment" }
|
|
|
|
--- The entities which are created for warp areas { name, x, y }
|
|
local area_entities = {
|
|
{ "small-lamp", -4, -2 }, { "small-lamp", -2, -4 }, { "medium-electric-pole", -3, -3 }, -- Top left corner
|
|
{ "small-lamp", 3, -2 }, { "small-lamp", 1, -4 }, { "medium-electric-pole", 2, -3 }, -- Top right corner
|
|
{ "small-lamp", 3, 1 }, { "small-lamp", 1, 3 }, { "medium-electric-pole", 2, 2 }, -- Bottom right corner
|
|
{ "small-lamp", -4, 1 }, { "small-lamp", -2, 3 }, { "medium-electric-pole", -3, 2 }, -- Bottom left corner
|
|
|
|
}
|
|
|
|
--- The tiles which are created for warp areas { name, x, y }
|
|
local area_tiles = {
|
|
{ "black-refined-concrete", -4, -2 }, { "black-refined-concrete", -4, -1 }, { "black-refined-concrete", -4, 0 }, { "black-refined-concrete", -4, 1 },
|
|
{ "black-refined-concrete", -3, -3 }, { "purple-refined-concrete", -3, -2 }, { "purple-refined-concrete", -3, -1 }, { "purple-refined-concrete", -3, 0 },
|
|
{ "purple-refined-concrete", -3, 1 }, { "black-refined-concrete", -3, 2 }, { "black-refined-concrete", -2, -4 }, { "purple-refined-concrete", -2, -3 },
|
|
{ "purple-refined-concrete", -2, -2 }, { "purple-refined-concrete", -2, -1 }, { "purple-refined-concrete", -2, 0 }, { "purple-refined-concrete", -2, 1 },
|
|
{ "purple-refined-concrete", -2, 2 }, { "black-refined-concrete", -2, 3 }, { "black-refined-concrete", -1, -4 }, { "purple-refined-concrete", -1, -3 },
|
|
{ "purple-refined-concrete", -1, -2 }, { "purple-refined-concrete", -1, -1 }, { "purple-refined-concrete", -1, 0 }, { "purple-refined-concrete", -1, 1 },
|
|
{ "purple-refined-concrete", -1, 2 }, { "black-refined-concrete", -1, 3 }, { "black-refined-concrete", 0, -4 }, { "purple-refined-concrete", 0, -3 },
|
|
{ "purple-refined-concrete", 0, -2 }, { "purple-refined-concrete", 0, -1 }, { "purple-refined-concrete", 0, 0 }, { "purple-refined-concrete", 0, 1 },
|
|
{ "purple-refined-concrete", 0, 2 }, { "black-refined-concrete", 0, 3 }, { "black-refined-concrete", 1, -4 }, { "purple-refined-concrete", 1, -3 },
|
|
{ "purple-refined-concrete", 1, -2 }, { "purple-refined-concrete", 1, -1 }, { "purple-refined-concrete", 1, 0 }, { "purple-refined-concrete", 1, 1 },
|
|
{ "purple-refined-concrete", 1, 2 }, { "black-refined-concrete", 1, 3 }, { "black-refined-concrete", 2, -3 }, { "purple-refined-concrete", 2, -2 },
|
|
{ "purple-refined-concrete", 2, -1 }, { "purple-refined-concrete", 2, 0 }, { "purple-refined-concrete", 2, 1 }, { "black-refined-concrete", 2, 2 },
|
|
{ "black-refined-concrete", 3, -2 }, { "black-refined-concrete", 3, -1 }, { "black-refined-concrete", 3, 0 }, { "black-refined-concrete", 3, 1 },
|
|
|
|
}
|
|
|
|
--- Names of the entities which make up a warp area, used to find them when the area is removed
|
|
local area_entity_names = {} --- @type string[]
|
|
for _, entity in ipairs(area_entities) do
|
|
area_entity_names[#area_entity_names + 1] = entity[1]
|
|
end
|
|
|
|
--- The role permission each action needs
|
|
local permissions = {
|
|
bypass_warp_cooldown = "exp_scenario.gui.warp_list.bypass_cooldown",
|
|
bypass_warp_proximity = "exp_scenario.gui.warp_list.bypass_proximity",
|
|
allow_add_warp = "exp_scenario.gui.warp_list.add",
|
|
allow_edit_warp = "exp_scenario.gui.warp_list.edit",
|
|
}
|
|
|
|
--- @class ExpGui_WarpList.Warp
|
|
--- @field id number
|
|
--- @field force LuaForce
|
|
--- @field name string
|
|
--- @field icon SignalID
|
|
--- @field surface LuaSurface
|
|
--- @field position MapPosition.struct The tile the warp is centred on
|
|
--- @field last_user LuaPlayer? Nil for warps made by the scenario
|
|
--- @field last_edit_tick number
|
|
--- @field editing table<number, string> Names of the players editing the warp keyed by their index
|
|
--- @field tag LuaCustomChartTag?
|
|
--- @field electric_pole LuaEntity? Pole within the warp area, warps on the same network can be travelled between
|
|
--- @field old_tile string? The tile the warp area replaced, nil when no area was made
|
|
--- @field new boolean? True until the first edit is confirmed, discarding that edit removes the warp
|
|
|
|
--- @class ExpGui_WarpList.PlayerStatus
|
|
--- @field in_range ExpGui_WarpList.Warp? The warp the player is standing on
|
|
--- @field on_cooldown boolean
|
|
--- @field bypass_proximity boolean
|
|
|
|
--- @param player LuaPlayer
|
|
--- @param action "bypass_warp_cooldown" | "bypass_warp_proximity" | "allow_add_warp" | "allow_edit_warp"
|
|
--- @return boolean
|
|
local function has_config_permission(player, action)
|
|
return Roles.player_has_permission(player, permissions[action])
|
|
end
|
|
|
|
--- Check if a player can edit a warp, the spawn warp can never be edited
|
|
--- @param player LuaPlayer
|
|
--- @param warp ExpGui_WarpList.Warp
|
|
--- @return boolean
|
|
local function has_permission_edit_warp(player, warp)
|
|
if Elements.container.get_spawn_warp(warp.force) == warp then
|
|
return false
|
|
end
|
|
|
|
if config.user_can_edit_own_warps and warp.last_user == player then
|
|
return true
|
|
end
|
|
|
|
return has_config_permission(player, "allow_edit_warp")
|
|
end
|
|
|
|
--- The sprite path for a warp icon, signal ids and sprite paths name virtual signals differently
|
|
--- @param icon SignalID
|
|
--- @return SpritePath
|
|
local function icon_sprite(icon)
|
|
if icon.type == "virtual" then
|
|
return "virtual-signal/" .. icon.name
|
|
end
|
|
return (icon.type or "item") .. "/" .. icon.name
|
|
end
|
|
|
|
--- Draw a cross over something that stops a warp being added
|
|
--- @param surface LuaSurface
|
|
--- @param player LuaPlayer
|
|
--- @param target LuaEntity | MapPosition
|
|
local function draw_blocker(surface, player, target)
|
|
rendering.draw_sprite{
|
|
sprite = "utility/rail_path_not_possible",
|
|
x_scale = 0.5,
|
|
y_scale = 0.5,
|
|
target = target,
|
|
surface = surface,
|
|
players = { player },
|
|
time_to_live = 60,
|
|
}
|
|
end
|
|
|
|
--- Button in the header which adds a warp at the player
|
|
--- @class ExpGui_WarpList.elements.add_warp_button: ExpElement
|
|
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
|
|
Elements.add_warp_button = Gui.define("warp_list/add_warp_button")
|
|
:track_all_elements()
|
|
:draw{
|
|
type = "sprite-button",
|
|
sprite = "utility/add",
|
|
tooltip = { "exp-gui_warp-list.tooltip-add" },
|
|
style = "shortcut_bar_button",
|
|
}
|
|
:style(Styles.sprite22)
|
|
:on_click(function(_, player)
|
|
if player.controller_type ~= defines.controllers.character then return end
|
|
local surface = player.physical_surface
|
|
local position = player.physical_position
|
|
|
|
local water_tiles = surface.find_tiles_filtered{
|
|
collision_mask = "water_tile",
|
|
radius = config.standard_proximity_radius + 1,
|
|
position = position,
|
|
}
|
|
if #water_tiles > 0 then
|
|
player.print({ "exp-gui_warp-list.add-warning-water", config.standard_proximity_radius + 1 }, Colors.orange_red)
|
|
player.play_sound{ path = "utility/wire_pickup" }
|
|
for _, tile in pairs(water_tiles) do
|
|
draw_blocker(surface, player, tile.position)
|
|
end
|
|
return
|
|
end
|
|
|
|
-- A larger radius than the area because the entities a player can place are larger than tiles
|
|
local entities = surface.find_entities_filtered{
|
|
radius = config.standard_proximity_radius + 2.5,
|
|
position = position,
|
|
collision_mask = { "item", "object", "player", "water_tile" },
|
|
}
|
|
if #entities > 1 then -- The player's own character is always found
|
|
player.print({ "exp-gui_warp-list.add-warning-entities", config.standard_proximity_radius + 2.5 }, Colors.orange_red)
|
|
player.play_sound{ path = "utility/wire_pickup" }
|
|
local character = player.character
|
|
for _, entity in pairs(entities) do
|
|
if entity ~= character then
|
|
draw_blocker(surface, player, entity)
|
|
end
|
|
end
|
|
return
|
|
end
|
|
|
|
local force = player.force --[[@as LuaForce]]
|
|
local warp = Elements.container.add_warp(force, surface, position, player)
|
|
Elements.container.set_editing(warp, player, true)
|
|
end) --[[@as any]]
|
|
|
|
--- Refresh whether the add button is enabled based on the closest warp
|
|
--- @param player LuaPlayer
|
|
--- @param blocking ExpGui_WarpList.Warp? The warp too close to add another, nil when a warp can be added
|
|
function Elements.add_warp_button.refresh_player_blocking(player, blocking)
|
|
local tooltip = blocking and { "exp-gui_warp-list.tooltip-add-too-close", blocking.name } or { "exp-gui_warp-list.tooltip-add" }
|
|
for _, add_warp_button in Elements.add_warp_button:tracked_elements(player) do
|
|
add_warp_button.enabled = blocking == nil
|
|
add_warp_button.tooltip = tooltip
|
|
end
|
|
end
|
|
|
|
--- @class ExpGui_WarpList.elements.warp_button.element_data
|
|
--- @field warp ExpGui_WarpList.Warp
|
|
|
|
--- Button which travels to the warp, enabled when the player is allowed to use it
|
|
--- @class ExpGui_WarpList.elements.warp_button: ExpElement
|
|
--- @field data table<LuaGuiElement, ExpGui_WarpList.elements.warp_button.element_data>
|
|
--- @overload fun(parent: LuaGuiElement, warp: ExpGui_WarpList.Warp): LuaGuiElement
|
|
Elements.warp_button = Gui.define("warp_list/warp_button")
|
|
:draw(function(_, parent, warp)
|
|
--- @cast warp ExpGui_WarpList.Warp
|
|
return parent.add{
|
|
type = "sprite-button",
|
|
sprite = icon_sprite(warp.icon),
|
|
style = "slot_button",
|
|
}
|
|
end)
|
|
:style(Styles.sprite32)
|
|
:element_data{
|
|
warp = Gui.from_argument(1),
|
|
}
|
|
:on_click(function(def, player, warp_button)
|
|
--- @cast def ExpGui_WarpList.elements.warp_button
|
|
Elements.container.teleport_player(player, def.data[warp_button].warp)
|
|
end) --[[@as any]]
|
|
|
|
--- @class ExpGui_WarpList.elements.icon_picker.element_data
|
|
--- @field warp ExpGui_WarpList.Warp
|
|
|
|
--- Signal picker shown in place of the warp button while editing
|
|
--- @class ExpGui_WarpList.elements.icon_picker: ExpElement
|
|
--- @field data table<LuaGuiElement, ExpGui_WarpList.elements.icon_picker.element_data>
|
|
--- @overload fun(parent: LuaGuiElement, warp: ExpGui_WarpList.Warp): LuaGuiElement
|
|
Elements.icon_picker = Gui.define("warp_list/icon_picker")
|
|
:draw(function(_, parent, warp)
|
|
--- @cast warp ExpGui_WarpList.Warp
|
|
return parent.add{
|
|
type = "choose-elem-button",
|
|
elem_type = "signal",
|
|
signal = warp.icon,
|
|
tooltip = { "exp-gui_warp-list.tooltip-icon" },
|
|
}
|
|
end)
|
|
:style(Styles.sprite32)
|
|
:element_data{
|
|
warp = Gui.from_argument(1),
|
|
} --[[@as any]]
|
|
|
|
--- Get the icon chosen in a picker, the game omits the type for items
|
|
--- @param icon_picker LuaGuiElement
|
|
--- @return SignalID
|
|
function Elements.icon_picker.get_icon(icon_picker)
|
|
local icon = icon_picker.elem_value --[[@as SignalID?]]
|
|
if not icon then
|
|
return Elements.icon_picker.data[icon_picker].warp.icon
|
|
end
|
|
icon.type = icon.type or "item"
|
|
return icon
|
|
end
|
|
|
|
--- Icon describing whether the warp can be used
|
|
--- @class ExpGui_WarpList.elements.status_label: ExpElement
|
|
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
|
|
Elements.status_label = Gui.define("warp_list/status_label")
|
|
:draw{
|
|
type = "label",
|
|
caption = status_icons.not_available,
|
|
}
|
|
:style{
|
|
top_padding = 1, -- Keeps the icon in place when the taller textfield is shown
|
|
single_line = false,
|
|
} --[[@as any]]
|
|
|
|
--- @class ExpGui_WarpList.elements.name_label.element_data
|
|
--- @field warp ExpGui_WarpList.Warp
|
|
|
|
--- Warp name, clicking it opens the map at the warp
|
|
--- @class ExpGui_WarpList.elements.name_label: ExpElement
|
|
--- @field data table<LuaGuiElement, ExpGui_WarpList.elements.name_label.element_data>
|
|
--- @overload fun(parent: LuaGuiElement, warp: ExpGui_WarpList.Warp): LuaGuiElement
|
|
Elements.name_label = Gui.define("warp_list/name_label")
|
|
:draw(function(_, parent, warp)
|
|
--- @cast warp ExpGui_WarpList.Warp
|
|
return parent.add{
|
|
type = "label",
|
|
caption = warp.name,
|
|
}
|
|
end)
|
|
:style{
|
|
single_line = true,
|
|
left_padding = 2,
|
|
right_padding = 2,
|
|
horizontally_stretchable = true,
|
|
}
|
|
:element_data{
|
|
warp = Gui.from_argument(1),
|
|
}
|
|
:on_click(function(def, player, name_label)
|
|
--- @cast def ExpGui_WarpList.elements.name_label
|
|
local warp = def.data[name_label].warp
|
|
player.set_controller{ type = defines.controllers.remote, position = warp.position, surface = warp.surface }
|
|
end) --[[@as any]]
|
|
|
|
--- Refresh the name and last edit of the label
|
|
--- @param name_label LuaGuiElement
|
|
--- @param warp ExpGui_WarpList.Warp
|
|
function Elements.name_label.refresh(name_label, warp)
|
|
local last_user = warp.last_user and warp.last_user.name or "<server>"
|
|
name_label.caption = warp.name
|
|
name_label.tooltip = { "exp-gui_warp-list.tooltip-last-edit", last_user, format_time(warp.last_edit_tick) }
|
|
end
|
|
|
|
--- @class ExpGui_WarpList.elements.name_field.element_data
|
|
--- @field warp ExpGui_WarpList.Warp
|
|
--- @field icon_picker LuaGuiElement
|
|
|
|
--- Textfield shown in place of the name label while editing, confirming it saves the edit
|
|
--- @class ExpGui_WarpList.elements.name_field: ExpElement
|
|
--- @field data table<LuaGuiElement, ExpGui_WarpList.elements.name_field.element_data>
|
|
--- @overload fun(parent: LuaGuiElement, warp: ExpGui_WarpList.Warp, icon_picker: LuaGuiElement): LuaGuiElement
|
|
Elements.name_field = Gui.define("warp_list/name_field")
|
|
:draw(function(_, parent, warp)
|
|
--- @cast warp ExpGui_WarpList.Warp
|
|
return parent.add{
|
|
type = "textfield",
|
|
text = warp.name,
|
|
clear_and_focus_on_right_click = true,
|
|
}
|
|
end)
|
|
:style{
|
|
minimal_width = 10,
|
|
maximal_width = 300,
|
|
horizontally_squashable = true,
|
|
horizontally_stretchable = true,
|
|
height = 22,
|
|
padding = -2,
|
|
left_margin = 2,
|
|
right_margin = 2,
|
|
}
|
|
:element_data{
|
|
warp = Gui.from_argument(1),
|
|
icon_picker = Gui.from_argument(2),
|
|
}
|
|
:on_confirmed(function(def, player, name_field)
|
|
--- @cast def ExpGui_WarpList.elements.name_field
|
|
local elements = def.data[name_field]
|
|
local icon = Elements.icon_picker.get_icon(elements.icon_picker)
|
|
Elements.container.update_warp(elements.warp, name_field.text, icon, player)
|
|
end) --[[@as any]]
|
|
|
|
--- @class ExpGui_WarpList.elements.confirm_edit_button.element_data
|
|
--- @field warp ExpGui_WarpList.Warp
|
|
--- @field name_field LuaGuiElement
|
|
--- @field icon_picker LuaGuiElement
|
|
|
|
--- Button which saves the name and icon being edited
|
|
--- @class ExpGui_WarpList.elements.confirm_edit_button: ExpElement
|
|
--- @field data table<LuaGuiElement, ExpGui_WarpList.elements.confirm_edit_button.element_data>
|
|
--- @overload fun(parent: LuaGuiElement, warp: ExpGui_WarpList.Warp, name_field: LuaGuiElement, icon_picker: LuaGuiElement): LuaGuiElement
|
|
Elements.confirm_edit_button = Gui.define("warp_list/confirm_edit_button")
|
|
:draw{
|
|
type = "sprite-button",
|
|
sprite = "utility/confirm_slot",
|
|
tooltip = { "exp-gui_warp-list.tooltip-confirm" },
|
|
style = "shortcut_bar_button_green",
|
|
}
|
|
:style(Styles.sprite22)
|
|
:element_data{
|
|
warp = Gui.from_argument(1),
|
|
name_field = Gui.from_argument(2),
|
|
icon_picker = Gui.from_argument(3),
|
|
}
|
|
:on_click(function(def, player, confirm_edit_button)
|
|
--- @cast def ExpGui_WarpList.elements.confirm_edit_button
|
|
local elements = def.data[confirm_edit_button]
|
|
local icon = Elements.icon_picker.get_icon(elements.icon_picker)
|
|
Elements.container.update_warp(elements.warp, elements.name_field.text, icon, player)
|
|
end) --[[@as any]]
|
|
|
|
--- @class ExpGui_WarpList.elements.discard_edit_button.element_data
|
|
--- @field warp ExpGui_WarpList.Warp
|
|
|
|
--- Button which discards an edit, a warp which was never confirmed is removed
|
|
--- @class ExpGui_WarpList.elements.discard_edit_button: ExpElement
|
|
--- @field data table<LuaGuiElement, ExpGui_WarpList.elements.discard_edit_button.element_data>
|
|
--- @overload fun(parent: LuaGuiElement, warp: ExpGui_WarpList.Warp): LuaGuiElement
|
|
Elements.discard_edit_button = Gui.define("warp_list/discard_edit_button")
|
|
:draw{
|
|
type = "sprite-button",
|
|
sprite = "utility/close_black",
|
|
tooltip = { "exp-gui_warp-list.tooltip-discard" },
|
|
style = "shortcut_bar_button_red",
|
|
}
|
|
:style(Styles.sprite22)
|
|
:element_data{
|
|
warp = Gui.from_argument(1),
|
|
}
|
|
:on_click(function(def, player, discard_edit_button)
|
|
--- @cast def ExpGui_WarpList.elements.discard_edit_button
|
|
local warp = def.data[discard_edit_button].warp
|
|
if warp.new then
|
|
Elements.container.remove_warp(warp)
|
|
else
|
|
Elements.container.set_editing(warp, player, false)
|
|
end
|
|
end) --[[@as any]]
|
|
|
|
--- @class ExpGui_WarpList.elements.edit_warp_button.element_data
|
|
--- @field warp ExpGui_WarpList.Warp
|
|
|
|
--- Button which starts editing a warp
|
|
--- @class ExpGui_WarpList.elements.edit_warp_button: ExpElement
|
|
--- @field data table<LuaGuiElement, ExpGui_WarpList.elements.edit_warp_button.element_data>
|
|
--- @overload fun(parent: LuaGuiElement, warp: ExpGui_WarpList.Warp): LuaGuiElement
|
|
Elements.edit_warp_button = Gui.define("warp_list/edit_warp_button")
|
|
:draw{
|
|
type = "sprite-button",
|
|
sprite = "utility/rename_icon",
|
|
tooltip = { "exp-gui_warp-list.tooltip-edit-none" },
|
|
style = "shortcut_bar_button",
|
|
}
|
|
:style(Styles.sprite22)
|
|
:element_data{
|
|
warp = Gui.from_argument(1),
|
|
}
|
|
:on_click(function(def, player, edit_warp_button)
|
|
--- @cast def ExpGui_WarpList.elements.edit_warp_button
|
|
Elements.container.set_editing(def.data[edit_warp_button].warp, player, true)
|
|
end) --[[@as any]]
|
|
|
|
--- Refresh the tooltip which lists who is editing the warp
|
|
--- @param edit_warp_button LuaGuiElement
|
|
--- @param warp ExpGui_WarpList.Warp
|
|
function Elements.edit_warp_button.refresh(edit_warp_button, warp)
|
|
if next(warp.editing) then
|
|
local player_names = table.get_values(warp.editing)
|
|
edit_warp_button.hovered_sprite = "utility/warning_icon"
|
|
edit_warp_button.tooltip = { "exp-gui_warp-list.tooltip-edit", table.concat(player_names, ", ") }
|
|
else
|
|
edit_warp_button.hovered_sprite = "utility/rename_icon"
|
|
edit_warp_button.tooltip = { "exp-gui_warp-list.tooltip-edit-none" }
|
|
end
|
|
end
|
|
|
|
--- @class ExpGui_WarpList.elements.remove_warp_button.element_data
|
|
--- @field warp ExpGui_WarpList.Warp
|
|
|
|
--- Button which removes a warp along with its area and map tag
|
|
--- @class ExpGui_WarpList.elements.remove_warp_button: ExpElement
|
|
--- @field data table<LuaGuiElement, ExpGui_WarpList.elements.remove_warp_button.element_data>
|
|
--- @overload fun(parent: LuaGuiElement, warp: ExpGui_WarpList.Warp): LuaGuiElement
|
|
Elements.remove_warp_button = Gui.define("warp_list/remove_warp_button")
|
|
:draw{
|
|
type = "sprite-button",
|
|
sprite = "utility/trash",
|
|
tooltip = { "exp-gui_warp-list.tooltip-remove" },
|
|
style = "shortcut_bar_button_red",
|
|
}
|
|
:style(Styles.sprite22)
|
|
:element_data{
|
|
warp = Gui.from_argument(1),
|
|
}
|
|
:on_click(function(def, _, remove_warp_button)
|
|
--- @cast def ExpGui_WarpList.elements.remove_warp_button
|
|
Elements.container.remove_warp(def.data[remove_warp_button].warp)
|
|
end) --[[@as any]]
|
|
|
|
--- @class ExpGui_WarpList.elements.warp_table.row
|
|
--- @field warp ExpGui_WarpList.Warp
|
|
--- @field icon_flow LuaGuiElement
|
|
--- @field name_flow LuaGuiElement
|
|
--- @field action_flow LuaGuiElement
|
|
--- @field warp_button LuaGuiElement
|
|
--- @field icon_picker LuaGuiElement
|
|
--- @field status_label LuaGuiElement
|
|
--- @field name_label LuaGuiElement
|
|
--- @field name_field LuaGuiElement
|
|
--- @field confirm_edit_button LuaGuiElement
|
|
--- @field discard_edit_button LuaGuiElement
|
|
--- @field edit_warp_button LuaGuiElement
|
|
--- @field remove_warp_button LuaGuiElement
|
|
|
|
--- @class ExpGui_WarpList.elements.warp_table.element_data
|
|
--- @field rows table<number, ExpGui_WarpList.elements.warp_table.row> Keyed by warp id
|
|
|
|
--- Table with a row of icon, name, and actions for each warp of the player's force
|
|
--- @class ExpGui_WarpList.elements.warp_table: ExpElement
|
|
--- @field data table<LuaGuiElement, ExpGui_WarpList.elements.warp_table.element_data>
|
|
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
|
|
Elements.warp_table = Gui.define("warp_list/warp_table")
|
|
:track_all_elements()
|
|
:element_data{ rows = {} }
|
|
:draw(function(_, parent)
|
|
local warp_table = Gui.elements.scroll_table(parent, 250, 3, "scroll")
|
|
-- Always showing the scrollbar stops the gui changing width as warps are added
|
|
assert(warp_table.parent).vertical_scroll_policy = "always"
|
|
return warp_table
|
|
end)
|
|
:style{
|
|
top_cell_padding = 3,
|
|
bottom_cell_padding = 3,
|
|
} --[[@as any]]
|
|
|
|
--- Add a row for a warp
|
|
--- @param warp_table LuaGuiElement
|
|
--- @param warp ExpGui_WarpList.Warp
|
|
--- @return ExpGui_WarpList.elements.warp_table.row
|
|
function Elements.warp_table.add_row(warp_table, warp)
|
|
local icon_flow = warp_table.add{ type = "flow" }
|
|
icon_flow.style.padding = 0
|
|
local warp_button = Elements.warp_button(icon_flow, warp)
|
|
local icon_picker = Elements.icon_picker(icon_flow, warp)
|
|
|
|
local name_flow = warp_table.add{ type = "flow" }
|
|
name_flow.style.padding = 0
|
|
local status_label = Elements.status_label(name_flow)
|
|
local name_label = Elements.name_label(name_flow, warp)
|
|
local name_field = Elements.name_field(name_flow, warp, icon_picker)
|
|
|
|
local action_flow = warp_table.add{ type = "flow" }
|
|
action_flow.style.padding = 0
|
|
local confirm_edit_button = Elements.confirm_edit_button(action_flow, warp, name_field, icon_picker)
|
|
local discard_edit_button = Elements.discard_edit_button(action_flow, warp)
|
|
local edit_warp_button = Elements.edit_warp_button(action_flow, warp)
|
|
local remove_warp_button = Elements.remove_warp_button(action_flow, warp)
|
|
|
|
local row = {
|
|
warp = warp,
|
|
icon_flow = icon_flow,
|
|
name_flow = name_flow,
|
|
action_flow = action_flow,
|
|
warp_button = warp_button,
|
|
icon_picker = icon_picker,
|
|
status_label = status_label,
|
|
name_label = name_label,
|
|
name_field = name_field,
|
|
confirm_edit_button = confirm_edit_button,
|
|
discard_edit_button = discard_edit_button,
|
|
edit_warp_button = edit_warp_button,
|
|
remove_warp_button = remove_warp_button,
|
|
}
|
|
|
|
Elements.warp_table.data[warp_table].rows[warp.id] = row
|
|
return row
|
|
end
|
|
|
|
--- Remove the row for a warp
|
|
--- @param warp_table LuaGuiElement
|
|
--- @param warp ExpGui_WarpList.Warp
|
|
function Elements.warp_table.remove_row(warp_table, warp)
|
|
local rows = Elements.warp_table.data[warp_table].rows
|
|
local row = rows[warp.id]
|
|
if not row then return end
|
|
rows[warp.id] = nil
|
|
row.icon_flow.destroy()
|
|
row.name_flow.destroy()
|
|
row.action_flow.destroy()
|
|
end
|
|
|
|
--- Refresh whether a warp can be used from where the player is
|
|
--- @param row ExpGui_WarpList.elements.warp_table.row
|
|
--- @param status ExpGui_WarpList.PlayerStatus
|
|
function Elements.warp_table._refresh_row_status(row, status)
|
|
local warp = row.warp
|
|
local in_range = status.in_range
|
|
local position = warp.position
|
|
local tooltip, icon, enabled
|
|
|
|
if not in_range then
|
|
if status.bypass_proximity then
|
|
tooltip, icon, enabled = { "exp-gui_warp-list.tooltip-goto-bypass", position.x, position.y }, status_icons.bypass, true
|
|
else
|
|
tooltip, icon, enabled = { "exp-gui_warp-list.tooltip-goto-disabled" }, status_icons.not_available, false
|
|
end
|
|
elseif in_range == warp then
|
|
tooltip, icon, enabled = { "exp-gui_warp-list.tooltip-goto-same-warp" }, status_icons.current, false
|
|
elseif status.on_cooldown then
|
|
tooltip, icon, enabled = { "exp-gui_warp-list.tooltip-goto-cooldown" }, status_icons.cooldown, false
|
|
else
|
|
-- Warps are connected when their poles share an electric network, missing poles never match
|
|
local network_id = warp.electric_pole and warp.electric_pole.valid and warp.electric_pole.electric_network_id or -1
|
|
local in_range_network_id = in_range.electric_pole and in_range.electric_pole.valid and in_range.electric_pole.electric_network_id or -2
|
|
if network_id == in_range_network_id then
|
|
tooltip, icon, enabled = { "exp-gui_warp-list.tooltip-goto", position.x, position.y }, status_icons.connected, true
|
|
elseif status.bypass_proximity then
|
|
tooltip, icon, enabled = { "exp-gui_warp-list.tooltip-goto-bypass-different-network", position.x, position.y }, status_icons.bypass, true
|
|
else
|
|
tooltip, icon, enabled = { "exp-gui_warp-list.tooltip-goto-different-network" }, status_icons.different, false
|
|
end
|
|
end
|
|
|
|
row.warp_button.tooltip = tooltip
|
|
row.warp_button.enabled = enabled
|
|
row.status_label.tooltip = tooltip
|
|
row.status_label.caption = icon
|
|
row.name_label.style.font = enabled and "default-semibold" or "default"
|
|
end
|
|
|
|
--- Refresh a row to match its warp, the player's permissions, and where the player is
|
|
--- @param warp_table LuaGuiElement
|
|
--- @param row ExpGui_WarpList.elements.warp_table.row
|
|
--- @param status ExpGui_WarpList.PlayerStatus
|
|
function Elements.warp_table.refresh_row(warp_table, row, status)
|
|
local player = Gui.get_player(warp_table)
|
|
local warp = row.warp
|
|
local can_edit = has_permission_edit_warp(player, warp)
|
|
local editing = warp.editing[player.index] ~= nil
|
|
|
|
Elements.edit_warp_button.refresh(row.edit_warp_button, warp)
|
|
Elements.name_label.refresh(row.name_label, warp)
|
|
|
|
row.warp_button.visible = not editing
|
|
row.icon_picker.visible = editing
|
|
row.name_label.visible = not editing
|
|
row.name_field.visible = editing
|
|
row.confirm_edit_button.visible = editing
|
|
row.discard_edit_button.visible = editing
|
|
row.edit_warp_button.visible = can_edit and not editing
|
|
row.remove_warp_button.visible = can_edit and not editing
|
|
|
|
if editing then
|
|
row.name_field.focus()
|
|
assert(warp_table.parent).scroll_to_element(row.name_field, "top-third")
|
|
else
|
|
row.warp_button.sprite = icon_sprite(warp.icon)
|
|
row.icon_picker.elem_value = warp.icon
|
|
row.name_field.text = warp.name
|
|
end
|
|
|
|
Elements.warp_table._refresh_row_status(row, status)
|
|
end
|
|
|
|
--- Rebuild every row in the order of the warps
|
|
--- @param warp_table LuaGuiElement
|
|
--- @param warps ExpGui_WarpList.Warp[]
|
|
--- @param status ExpGui_WarpList.PlayerStatus
|
|
function Elements.warp_table.rebuild(warp_table, warps, status)
|
|
warp_table.clear()
|
|
Elements.warp_table.data[warp_table].rows = {}
|
|
for _, warp in ipairs(warps) do
|
|
local row = Elements.warp_table.add_row(warp_table, warp)
|
|
Elements.warp_table.refresh_row(warp_table, row, status)
|
|
end
|
|
end
|
|
|
|
--- Refresh the row of one warp for the online players of its force
|
|
--- @param warp ExpGui_WarpList.Warp
|
|
function Elements.warp_table.refresh_row_force(warp)
|
|
for player, warp_table in Elements.warp_table:online_elements(warp.force) do
|
|
--- @cast player -nil
|
|
local row = Elements.warp_table.data[warp_table].rows[warp.id]
|
|
if row then
|
|
Elements.warp_table.refresh_row(warp_table, row, Elements.container.get_status(player))
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Refresh whether each warp can be used from where the player is
|
|
--- @param player LuaPlayer
|
|
function Elements.warp_table.refresh_status_player(player)
|
|
local status = Elements.container.get_status(player)
|
|
for _, warp_table in Elements.warp_table:tracked_elements(player) do
|
|
for _, row in pairs(Elements.warp_table.data[warp_table].rows) do
|
|
Elements.warp_table._refresh_row_status(row, status)
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Rebuild the rows for a player, used when their force or permissions change
|
|
--- @param player LuaPlayer
|
|
function Elements.warp_table.rebuild_player(player)
|
|
local warps = Elements.container.get_warps(player.force --[[@as LuaForce]])
|
|
local status = Elements.container.get_status(player)
|
|
for _, warp_table in Elements.warp_table:tracked_elements(player) do
|
|
Elements.warp_table.rebuild(warp_table, warps, status)
|
|
end
|
|
end
|
|
|
|
--- Rebuild the rows for the online players of a force, used when the warps change
|
|
--- @param force LuaForce
|
|
function Elements.warp_table.rebuild_force(force)
|
|
local warps = Elements.container.get_warps(force)
|
|
for player, warp_table in Elements.warp_table:online_elements(force) do
|
|
--- @cast player -nil
|
|
Elements.warp_table.rebuild(warp_table, warps, Elements.container.get_status(player))
|
|
end
|
|
end
|
|
|
|
--- Progress bar which fills as the warp cooldown runs out
|
|
--- @class ExpGui_WarpList.elements.cooldown_bar: ExpElement
|
|
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
|
|
Elements.cooldown_bar = Gui.define("warp_list/cooldown_bar")
|
|
:track_all_elements()
|
|
:draw{
|
|
type = "progressbar",
|
|
value = 1,
|
|
tooltip = { "exp-gui_warp-list.tooltip-cooldown-none", config.cooldown_duration },
|
|
}
|
|
:style{
|
|
horizontally_stretchable = true,
|
|
color = Colors.light_blue,
|
|
} --[[@as any]]
|
|
|
|
--- Refresh the bar to show the ticks left on the cooldown
|
|
--- @param cooldown_bar LuaGuiElement
|
|
--- @param cooldown number
|
|
function Elements.cooldown_bar.refresh(cooldown_bar, cooldown)
|
|
if cooldown > 0 then
|
|
cooldown_bar.value = 1 - cooldown / (config.cooldown_duration * 60)
|
|
cooldown_bar.tooltip = { "exp-gui_warp-list.tooltip-cooldown", ceil(cooldown / 60) }
|
|
else
|
|
cooldown_bar.value = 1
|
|
cooldown_bar.tooltip = { "exp-gui_warp-list.tooltip-cooldown-none", config.cooldown_duration }
|
|
end
|
|
end
|
|
|
|
--- Refresh the bars of a player
|
|
--- @param player LuaPlayer
|
|
--- @param cooldown number
|
|
function Elements.cooldown_bar.refresh_player(player, cooldown)
|
|
for _, cooldown_bar in Elements.cooldown_bar:tracked_elements(player) do
|
|
Elements.cooldown_bar.refresh(cooldown_bar, cooldown)
|
|
end
|
|
end
|
|
|
|
--- @class ExpGui_WarpList.elements.container.elements
|
|
--- @field frame LuaGuiElement
|
|
--- @field add_warp_button LuaGuiElement
|
|
--- @field warp_table LuaGuiElement
|
|
--- @field cooldown_bar LuaGuiElement
|
|
|
|
--- @class ExpGui_WarpList.elements.container.force_data
|
|
--- @field warps ExpGui_WarpList.Warp[] The spawn warp first, then by name
|
|
--- @field spawn ExpGui_WarpList.Warp?
|
|
--- @field next_warp_id number
|
|
|
|
--- @class ExpGui_WarpList.elements.container.player_data
|
|
--- @field cooldown number Ticks until the player can warp again
|
|
--- @field in_range ExpGui_WarpList.Warp? The warp the player is standing on
|
|
--- @field blocking ExpGui_WarpList.Warp? The warp too close for another to be added
|
|
--- @field keep_open boolean True once the player toggles the list themselves, which stops it hiding when they leave a warp
|
|
|
|
--- @class ExpGui_WarpList.elements.container.data
|
|
--- @field [LuaGuiElement] ExpGui_WarpList.elements.container.elements
|
|
--- @field [LuaForce] ExpGui_WarpList.elements.container.force_data
|
|
--- @field [LuaPlayer] ExpGui_WarpList.elements.container.player_data
|
|
|
|
--- Container added to the left gui flow
|
|
--- @class ExpGui_WarpList.elements.container: ExpElement
|
|
--- @field data ExpGui_WarpList.elements.container.data
|
|
Elements.container = Gui.define("warp_list/container")
|
|
:draw(function(def, parent)
|
|
--- @cast def ExpGui_WarpList.elements.container
|
|
local player = Gui.get_player(parent)
|
|
local allow_add_warp = has_config_permission(player, "allow_add_warp")
|
|
local container = Gui.elements.container(parent, allow_add_warp and 268 or 220)
|
|
|
|
local header = Gui.elements.header(container, {
|
|
caption = { "exp-gui_warp-list.caption-main" },
|
|
tooltip = {
|
|
"exp-gui_warp-list.tooltip-sub",
|
|
config.cooldown_duration,
|
|
config.standard_proximity_radius,
|
|
{ "exp-gui_warp-list.tooltip-sub-current", status_icons.current },
|
|
{ "exp-gui_warp-list.tooltip-sub-connected", status_icons.connected },
|
|
{ "exp-gui_warp-list.tooltip-sub-different", status_icons.different },
|
|
{ "exp-gui_warp-list.tooltip-sub-cooldown", status_icons.cooldown },
|
|
{ "exp-gui_warp-list.tooltip-sub-not-available", status_icons.not_available },
|
|
{ "exp-gui_warp-list.tooltip-sub-bypass", status_icons.bypass },
|
|
},
|
|
})
|
|
|
|
local add_warp_button = Elements.add_warp_button(header)
|
|
add_warp_button.visible = allow_add_warp
|
|
|
|
local warp_table = Elements.warp_table(container)
|
|
local force = player.force --[[@as LuaForce]]
|
|
Elements.warp_table.rebuild(warp_table, def.get_warps(force), def.get_status(player))
|
|
|
|
local cooldown_bar = Elements.cooldown_bar(container)
|
|
Elements.cooldown_bar.refresh(cooldown_bar, def._get_player_data(player).cooldown)
|
|
|
|
local root = Gui.elements.container.get_root_element(container)
|
|
def.data[root] = {
|
|
frame = container,
|
|
add_warp_button = add_warp_button,
|
|
warp_table = warp_table,
|
|
cooldown_bar = cooldown_bar,
|
|
}
|
|
|
|
return root
|
|
end) --[[@as any]]
|
|
|
|
--- Get or create the data for a force
|
|
--- @param force LuaForce
|
|
--- @return ExpGui_WarpList.elements.container.force_data
|
|
function Elements.container._get_force_data(force)
|
|
local force_data = Elements.container.data[force]
|
|
if not force_data then
|
|
force_data = { warps = {}, next_warp_id = 1 }
|
|
Elements.container.data[force] = force_data
|
|
end
|
|
return force_data
|
|
end
|
|
|
|
--- Get or create the data for a player
|
|
--- @param player LuaPlayer
|
|
--- @return ExpGui_WarpList.elements.container.player_data
|
|
function Elements.container._get_player_data(player)
|
|
local player_data = Elements.container.data[player]
|
|
if not player_data then
|
|
player_data = { cooldown = 0, keep_open = false }
|
|
Elements.container.data[player] = player_data
|
|
end
|
|
return player_data
|
|
end
|
|
|
|
--- Get the warps of a force, the spawn warp first and then by name
|
|
--- @param force LuaForce
|
|
--- @return ExpGui_WarpList.Warp[]
|
|
function Elements.container.get_warps(force)
|
|
return Elements.container._get_force_data(force).warps
|
|
end
|
|
|
|
--- Get the spawn warp of a force
|
|
--- @param force LuaForce
|
|
--- @return ExpGui_WarpList.Warp?
|
|
function Elements.container.get_spawn_warp(force)
|
|
return Elements.container._get_force_data(force).spawn
|
|
end
|
|
|
|
--- Get what decides which warps a player can use
|
|
--- @param player LuaPlayer
|
|
--- @return ExpGui_WarpList.PlayerStatus
|
|
function Elements.container.get_status(player)
|
|
local player_data = Elements.container._get_player_data(player)
|
|
return {
|
|
in_range = player_data.in_range,
|
|
on_cooldown = player_data.cooldown > 0,
|
|
bypass_proximity = has_config_permission(player, "bypass_warp_proximity"),
|
|
}
|
|
end
|
|
|
|
--- Keep the warps of a force in display order
|
|
--- @param force_data ExpGui_WarpList.elements.container.force_data
|
|
function Elements.container._sort_warps(force_data)
|
|
local spawn = force_data.spawn
|
|
table.sort(force_data.warps, function(a, b)
|
|
if a == spawn then return true end
|
|
if b == spawn then return false end
|
|
if a.name == b.name then return a.id < b.id end
|
|
return a.name < b.name
|
|
end)
|
|
end
|
|
|
|
--- Add or update the map tag of a warp
|
|
--- @param warp ExpGui_WarpList.Warp
|
|
function Elements.container._make_tag(warp)
|
|
local tag = warp.tag
|
|
if tag and tag.valid then
|
|
tag.text = "Warp: " .. warp.name
|
|
tag.icon = warp.icon
|
|
return
|
|
end
|
|
|
|
local position = warp.position
|
|
warp.tag = warp.force.add_chart_tag(warp.surface, {
|
|
position = { x = position.x + 0.5, y = position.y + 0.5 },
|
|
text = "Warp: " .. warp.name,
|
|
icon = warp.icon,
|
|
})
|
|
end
|
|
|
|
--- Remove the map tag of a warp
|
|
--- @param warp ExpGui_WarpList.Warp
|
|
function Elements.container._remove_tag(warp)
|
|
local tag = warp.tag
|
|
if tag and tag.valid then
|
|
tag.destroy()
|
|
end
|
|
warp.tag = nil
|
|
end
|
|
|
|
--- Place the tiles and entities which mark a warp on the ground
|
|
--- @param warp ExpGui_WarpList.Warp
|
|
function Elements.container._make_area(warp)
|
|
local surface = warp.surface
|
|
local position = warp.position
|
|
warp.old_tile = surface.get_tile(position.x, position.y).name
|
|
|
|
local tiles = {}
|
|
for index, tile in ipairs(area_tiles) do
|
|
tiles[index] = { name = tile[1], position = { x = tile[2] + position.x, y = tile[3] + position.y } }
|
|
end
|
|
surface.set_tiles(tiles)
|
|
|
|
for _, entity_config in ipairs(area_entities) do
|
|
local entity = assert(surface.create_entity{
|
|
name = entity_config[1],
|
|
position = { x = entity_config[2] + position.x, y = entity_config[3] + position.y },
|
|
force = "neutral",
|
|
})
|
|
entity.destructible = false
|
|
entity.health = 0
|
|
entity.minable_flag = false
|
|
entity.rotatable = false
|
|
|
|
if entity.type == "electric-pole" then
|
|
warp.electric_pole = entity
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Restore the ground under a warp and remove its entities
|
|
--- @param warp ExpGui_WarpList.Warp
|
|
function Elements.container._remove_area(warp)
|
|
local old_tile = warp.old_tile
|
|
if not old_tile then return end
|
|
|
|
local surface = warp.surface
|
|
local position = warp.position
|
|
local tiles = {}
|
|
for index, tile in ipairs(area_tiles) do
|
|
tiles[index] = { name = old_tile, position = { x = tile[2] + position.x, y = tile[3] + position.y } }
|
|
end
|
|
surface.set_tiles(tiles)
|
|
|
|
local radius = config.standard_proximity_radius
|
|
local area = {
|
|
left_top = { x = position.x - radius, y = position.y - radius },
|
|
right_bottom = { x = position.x + radius, y = position.y + radius },
|
|
}
|
|
local entities = surface.find_entities_filtered{ force = "neutral", area = area, name = area_entity_names }
|
|
for _, entity in pairs(entities) do
|
|
if entity.destructible == false then
|
|
entity.destructible = true
|
|
entity.die(entity.force)
|
|
end
|
|
end
|
|
|
|
-- The area may not be covered by a radar
|
|
warp.force.chart(surface, area)
|
|
end
|
|
|
|
--- Add a warp for a force, when added by a player it is new until they confirm their first edit
|
|
--- @param force LuaForce
|
|
--- @param surface LuaSurface
|
|
--- @param position MapPosition.struct
|
|
--- @param player LuaPlayer?
|
|
--- @param name string?
|
|
--- @return ExpGui_WarpList.Warp
|
|
function Elements.container.add_warp(force, surface, position, player, name)
|
|
local force_data = Elements.container._get_force_data(force)
|
|
local id = force_data.next_warp_id
|
|
force_data.next_warp_id = id + 1
|
|
|
|
--- @type ExpGui_WarpList.Warp
|
|
local warp = {
|
|
id = id,
|
|
force = force,
|
|
name = name or "New warp",
|
|
icon = { type = default_icon.type, name = default_icon.name },
|
|
surface = surface,
|
|
position = { x = floor(position.x), y = floor(position.y) },
|
|
last_user = player,
|
|
last_edit_tick = game.tick,
|
|
editing = {},
|
|
}
|
|
|
|
-- Warps added by a player start in edit mode, discarding that edit removes them again
|
|
if player then
|
|
warp.new = true
|
|
Elements.container._make_area(warp)
|
|
end
|
|
|
|
force_data.warps[#force_data.warps + 1] = warp
|
|
Elements.container._sort_warps(force_data)
|
|
Elements.container._make_tag(warp)
|
|
|
|
Elements.warp_table.rebuild_force(force)
|
|
return warp
|
|
end
|
|
|
|
--- Remove a warp along with its area and map tag
|
|
--- @param warp ExpGui_WarpList.Warp
|
|
function Elements.container.remove_warp(warp)
|
|
local force_data = Elements.container._get_force_data(warp.force)
|
|
table.remove_element(force_data.warps, warp)
|
|
if force_data.spawn == warp then
|
|
force_data.spawn = nil
|
|
end
|
|
|
|
Elements.container._remove_tag(warp)
|
|
Elements.container._remove_area(warp)
|
|
|
|
-- Offline players are corrected by their next proximity update
|
|
for _, player in pairs(warp.force.connected_players) do
|
|
local player_data = Elements.container._get_player_data(player)
|
|
if player_data.in_range == warp then player_data.in_range = nil end
|
|
if player_data.blocking == warp then player_data.blocking = nil end
|
|
end
|
|
|
|
for _, warp_table in Elements.warp_table:online_elements(warp.force) do
|
|
Elements.warp_table.remove_row(warp_table, warp)
|
|
end
|
|
end
|
|
|
|
--- Save a new name and icon for a warp, which also ends the player's edit
|
|
--- @param warp ExpGui_WarpList.Warp
|
|
--- @param name string
|
|
--- @param icon SignalID
|
|
--- @param player LuaPlayer
|
|
function Elements.container.update_warp(warp, name, icon, player)
|
|
warp.name = name
|
|
warp.icon = icon
|
|
warp.last_user = player
|
|
warp.last_edit_tick = game.tick
|
|
warp.editing[player.index] = nil
|
|
warp.new = nil
|
|
|
|
Elements.container._sort_warps(Elements.container._get_force_data(warp.force))
|
|
Elements.container._make_tag(warp)
|
|
Elements.warp_table.rebuild_force(warp.force)
|
|
end
|
|
|
|
--- Start or stop a player editing a warp
|
|
--- @param warp ExpGui_WarpList.Warp
|
|
--- @param player LuaPlayer
|
|
--- @param editing boolean
|
|
function Elements.container.set_editing(warp, player, editing)
|
|
warp.editing[player.index] = editing and player.name or nil
|
|
Elements.warp_table.refresh_row_force(warp)
|
|
end
|
|
|
|
--- Make a warp the spawn of its force, the force respawns there
|
|
--- @param warp ExpGui_WarpList.Warp
|
|
function Elements.container.set_spawn_warp(warp)
|
|
local force_data = Elements.container._get_force_data(warp.force)
|
|
force_data.spawn = warp
|
|
Elements.container._sort_warps(force_data)
|
|
warp.force.set_spawn_position(warp.position, warp.surface)
|
|
end
|
|
|
|
--- Move a player to a warp and start their cooldown
|
|
--- @param player LuaPlayer
|
|
--- @param warp ExpGui_WarpList.Warp
|
|
function Elements.container.teleport_player(player, warp)
|
|
local surface = warp.surface
|
|
local position = { x = warp.position.x + 0.5, y = warp.position.y + 0.5 }
|
|
|
|
local vehicle = player.vehicle
|
|
if vehicle then
|
|
local vehicle_position = surface.find_non_colliding_position(vehicle.name, position, 32, 1)
|
|
-- Only cars can be teleported between surfaces
|
|
if vehicle.type == "car" then
|
|
vehicle.teleport(vehicle_position, surface)
|
|
elseif surface == vehicle.surface then
|
|
if not vehicle.teleport(vehicle_position) then
|
|
player.driving = false
|
|
player.teleport(surface.find_non_colliding_position("character", position, 32, 1), surface)
|
|
end
|
|
end
|
|
else
|
|
player.teleport(surface.find_non_colliding_position("character", position, 32, 1), surface)
|
|
end
|
|
|
|
local player_data = Elements.container._get_player_data(player)
|
|
if not has_config_permission(player, "bypass_warp_cooldown") then
|
|
player_data.cooldown = config.cooldown_duration * 60
|
|
Elements.cooldown_bar.refresh_player(player, player_data.cooldown)
|
|
end
|
|
player_data.in_range = warp
|
|
Elements.warp_table.refresh_status_player(player)
|
|
end
|
|
|
|
--- Refresh the parts of a player's gui which depend on their permissions
|
|
--- @param player LuaPlayer
|
|
function Elements.container.refresh_player(player)
|
|
local allow_add_warp = has_config_permission(player, "allow_add_warp")
|
|
for _, container in Elements.container:tracked_elements(player) do
|
|
local elements = Elements.container.data[container]
|
|
elements.frame.style.minimal_width = allow_add_warp and 268 or 220
|
|
elements.add_warp_button.visible = allow_add_warp
|
|
end
|
|
Elements.warp_table.rebuild_player(player)
|
|
end
|
|
|
|
--- Count down the cooldown of a player
|
|
--- @param player LuaPlayer
|
|
--- @param player_data ExpGui_WarpList.elements.container.player_data
|
|
function Elements.container._update_cooldown(player, player_data)
|
|
if player_data.cooldown == 0 then return end
|
|
local cooldown = player_data.cooldown - update_interval
|
|
if cooldown < 0 then cooldown = 0 end
|
|
player_data.cooldown = cooldown
|
|
|
|
Elements.cooldown_bar.refresh_player(player, cooldown)
|
|
if cooldown == 0 then
|
|
Elements.warp_table.refresh_status_player(player)
|
|
end
|
|
end
|
|
|
|
--- Find which warp a player is standing on and whether one can be added where they are
|
|
--- @param player LuaPlayer
|
|
--- @param player_data ExpGui_WarpList.elements.container.player_data
|
|
function Elements.container._update_proximity(player, player_data)
|
|
local force_data = Elements.container._get_force_data(player.force --[[@as LuaForce]])
|
|
local surface = player.physical_surface
|
|
local position = player.physical_position
|
|
local px, py = position.x, position.y
|
|
|
|
local closest, closest_distance = nil, nil --- @type ExpGui_WarpList.Warp?, number?
|
|
for _, warp in ipairs(force_data.warps) do
|
|
if warp.surface == surface then
|
|
local dx, dy = px - warp.position.x, py - warp.position.y
|
|
local distance = dx * dx + dy * dy
|
|
if not closest_distance or distance < closest_distance then
|
|
closest, closest_distance = warp, distance
|
|
end
|
|
end
|
|
end
|
|
|
|
local in_range = nil --- @type ExpGui_WarpList.Warp?
|
|
if closest and closest_distance < (closest == force_data.spawn and config.spawn_proximity_radius or config.standard_proximity_radius) ^ 2 then
|
|
in_range = closest
|
|
end
|
|
if in_range ~= player_data.in_range then
|
|
player_data.in_range = in_range
|
|
if not player_data.keep_open then
|
|
Gui.toolbar.set_left_element_visible_state(Elements.container, player, in_range ~= nil)
|
|
end
|
|
Elements.warp_table.refresh_status_player(player)
|
|
end
|
|
|
|
local blocking = nil --- @type ExpGui_WarpList.Warp?
|
|
if closest and closest_distance <= config.minimum_distance ^ 2 then
|
|
blocking = closest
|
|
end
|
|
if blocking ~= player_data.blocking then
|
|
player_data.blocking = blocking
|
|
Elements.add_warp_button.refresh_player_blocking(player, blocking)
|
|
end
|
|
end
|
|
|
|
--- Add the element to the left flow with a toolbar button
|
|
Gui.add_left_element(Elements.container, false)
|
|
Gui.toolbar.create_button{
|
|
name = "toggle_warp_list",
|
|
left_element = Elements.container,
|
|
sprite = default_icon.type .. "/" .. default_icon.name,
|
|
tooltip = { "exp-gui_warp-list.tooltip-main" },
|
|
visible = function(player, element)
|
|
return feature:is_enabled() and Roles.player_has_permission(player, "exp_scenario.gui.warp_list")
|
|
end
|
|
}:on_click(function(def, player)
|
|
-- A list the player opened themselves stays open when they leave a warp
|
|
Elements.container._get_player_data(player).keep_open = Gui.toolbar.get_button_toggled_state(def, player)
|
|
end)
|
|
|
|
--- Give a force a spawn warp when its first player is created
|
|
--- @param event EventData.on_player_created
|
|
local function on_player_created(event)
|
|
local player = Gui.get_player(event)
|
|
local force = player.force --[[@as LuaForce]]
|
|
if Elements.container.get_spawn_warp(force) then return end
|
|
|
|
local surface = player.physical_surface
|
|
local position = force.get_spawn_position(surface)
|
|
local warp = Elements.container.add_warp(force, surface, position, nil, "Spawn")
|
|
Elements.container.set_spawn_warp(warp)
|
|
|
|
-- Spawn has no warp area, so any pole nearby connects it to the network
|
|
local poles = surface.find_entities_filtered{ type = "electric-pole", position = position, radius = 20, limit = 1 }
|
|
warp.electric_pole = poles[1]
|
|
end
|
|
|
|
--- Rebuild the gui for a player as it may be outdated
|
|
--- @param event EventData.on_player_joined_game | EventData.on_player_changed_force
|
|
local function refresh_player_warps(event)
|
|
local player = Gui.get_player(event)
|
|
Elements.container.refresh_player(player)
|
|
Elements.cooldown_bar.refresh_player(player, Elements.container._get_player_data(player).cooldown)
|
|
end
|
|
|
|
--- Rebuild the gui for a player as their permissions may have changed
|
|
--- @param event EventData.ExpRoles.on_player_roles_changed
|
|
local function refresh_player_permissions(event)
|
|
Elements.container.refresh_player(Gui.get_player(event))
|
|
end
|
|
|
|
--- Restore the map tag of a warp when a player edits or removes it
|
|
--- @param event EventData.on_chart_tag_modified | EventData.on_chart_tag_removed
|
|
local function on_chart_tag_changed(event)
|
|
if not event.player_index then return end
|
|
for _, warp in ipairs(Elements.container.get_warps(event.force)) do
|
|
local tag = warp.tag
|
|
if not tag or not tag.valid or tag == event.tag then
|
|
if event.name == defines.events.on_chart_tag_removed then
|
|
warp.tag = nil
|
|
end
|
|
Elements.container._make_tag(warp)
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Count down cooldowns and check where each player is
|
|
local function update_players()
|
|
for _, player in pairs(game.connected_players) do
|
|
local player_data = Elements.container._get_player_data(player)
|
|
Elements.container._update_cooldown(player, player_data)
|
|
Elements.container._update_proximity(player, player_data)
|
|
end
|
|
end
|
|
|
|
local e = defines.events
|
|
|
|
return feature:guard{
|
|
elements = Elements,
|
|
events = {
|
|
[e.on_player_created] = on_player_created,
|
|
[e.on_player_joined_game] = refresh_player_warps,
|
|
[e.on_player_changed_force] = refresh_player_warps,
|
|
[e.on_chart_tag_modified] = on_chart_tag_changed,
|
|
[e.on_chart_tag_removed] = on_chart_tag_changed,
|
|
[Roles.events.on_player_roles_changed] = refresh_player_permissions,
|
|
},
|
|
on_nth_tick = {
|
|
[update_interval] = update_players,
|
|
}
|
|
}
|