diff --git a/config/gui/warps.lua b/config/gui/warps.lua index 98e8f627..29e8075d 100644 --- a/config/gui/warps.lua +++ b/config/gui/warps.lua @@ -29,13 +29,24 @@ return { -- Warp area generation entities = { --- @setting entities The entities which are created for warp areas - {'small-lamp',-3,-2},{'small-lamp',-3,2},{'small-lamp',3,-2},{'small-lamp',3,2}, - {'small-lamp',-2,-3},{'small-lamp',2,-3},{'small-lamp',-2,3},{'small-lamp',2,3}, - {'small-electric-pole',-3,-3},{'small-electric-pole',3,3},{'small-electric-pole',-3,3},{'small-electric-pole',3,-3} + {'small-lamp', -4, -2}, {'small-lamp', -2, -4}, {'small-electric-pole',-3,-3}, -- Top left corner + {'small-lamp', 3, -2}, {'small-lamp', 1, -4}, {'small-electric-pole',2,-3}, -- Top right corner + {'small-lamp', 3, 1}, {'small-lamp', 1, 3}, {'small-electric-pole',2,2}, -- Bottom right corner + {'small-lamp', -4, 1}, {'small-lamp', -2, 3}, {'small-electric-pole',-3,2}, -- Bottom left corner }, - base_tile = 'tutorial-grid', --- @setting base_tile The tile which is used for the warp areas tiles = { --- @setting tiles The tiles which are created for warp areas - {-3,-2},{-3,-1},{-3,0},{-3,1},{-3,2},{3,-2},{3,-1},{3,0},{3,1},{3,2}, - {-2,-3},{-1,-3},{0,-3},{1,-3},{2,-3},{-2,3},{-1,3},{0,3},{1,3},{2,3} + {"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} } -} \ No newline at end of file +} diff --git a/locale/en/gui.cfg b/locale/en/gui.cfg index 7f3fba73..d6385837 100644 --- a/locale/en/gui.cfg +++ b/locale/en/gui.cfg @@ -91,20 +91,34 @@ invalid=Autofill set to maximum amount: __1__ __2__ for __3__ inserted=Inserted __1__ __2__ into __3__ [warp-list] -main-caption=Warp List +main-caption=Warp List [img=info] main-tooltip=Warp List; Must be within __1__ tiles to use -sub-tooltip=Warps can only be used every __1__ seconds and when within __2__ tiles -too-close=Can't make warp; too close to warp: __1__ +sub-tooltip=Warps can only be used every __1__ seconds and when within __2__ tiles\n__3__\n__4__\n__5__\n__6__\n__7__\n__8__ +sub-tooltip-current= - __1__ This is your current warp point; +sub-tooltip-connected= - __1__ You can travel to this warp point; +sub-tooltip-different= - __1__ This warp is on a different network; +sub-tooltip-cooldown= - __1__ You are currently on cooldown; +sub-tooltip-not_available= - __1__ You are not in range of a warp point; +sub-tooltip-bypass= - __1__ Your role allows you to travel here; +too-close=Cannot create warp; too close to existing warp point: __1__ +too-close-to-water=Cannot create warp; too close to water, please move __1__ tiles away from the water body or landfill it +too-close-to-entities=Cannot create warp; too close to other entities, please move __1__ tiles away from the entities or remove them last-edit=Last edited by __1__ at __2__\nClick to view on map add-tooltip=Add new warp confirm-tooltip=Save changes cancel-tooltip=Discard changes edit-tooltip=Currently being edited by: __1__ edit-tooltip-none=Currently being edited by: Nobody -discard-tooltip=Remove warp -timer-tooltip=Warp cooldown takes __1__ seconds +remove-tooltip=Remove warp +timer-tooltip=Please wait __1__ seconds before you can warp +timer-tooltip-zero=After each warp you will need to wait __1__ seconds before you can warp again goto-tooltip=Go to x __1__ y __2__ -goto-disabled=You must be on a warp point and have a full charge to warp +goto-bypass=Go to x __1__ y __2__, bypass mode +goto-bypass-different-network=Go to x __1__ y __2__, bypass mode different network +goto-same-warp=You are already on this warp +goto-different-network=This warp is on a different network, use power poles to connect it +goto-cooldown=You are on cooldown, wait for your cooldown to recharge +goto-disabled=You are not on a warp point, walk to a warp point goto-edit=Edit warp icon [readme] diff --git a/modules/control/warps.lua b/modules/control/warps.lua index 6baf9602..16b232a6 100644 --- a/modules/control/warps.lua +++ b/modules/control/warps.lua @@ -37,9 +37,16 @@ Global.register(force_warps, function(tbl) force_warps = tbl end) --- When a warp is updated change its chat tag and resort the warp order +-- Create an array of entity names that will be added to the remove filter +local remove_warp_area_entity_names = {} +for _, entity in pairs(config.entities) do + table.insert(remove_warp_area_entity_names, entity[1]) +end + +-- When a warp is updated change its chat tag and restore the warp order WrapData:on_update(function(warp_id, warp, old_warp) if warp then + warp.updates = warp.updates + 1 -- Update the map chart tag if there is one if warp.tag then Warps.make_warp_tag(warp_id) @@ -148,31 +155,15 @@ function Warps.make_warp_area(warp_id) local position = warp.position local posx = position.x local posy = position.y - local radius = config.standard_proximity_radius - local radius2 = radius^2 - -- Get the tile that is being replaced, store.update not needed as we dont want it to trigger + -- Get the tile that is being replaced, store.update not needed as we don't want it to trigger local old_tile = surface.get_tile(position).name warp.old_tile = old_tile - -- Make a circle that acts as a base for the warp structure - local base_tile = config.base_tile - local base_tiles = {} - for x = -radius, radius do - local x2 = x^2 - for y = -radius, radius do - local y2 = y^2 - if x2+y2 < radius2 then - table.insert(base_tiles, {name=base_tile, position={x+posx, y+posy}}) - end - end - end - surface.set_tiles(base_tiles) - -- Add a tile pattern on top of the base local tiles = {} - for _, pos in pairs(config.tiles) do - table.insert(tiles, {name=base_tile, position={pos[1]+posx, pos[2]+posy}}) + for _, tile in pairs(config.tiles) do + table.insert(tiles, {name=tile[1], position={tile[2]+posx, tile[3]+posy}}) end surface.set_tiles(tiles) @@ -187,6 +178,11 @@ function Warps.make_warp_area(warp_id) entity.health = 0 entity.minable = false entity.rotatable = false + + -- Save reference of the last power pole + if entity.type == 'electric-pole' then + warp.electric_pole = entity + end end end @@ -202,22 +198,15 @@ function Warps.remove_warp_area(warp_id) local position = warp.position local surface = warp.surface local radius = config.standard_proximity_radius - local radius2 = radius^2 -- Check that a warp area was created previously - local base_tile = warp.old_tile - if not base_tile then return end + local old_tile = warp.old_tile + if not old_tile then return end - -- Reset all the tiles that were replaced + -- Restore the original tiles before the creation of the warp local tiles = {} - for x = -radius, radius do - local x2 = x^2 - for y = -radius, radius do - local y2 = y^2 - if x2+y2 < radius2 then - table.insert(tiles, {name=base_tile, position={x+position.x, y+position.y}}) - end - end + for _, tile in pairs(config.tiles) do + table.insert(tiles, {name=old_tile, position={tile[2]+position.x, tile[3]+position.y}}) end surface.set_tiles(tiles) @@ -226,11 +215,17 @@ function Warps.remove_warp_area(warp_id) {position.x+radius, position.y+radius} } - -- Remove all the entities that are in the area - local entities = surface.find_entities_filtered{ force='neutral', area=area } - for _, entity in pairs(entities) do if entity and entity.valid and entity.name ~= 'player' then entity.destroy() end end + -- Remove warp structure entities + local entities = surface.find_entities_filtered{ force='neutral', area=area, name = remove_warp_area_entity_names } + for _, entity in pairs(entities) do + -- Destroy them, this will leave corpses of the entities that it destroyed. + if entity and entity.valid and entity.destructible == false then + entity.destructible = true + entity.die(entity.force) + end + end - -- Rechart map area, usefull if warp is not covered by a radar + -- Rechart map area, useful if warp is not covered by a radar game.forces[warp.force_name].chart(surface, area) end @@ -351,7 +346,8 @@ function Warps.add_warp(force_name, surface, position, player_name, warp_name) }, last_edit_name = player_name or '', last_edit_time = game.tick, - currently_editing = editing + currently_editing = editing, + updates = 0, }) return warp_id @@ -385,6 +381,11 @@ Warps.update_warp(warp_id, 'My Warp', 'iron-plate', game.player.name) ]] function Warps.update_warp(warp_id, new_name, new_icon, player_name) WrapData:update(warp_id, function(_, warp) + -- If the icon is not valid then replace with the old icon + if new_icon and not new_icon.name or not new_icon.type then + new_icon = warp.icon + end + warp.last_edit_name = player_name or '' warp.last_edit_time = game.tick warp.name = new_name or warp.name @@ -476,4 +477,4 @@ function Warps.get_editing(warp_id, player_name) end -- Module return -return Warps \ No newline at end of file +return Warps diff --git a/modules/gui/warp-list.lua b/modules/gui/warp-list.lua index 699b46f0..7f73bde5 100644 --- a/modules/gui/warp-list.lua +++ b/modules/gui/warp-list.lua @@ -12,7 +12,7 @@ local Roles = require 'expcore.roles' --- @dep expcore.roles local Colors = require 'utils.color_presets' --- @dep utils.color_presets local config = require 'config.gui.warps' --- @dep config.gui.warps local Warps = require 'modules.control.warps' --- @dep modules.control.warps -local format_time = _C.format_time --- @dep expcore.common +local format_time, player_return = _C.format_time, _C.player_return --- @dep expcore.common --- Stores all data for the warp gui local WrapGuiData = Datastore.connect('WrapGuiData') @@ -30,10 +30,18 @@ end) --- Styles used for sprite buttons local Styles = { - sprite20 = Gui.sprite_style(20), - sprite22 = Gui.sprite_style(20, nil, { right_margin = -3 }), + sprite22 = { height = 22, width = 22, padding = -2 }, sprite32 = { height = 32, width = 32, left_margin = 1 } } +--- Status icon of a warp +local warp_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]', +} --- Returns if a player is allowed to edit the given warp --- If a player is allowed to use the edit buttons @@ -73,180 +81,73 @@ Gui.element{ type = 'sprite-button', sprite = 'utility/add', tooltip = {'warp-list.add-tooltip'}, - style = 'tool_button' + style = 'shortcut_bar_button' } -:style(Styles.sprite20) +:style(Styles.sprite22) :on_click(function(player, _) -- Add the new warp local force_name = player.force.name local surface = player.surface local position = player.position + + -- Check if the warp is too close to water + 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_return({'expcore-commands.command-fail', {'warp-list.too-close-to-water', config.standard_proximity_radius + 1}}, 'orange_red', player) + if game.player then game.player.play_sound{ path = 'utility/wire_pickup' } end + for _, tile in pairs(water_tiles) do + rendering.draw_sprite{ + sprite = 'utility/rail_path_not_possible', + x_scale = 0.5, + y_scale = 0.5, + target = tile.position, + surface = surface, + players = {player}, + time_to_live = 60 + } + end + return + end + + -- Check if there are player entities in the way (has a bigger radius because the enities that can be placed by a player are larger) + local entities = surface.find_entities_filtered{ + radius = config.standard_proximity_radius + 4.5, + position = position, + collision_mask = { + 'item-layer', 'object-layer', 'player-layer', 'water-tile' + } + } + -- Remove 1 because that is the current player + if #entities > 1 then + player_return({'expcore-commands.command-fail', {'warp-list.too-close-to-entities', config.standard_proximity_radius + 4.5}}, 'orange_red', player) + if game.player then game.player.play_sound{path='utility/wire_pickup'} end + local character = player.character + for _, entity in pairs(entities) do + if entity ~= character then + rendering.draw_sprite{ + sprite = 'utility/rail_path_not_possible', + x_scale = 0.5, + y_scale = 0.5, + target = entity, + surface = surface, + players = {player}, + time_to_live = 60 + } + end + end + return + end + + -- Create the warp local warp_id = Warps.add_warp(force_name, surface, position, player.name) Warps.make_warp_tag(warp_id) Warps.make_warp_area(warp_id) end) ---- Removes a warp from the list, including the physical area and map tag --- @element discard_warp -local discard_warp = -Gui.element{ - type = 'sprite-button', - sprite = 'utility/trash', - tooltip = {'warp-list.discard-tooltip'}, - style = 'tool_button' -} -:style(Styles.sprite20) -:on_click(function(_, element) - local warp_id = element.parent.name:sub(6) - Warps.remove_warp(warp_id) -end) ---- Opens edit mode for the warp --- @element edit_warp -local edit_warp = -Gui.element{ - type = 'sprite-button', - sprite = 'utility/rename_icon_normal', - tooltip = {'warp-list.edit-tooltip-none'}, - style = 'tool_button' -} -:style(Styles.sprite20) -:on_click(function(player, element) - local warp_id = element.parent.name:sub(6) - Warps.set_editing(warp_id, player.name, true) -end) - ---- Set of three elements which make up each row of the warp table --- @element add_warp_base -local add_warp_base = -Gui.element(function(_, parent, warp_id) - -- Add the icon flow - local icon_flow = - parent.add{ - name = 'icon-'..warp_id, - type = 'flow', - caption = warp_id - } - icon_flow.style.padding = 0 - - -- Add a flow which will contain the warp name and edit buttons - local warp_flow = parent.add{ type = 'flow', name = warp_id } - warp_flow.style.padding = 0 - - -- Add the two edit buttons outside the warp flow - local edit_flow = Gui.alignment(parent, 'edit-'..warp_id) - edit_warp(edit_flow) - discard_warp(edit_flow) - - -- Return the warp flow as the main element - return warp_flow -end) - --- Removes the three elements that are added as part of the warp base -local function remove_warp_base(parent, warp_id) - Gui.destroy_if_valid(parent['icon-'..warp_id]) - Gui.destroy_if_valid(parent['edit-'..warp_id]) - Gui.destroy_if_valid(parent[warp_id]) -end - ---- Confirms the edit to name or icon of the warp --- @element confirm_edit -local warp_editing -local warp_icon_button -local confirm_edit = -Gui.element{ - type = 'sprite-button', - sprite = 'utility/confirm_slot', - tooltip = {'warp-list.confirm-tooltip'}, - style = 'shortcut_bar_button_green' -} -:style(Styles.sprite22) -:on_click(function(player, element) - local warp_id = element.parent.name - local warp_name = element.parent[warp_editing.name].text - local warp_icon = element.parent.parent['icon-'..warp_id][warp_icon_button.name].elem_value - Warps.set_editing(warp_id, player.name) - Warps.update_warp(warp_id, warp_name, warp_icon, player.name) -end) - ---- Cancels the editing changes of the selected warp name or icon --- @element cancel_edit -local cancel_edit = -Gui.element{ - type = 'sprite-button', - sprite = 'utility/close_black', - tooltip = {'warp-list.cancel-tooltip'}, - style = 'shortcut_bar_button_red' -} -:style(Styles.sprite22) -:on_click(function(player, element) - local warp_id = element.parent.name - Warps.set_editing(warp_id, player.name) -end) - ---- Editing state for a warp, contains a text field and the two edit buttons --- @element warp_editing -warp_editing = -Gui.element(function(event_trigger, parent, warp) - local name = warp.name - - -- Draw the element - local element = - parent.add{ - name = event_trigger, - type = 'textfield', - text = name, - clear_and_focus_on_right_click = true - } - - -- Add the edit buttons - cancel_edit(parent) - confirm_edit(parent) - - -- Return the element - return element -end) -:style{ - maximal_width = 110, - height = 20 -} -:on_confirmed(function(player, element, _) - local warp_id = element.parent.name - local warp_name = element.text - local warp_icon = element.parent.parent['icon-'..warp_id][warp_icon_button.name].elem_value - Warps.set_editing(warp_id, player.name) - Warps.update_warp(warp_id, warp_name, warp_icon, player.name) -end) - ---- Default state for a warp, contains only a label with the warp name --- @element warp_label -local warp_label = -Gui.element(function(event_trigger, parent, warp) - local last_edit_name = warp.last_edit_name - local last_edit_time = warp.last_edit_time - -- Draw the element - return parent.add{ - name = event_trigger, - type = 'label', - caption = warp.name, - tooltip = {'warp-list.last-edit', last_edit_name, format_time(last_edit_time)} - } -end) -:style{ - single_line = false, - maximal_width = 150 -} -:on_click(function(player, element, _) - local warp_id = element.parent.name - local warp = Warps.get_warp(warp_id) - local position = warp.position - player.zoom_to_world(position, 1.5) -end) - -local update_wrap_buttons ---- Default state for the warp icon, when pressed teleports the player +--- Warp icon button, this will trigger a warp when the player is able to -- @element warp_icon_button -warp_icon_button = +local warp_icon_button = Gui.element(function(event_trigger, parent, warp) local warp_position = warp.position @@ -273,17 +174,18 @@ end) -- Reset the warp cooldown if the player does not have unlimited warps if not check_player_permissions(player, 'bypass_warp_cooldown') then - PlayerCooldown:set(player, config.cooldown_duration) - update_wrap_buttons(player) + PlayerCooldown:set(player, config.update_smoothing*config.cooldown_duration) end + + PlayerInRange:set(player, warp_id) end) ---- Editing state for the warp icon, chose elem used to chosse icon +--- The button that is visible when the warp is in edit state -- @element warp_icon_editing local warp_icon_editing = -Gui.element(function(_, parent, warp) +Gui.element(function(event_trigger, parent, warp) return parent.add{ - name = warp_icon_button.name, + name = event_trigger, type = 'choose-elem-button', elem_type = 'signal', signal = {type = warp.icon.type, name = warp.icon.name}, @@ -292,12 +194,215 @@ Gui.element(function(_, parent, warp) end) :style(Styles.sprite32) +--- Warp label, visible if the player is not in edit state +-- @element warp_label +local warp_label = +Gui.element(function(event_trigger, parent, warp) + local last_edit_name = warp.last_edit_name + local last_edit_time = warp.last_edit_time + -- Draw the element + return parent.add{ + name = event_trigger, + type = 'label', + caption = warp.name, + tooltip = {'warp-list.last-edit', last_edit_name, format_time(last_edit_time)} + } +end) +:style{ + single_line = true, + left_padding = 2, + right_padding = 2, + horizontally_stretchable = true +} +:on_click(function(player, element, _) + local warp_id = element.parent.caption + local warp = Warps.get_warp(warp_id) + local position = warp.position + player.zoom_to_world(position, 1.5) +end) + +--- Warp status, visible if the player is not in edit state +--- This will show if the warp is connected or not +-- @element warp_status +local warp_status = +Gui.element(function(event_trigger, parent) + -- Draw the element + return parent.add{ + name = event_trigger, + type = 'label', + caption = '[img=utility/electricity_icon_unplugged]', -- Temporary icon + } +end) +:style{ + -- When editing mode because textbox is larger the icon would move up. + top_padding = 1, + single_line = false, +} + +--- Warp textfield, visible if the player is in edit state +-- @element warp_textfield +local warp_textfield = +Gui.element(function(event_trigger, parent, warp) + -- Draw the element + return parent.add{ + name = event_trigger, + type = 'textfield', + text = warp.name, + clear_and_focus_on_right_click = true + } +end) +:style{ + -- Required fields to make it squashable and strechable. + minimal_width = 10, + maximal_width = 300, + horizontally_squashable = "on", + horizontally_stretchable = "on", + -- Other styling + height = 22, + padding = -2, + left_margin = 2, + right_margin = 2, +} +:on_confirmed(function(player, element, _) + local warp_id = element.parent.caption + local warp_name = element.text + local warp_icon = element.parent.parent['icon-'..warp_id][warp_icon_editing.name].elem_value + Warps.set_editing(warp_id, player.name) + Warps.update_warp(warp_id, warp_name, warp_icon, player.name) +end) + + +--- Confirms the edit to name or icon of the warp +-- @element confirm_edit_button +local confirm_edit_button = +Gui.element{ + type = 'sprite-button', + sprite = 'utility/confirm_slot', + tooltip = {'warp-list.confirm-tooltip'}, + style = 'shortcut_bar_button_green' +} +:style(Styles.sprite22) +:on_click(function(player, element) + local warp_id = element.parent.caption + local warp_name = element.parent.parent['name-'..warp_id][warp_textfield.name].text + local warp_icon = element.parent.parent['icon-'..warp_id][warp_icon_editing.name].elem_value + Warps.set_editing(warp_id, player.name) + Warps.update_warp(warp_id, warp_name, warp_icon, player.name) +end) + +--- Cancels the editing changes of the selected warp name or icon +-- @element cancel_edit_button +local cancel_edit_button = +Gui.element{ + type = 'sprite-button', + sprite = 'utility/close_black', + tooltip = {'warp-list.cancel-tooltip'}, + style = 'shortcut_bar_button_red' +} +:style(Styles.sprite22) +:on_click(function(player, element) + local warp_id = element.parent.caption + -- Check if this is the first edit, if so remove the warp. + local warp = Warps.get_warp(warp_id) + if warp.updates == 1 then + Warps.remove_warp(warp_id) + return + end + Warps.set_editing(warp_id, player.name) +end) + +--- Removes a warp from the list, including the physical area and map tag +-- @element remove_warp_button +local remove_warp_button = +Gui.element{ + type = 'sprite-button', + sprite = 'utility/trash', + tooltip = {'warp-list.remove-tooltip'}, + style = 'shortcut_bar_button_red' +} +:style(Styles.sprite22) +:on_click(function(_, element) + local warp_id = element.parent.caption + Warps.remove_warp(warp_id) +end) + +--- Opens edit mode for the warp +-- @element edit_warp_button +local edit_warp_button = +Gui.element{ + type = 'sprite-button', + sprite = 'utility/rename_icon_normal', + tooltip = {'warp-list.edit-tooltip-none'}, + style = 'shortcut_bar_button' +} +:style(Styles.sprite22) +:on_click(function(player, element) + local warp_id = element.parent.caption + Warps.set_editing(warp_id, player.name, true) +end) + +local update_all_warp_elements +--- Set of three elements which make up each row of the warp table +-- @element add_warp_elements +local add_warp_elements = +Gui.element(function(_, parent, warp) + -- Add icon flow, this will contain the warp button and warp icon edit button + local icon_flow = parent.add{ + name = 'icon-'..warp.warp_id, + type = 'flow', + caption = warp.warp_id + } + icon_flow.style.padding = 0 + + -- Add the button and the icon edit button + warp_icon_button(icon_flow, warp) + warp_icon_editing(icon_flow, warp) + + -- Add name flow, this will contain the warp label and textbox + local name_flow = parent.add{ + type = 'flow', + name = 'name-'..warp.warp_id, + caption = warp.warp_id + } + name_flow.style.padding = 0 + + -- Add the label and textfield of the warp + warp_status(name_flow) + warp_label(name_flow, warp) + warp_textfield(name_flow, warp) + + + -- Add button flow, this will contain buttons to manage this specific warp + local button_flow = parent.add{ + type = 'flow', + name = 'button-'..warp.warp_id, + caption = warp.warp_id + } + button_flow.style.padding = 0 + + -- Add both edit state buttons + confirm_edit_button(button_flow) + cancel_edit_button(button_flow) + edit_warp_button(button_flow) + remove_warp_button(button_flow) + + -- Return the warp flow elements + return { icon_flow, name_flow, button_flow } +end) + +-- Removes the three elements that are added as part of the warp base +local function remove_warp_elements(parent, warp_id) + Gui.destroy_if_valid(parent['icon-'..warp_id]) + Gui.destroy_if_valid(parent['name-'..warp_id]) + Gui.destroy_if_valid(parent['button-'..warp_id]) +end + --- This timer controls when a player is able to warp, eg every 60 seconds -- @element warp_timer local warp_timer = Gui.element{ type = 'progressbar', - tooltip = {'warp-list.timer-tooltip', config.cooldown_duration}, + tooltip = {'warp-list.timer-tooltip-zero', config.cooldown_duration}, minimum_value = 0, maximum_value = config.cooldown_duration*config.update_smoothing } @@ -307,30 +412,97 @@ Gui.element{ } local warp_list_container + +-- Helper function to style and enable or disable a button element +local function update_warp_elements(element, warp, warp_player_is_on, on_cooldown, bypass_warp_proximity) + -- Check if button element is valid + if not element or not element.valid then return end + + local label_style = element.parent.parent['name-'..warp.warp_id][warp_label.name].style + local warp_status_element = element.parent.parent['name-'..warp.warp_id][warp_status.name] + + -- If player is not on a warp + if not warp_player_is_on then + -- If player is allowed to warp without being on a warp. If not then disable the warp location + if bypass_warp_proximity then + local position = warp.position + element.tooltip = {'warp-list.goto-bypass', position.x, position.y} + element.enabled = true + warp_status_element.tooltip = {'warp-list.goto-bypass', position.x, position.y} + warp_status_element.caption = warp_status_icons.bypass + label_style.font = 'default-semibold' + else + element.tooltip = {'warp-list.goto-disabled'} + element.enabled = false + warp_status_element.tooltip = {'warp-list.goto-disabled'} + warp_status_element.caption = warp_status_icons.not_available + label_style.font = 'default' + end + -- If player is on the warp that is being updated + elseif warp_player_is_on.warp_id == warp.warp_id then + element.tooltip = {'warp-list.goto-same-warp'} + element.enabled = false + warp_status_element.tooltip = {'warp-list.goto-same-warp'} + warp_status_element.caption = warp_status_icons.current + label_style.font = 'default' + -- If player is on cooldown + elseif on_cooldown then + element.tooltip = {'warp-list.goto-cooldown'} + element.enabled = false + warp_status_element.tooltip = {'warp-list.goto-cooldown'} + warp_status_element.caption = warp_status_icons.cooldown + label_style.font = 'default' + else + -- If the warp the player is standing on is the same as the warp that is being updated + local warp_electric_network_id = warp.electric_pole and warp.electric_pole.electric_network_id or -1 + local player_warp_electric_network_id = warp_player_is_on.electric_pole and warp_player_is_on.electric_pole.electric_network_id or -2 + if warp_electric_network_id == player_warp_electric_network_id then + local position = warp.position + element.tooltip = {'warp-list.goto-tooltip', position.x, position.y} + element.enabled = true + warp_status_element.tooltip = {'warp-list.goto-tooltip', position.x, position.y} + warp_status_element.caption = warp_status_icons.connected + label_style.font = 'default-semibold' + -- If the warp is not on the same network but the player is allowed to warp without being on a warp + elseif bypass_warp_proximity then + local position = warp.position + element.tooltip = {'warp-list.goto-bypass-different-network', position.x, position.y} + element.enabled = true + warp_status_element.tooltip = {'warp-list.goto-bypass-different-network', position.x, position.y} + warp_status_element.caption = warp_status_icons.bypass + label_style.font = 'default-semibold' + -- If the warp is on a different network than the one the player is standing on + else + element.tooltip = {'warp-list.goto-different-network'} + element.enabled = false + warp_status_element.tooltip = {'warp-list.goto-different-network'} + warp_status_element.caption = warp_status_icons.different + label_style.font = 'default' + end + end +end + --- Update the warp buttons for a player -function update_wrap_buttons(player, timer, in_range) +function update_all_warp_elements(player, timer, warp_id) -- Get the warp table local frame = Gui.get_left_element(player, warp_list_container) local scroll_table = frame.container.scroll.table - -- Check if the buttons should be active + -- Check if the player is currenty on cooldown timer = timer or PlayerCooldown:get(player) - in_range = in_range or PlayerInRange:get(player) - local button_disabled = timer > 0 or not in_range + local on_cooldown = timer > 0 + -- Get the warp the player is on + warp_id = warp_id or PlayerInRange:get(player) + local warp_player_is_on = warp_id and Warps.get_warp(warp_id) or nil + -- Check player permission + local bypass_warp_proximity = check_player_permissions(player, 'bypass_warp_proximity') -- Change the enabled state of the warp buttons local warp_ids = Warps.get_force_warp_ids(player.force.name) - for _, warp_id in pairs(warp_ids) do - local element = scroll_table['icon-'..warp_id][warp_icon_button.name] - if element and element.valid then - element.enabled = not button_disabled - if button_disabled then - element.tooltip = {'warp-list.goto-disabled'} - else - local position = Warps.get_warp(warp_id).position - element.tooltip = {'warp-list.goto-tooltip', position.x, position.y} - end - end + for _, next_warp_id in pairs(warp_ids) do + local element = scroll_table['icon-'..next_warp_id][warp_icon_button.name] + local next_warp = Warps.get_warp(next_warp_id) + update_warp_elements(element, next_warp, warp_player_is_on, on_cooldown, bypass_warp_proximity) end end @@ -338,25 +510,39 @@ end local function update_warp(player, warp_table, warp_id) local warp = Warps.get_warp(warp_id) - -- Warp no longer exists so should be removed from the list + -- If the warp no longer exists then remove the warp elements from the warp table if not warp then - remove_warp_base(warp_table, warp_id) + remove_warp_elements(warp_table, warp_id) return end - -- Get the warp flow for this warp - local warp_flow = warp_table[warp_id] or add_warp_base(warp_table, warp_id) + -- Create the warp elements if they do not already exist + if not warp_table['icon-'..warp_id] then + add_warp_elements(warp_table, warp) + end local icon_flow = warp_table['icon-'..warp_id] + local name_flow = warp_table['name-'..warp_id] + local button_flow = warp_table['button-'..warp_id] - -- Update the edit flow - local edit_flow = warp_table['edit-'..warp_id] + -- Create local references to the elements for this warp + local warp_icon_element = icon_flow[warp_icon_button.name] + local warp_icon_edit_element = icon_flow[warp_icon_editing.name] + + local label_element = name_flow[warp_label.name] + local textfield_element = name_flow[warp_textfield.name] + + local cancel_edit_element = button_flow[cancel_edit_button.name] + local confirm_edit_element = button_flow[confirm_edit_button.name] + + local edit_warp_element = button_flow[edit_warp_button.name] + local remove_warp_element = button_flow[remove_warp_button.name] + + -- Hide the edit button if the player is not allowed to edit the warp local player_allowed_edit = check_player_permissions(player, 'allow_edit_warp', warp) local players_editing = table.get_keys(warp.currently_editing) - local edit_warp_element = edit_flow[edit_warp.name] - local discard_warp_element = edit_flow[discard_warp.name] - edit_warp_element.visible = player_allowed_edit - discard_warp_element.visible = player_allowed_edit + + -- Set the tooltip of the edit button if #players_editing > 0 then edit_warp_element.hovered_sprite = 'utility/warning_icon' edit_warp_element.tooltip = {'warp-list.edit-tooltip', table.concat(players_editing, ', ')} @@ -365,54 +551,53 @@ local function update_warp(player, warp_table, warp_id) edit_warp_element.tooltip = {'warp-list.edit-tooltip-none'} end - -- Check if the player is was editing and/or currently editing - local warp_label_element = warp_flow[warp_label.name] or warp_label(warp_flow, warp) - local icon_entry = icon_flow[warp_icon_button.name] or warp_icon_button(icon_flow, warp) - local player_was_editing = icon_entry.type == 'choose-elem-button' + -- Set the visibility of the warp elements based on whether the user is editing or not local player_is_editing = warp.currently_editing[player.name] - - -- Update the warp and icon flow - if not player_was_editing and not player_is_editing then - -- Update the warp name label and icon - local warp_name = warp.name - local warp_icon = warp.icon - local last_edit_name = warp.last_edit_name - local last_edit_time = warp.last_edit_time - warp_label_element.caption = warp_name - warp_label_element.tooltip = {'warp-list.last-edit', last_edit_name, format_time(last_edit_time)} + if player_is_editing then + -- Set the icon elements visibility + warp_icon_element.visible = false + warp_icon_edit_element.visible = true + -- Set the name elements visibility + label_element.visible = false + textfield_element.visible = true + textfield_element.focus() + warp_table.parent.scroll_to_element(textfield_element, 'top-third') + -- Set the edit buttons + cancel_edit_element.visible = true + confirm_edit_element.visible = true + -- Set the warp buttons + edit_warp_element.visible = false + remove_warp_element.visible = false + else + -- Set the icon elements visibility + warp_icon_element.visible = true -- The SpritePath type is not the same as the SignalID type - local sprite = warp_icon.type .. '/' ..warp_icon.name - if warp_icon.type == 'virtual' then - sprite = 'virtual-signal/' ..warp_icon.name + local sprite = warp.icon.type .. '/' ..warp.icon.name + if warp.icon.type == 'virtual' then + sprite = 'virtual-signal/' ..warp.icon.name end - icon_entry.sprite = sprite - - elseif player_was_editing and not player_is_editing then - -- Player was editing but is no longer, remove text field and add label - edit_warp_element.enabled = true - warp_flow.clear() - warp_label(warp_flow, warp) - - icon_flow.clear() - local warp_icon_element = warp_icon_button(icon_flow, warp) - local timer = PlayerCooldown:get(player) - local in_range = PlayerInRange:get(player) - local apply_proximity = not check_player_permissions(player, 'bypass_warp_proximity') - if timer > 0 or (apply_proximity and not in_range) then - warp_icon_element.enabled = false - warp_icon_element.tooltip = {'warp-list.goto-disabled'} - end - - elseif not player_was_editing and player_is_editing then - -- Player was not editing but now is, remove label and add text field - edit_warp_element.enabled = false - warp_flow.clear() - warp_editing(warp_flow, warp).focus() - warp_table.parent.scroll_to_element(warp_flow, 'top-third') - icon_flow.clear() - warp_icon_editing(icon_flow, warp) - + warp_icon_element.sprite = sprite + -- Set icon edit to the warps icon + warp_icon_edit_element.elem_value = warp.icon + warp_icon_edit_element.visible = false + -- Set the name elements visibility + label_element.visible = true + label_element.caption = warp.name + textfield_element.visible = false + textfield_element.text = warp.name + -- Set the edit buttons + cancel_edit_element.visible = false + confirm_edit_element.visible = false + -- Set the warp buttons + edit_warp_element.visible = true and player_allowed_edit + remove_warp_element.visible = true and player_allowed_edit end + + local timer = PlayerCooldown:get(player) + local current_warp_id = PlayerInRange:get(player) + local to_warp = current_warp_id and Warps.get_warp(current_warp_id) or nil + local bypass_warp_proximity = check_player_permissions(player, 'bypass_warp_proximity') + update_warp_elements(warp_icon_element, warp, to_warp, timer > 0, bypass_warp_proximity) end -- Update all the warps for a player @@ -425,13 +610,12 @@ local function update_all_warps(player, warp_table) end -- Update all warps for all players on a force -local function update_all_wrap_force(force) +local function update_all_warp_force(force) local warp_ids = Warps.get_force_warp_ids(force.name) for _, player in pairs(force.connected_players) do local frame = Gui.get_left_element(player, warp_list_container) local warp_table = frame.container.scroll.table - warp_table.clear() for _, warp_id in ipairs(warp_ids) do update_warp(player, warp_table, warp_id) end @@ -442,24 +626,39 @@ end -- @element warp_list_container warp_list_container = Gui.element(function(event_trigger, parent) + local player = Gui.get_player_from_element(parent) + -- Check if user has permission to add warps + local allow_add_warp = check_player_permissions(player, 'allow_add_warp') + -- Draw the internal container - local container = Gui.container(parent, event_trigger, 200) + local container = Gui.container(parent, event_trigger, allow_add_warp and 268 or 220) -- Draw the header local header = Gui.header( container, {'warp-list.main-caption'}, - {'warp-list.sub-tooltip', config.cooldown_duration, config.standard_proximity_radius}, + { + 'warp-list.sub-tooltip', + config.cooldown_duration, + config.standard_proximity_radius, + {'warp-list.sub-tooltip-current',warp_status_icons.current}, + {'warp-list.sub-tooltip-connected',warp_status_icons.connected}, + {'warp-list.sub-tooltip-different',warp_status_icons.different}, + {'warp-list.sub-tooltip-cooldown',warp_status_icons.cooldown}, + {'warp-list.sub-tooltip-not_available',warp_status_icons.not_available}, + {'warp-list.sub-tooltip-bypass',warp_status_icons.bypass}, + }, true ) -- Draw the new warp button - local player = Gui.get_player_from_element(parent) local add_new_warp_element = add_new_warp(header) - add_new_warp_element.visible = check_player_permissions(player, 'allow_add_warp') + add_new_warp_element.visible = allow_add_warp -- Draw the scroll table for the warps local scroll_table = Gui.scroll_table(container, 250, 3) + -- Set the scroll panel to always show the scrollbar (not doing this will result in a changing gui size) + scroll_table.parent.vertical_scroll_policy = 'always' -- Change the style of the scroll table local scroll_table_style = scroll_table.style @@ -470,12 +669,14 @@ Gui.element(function(event_trigger, parent) local warp_timer_element = warp_timer(container) -- Change the progress of the warp timer - local progress = 1 local timer = PlayerCooldown:get(player) if timer > 0 then - progress = 1 - (timer/config.cooldown_duration) + warp_timer_element.tooltip = {'warp-list.timer-tooltip', math.floor(timer/config.update_smoothing)} + warp_timer_element.value = 1 - (timer/config.update_smoothing/config.cooldown_duration) + else + warp_timer_element.tooltip = {'warp-list.timer-tooltip-zero', config.cooldown_duration} + warp_timer_element.value = 1 end - warp_timer_element.value = progress -- Add any existing warps update_all_warps(player, scroll_table) @@ -499,25 +700,22 @@ end) Warps.on_update(function(_, warp, old_warp) -- Get the force to update, warp is nil when removed if warp then - update_all_wrap_force(game.forces[warp.force_name]) + update_all_warp_force(game.forces[warp.force_name]) else - update_all_wrap_force(game.forces[old_warp.force_name]) + update_all_warp_force(game.forces[old_warp.force_name]) end end) --- When the player leaves or enters range of a warp this is triggered -PlayerInRange:on_update(function(player_name, player_in_range) +PlayerInRange:on_update(function(player_name, warp_id) local player = game.players[player_name] -- Change if the frame is visible based on if the player is in range if not keep_gui_open[player.name] then - Gui.toggle_left_element(player, warp_list_container, player_in_range) + Gui.toggle_left_element(player, warp_list_container, warp_id ~= nil) end - -- Check if the player requires proximity - if not check_player_permissions(player, 'bypass_warp_proximity') then - update_wrap_buttons(player, nil, player_in_range) - end + update_all_warp_elements(player, nil, warp_id) end) --- Update the warp cooldown progress bars to match the current cooldown @@ -528,15 +726,17 @@ PlayerCooldown:on_update(function(player_name, player_cooldown) local warp_timer_element = frame.container[warp_timer.name] -- Set the progress - local progress = 1 if player_cooldown and player_cooldown > 0 then - progress = 1 - (player_cooldown/config.cooldown_duration) + warp_timer_element.tooltip = {'warp-list.timer-tooltip', math.floor(player_cooldown/config.update_smoothing)} + warp_timer_element.value = 1 - (player_cooldown/config.update_smoothing/config.cooldown_duration) + else + warp_timer_element.tooltip = {'warp-list.timer-tooltip-zero', config.cooldown_duration} + warp_timer_element.value = 1 end - warp_timer_element.value = progress -- Trigger update of buttons if cooldown is now 0 if player_cooldown == 0 then - update_wrap_buttons(player, player_cooldown, nil) + update_all_warp_elements(player, player_cooldown, nil) end end) @@ -595,9 +795,9 @@ Event.on_nth_tick(math.floor(60/config.update_smoothing), function() -- Check the dist to the closest warp local in_range = closest_warp.warp_id == warp_ids.spawn and closest_distance < rs2 or closest_distance < r2 if was_in_range and not in_range then - PlayerInRange:set(player, false) + PlayerInRange:set(player, nil) elseif not was_in_range and in_range then - PlayerInRange:set(player, true) + PlayerInRange:set(player, closest_warp.warp_id) end -- Change the enabled state of the add warp button @@ -631,6 +831,12 @@ Event.add(defines.events.on_player_created, function(event) spawn_id = Warps.add_warp(force.name, player.surface, spawn_position, nil, 'Spawn') Warps.set_spawn_warp(spawn_id, force) Warps.make_warp_tag(spawn_id) + + local entities = player.surface.find_entities_filtered{ type = 'electric-pole', position = spawn_position, radius = 20, limit = 1 } + if entities and entities[1] then + local warp = Warps.get_warp(spawn_id) + warp.electric_pole = entities[1] + end end end) @@ -647,13 +853,18 @@ local function role_update_event(event) local player = game.players[event.player_index] local container = Gui.get_left_element(player, warp_list_container).container + -- Check if user has permission to add warps + local allow_add_warp = check_player_permissions(player, 'allow_add_warp') + -- Update container size depending on whether the player is allowed to add warps + container.parent.style.width = allow_add_warp and 268 or 220 + -- Update the warps, incase the user can now edit them local scroll_table = container.scroll.table update_all_warps(player, scroll_table) -- Update the new warp button incase the user can now add them local add_new_warp_element = container.header.alignment[add_new_warp.name] - add_new_warp_element.visible = check_player_permissions(player, 'allow_add_warp') + add_new_warp_element.visible = allow_add_warp end Event.add(Roles.events.on_role_assigned, role_update_event) @@ -678,4 +889,4 @@ local function maintain_tag(event) end Event.add(defines.events.on_chart_tag_modified, maintain_tag) -Event.add(defines.events.on_chart_tag_removed, maintain_tag) \ No newline at end of file +Event.add(defines.events.on_chart_tag_removed, maintain_tag)