--[[-- Gui Module - Virtual Layer - Adds a virtual layer to store power to save space. @gui Virtual Layer @alias vlayer_container ]] local Gui = require 'expcore.gui' --- @dep expcore.gui local Roles = require 'expcore.roles' --- @dep expcore.roles local Event = require 'utils.event' --- @dep utils.event local format_number = require('util').format_number --- @dep util local config = require 'config.vlayer' --- @dep config.vlayer local vlayer = require 'modules.control.vlayer' local function format_energy(amount, unit) if amount < 1 then return '0 ' .. unit end local suffix = '' local suffix_list = { ['T'] = 1000000000000, ['G'] = 1000000000, ['M'] = 1000000, ['k'] = 1000 } for letter, limit in pairs (suffix_list) do if math.abs(amount) >= limit then amount = string.format('%.1f', amount / limit) suffix = letter break end end local formatted, k = amount while true do formatted, k = string.gsub(formatted, '^(-?%d+)(%d%d%d)', '%1,%2') if (k == 0) then break end end return formatted .. ' ' .. suffix .. unit end local function vlayer_convert_chest(player) local entities = player.surface.find_entities_filtered{position=player.position, radius=8, name='steel-chest', force=player.force, limit=1} if (not entities or #entities == 0) then player.print('No steel chest detected') return end local entity = entities[1] local pos = entity.position if (not entity.get_inventory(defines.inventory.chest).is_empty()) then player.print('Chest is not emptied') return nil end entity.destroy() return {x=string.format('%.1f', pos.x), y=string.format('%.1f', pos.y)} end --- Display label for the number of solar panels -- @element vlayer_gui_display_item_solar_name local vlayer_gui_display_item_solar_name = Gui.element{ type = 'label', name = 'vlayer_display_item_solar_name', caption = '[img=entity/solar-panel] Solar Panel', style = 'heading_1_label' }:style{ width = 200 } local vlayer_gui_display_item_solar_count = Gui.element{ type = 'label', name = 'vlayer_display_item_solar_count', caption = '0', style = 'heading_1_label' }:style{ width = 120 } --- Display label for the number of accumulators -- @element vlayer_gui_display_item_accumulator_name local vlayer_gui_display_item_accumulator_name = Gui.element{ type = 'label', name = 'vlayer_display_item_accumulator_name', caption = '[img=entity/accumulator] Accumulator', style = 'heading_1_label' }:style{ width = 200 } local vlayer_gui_display_item_accumulator_count = Gui.element{ type = 'label', name = 'vlayer_display_item_accumulator_count', caption = '0', style = 'heading_1_label' }:style{ width = 120 } --- Display label for the current energy production -- @element vlayer_gui_display_signal_production_name local vlayer_gui_display_signal_production_name = Gui.element{ type = 'label', name = 'vlayer_display_signal_peak_name', caption = '[virtual-signal=signal-P] Current Production', style = 'heading_1_label' }:style{ width = 200 } local vlayer_gui_display_signal_production_count = Gui.element{ type = 'label', name = 'vlayer_display_signal_peak_solar_count', caption = '0', style = 'heading_1_label' }:style{ width = 120 } --- Display label for the sustained energy production -- @element vlayer_gui_display_signal_sustained_name local vlayer_gui_display_signal_sustained_name = Gui.element{ type = 'label', name = 'vlayer_display_signal_sustained_name', caption = '[virtual-signal=signal-S] Sustained Production', style = 'heading_1_label' }:style{ width = 200 } local vlayer_gui_display_signal_sustained_count = Gui.element{ type = 'label', name = 'vlayer_display_signal_sustained_count', caption = '0', style = 'heading_1_label' }:style{ width = 120 } --- Display label for the sustained energy capacity -- @element vlayer_gui_display_signal_capacity_name local vlayer_gui_display_signal_capacity_name = Gui.element{ type = 'label', name = 'vlayer_display_signal_max_name', caption = '[virtual-signal=signal-C] Battery Capacity', style = 'heading_1_label' }:style{ width = 200 } local vlayer_gui_display_signal_capacity_count = Gui.element{ type = 'label', name = 'vlayer_display_signal_max_count', caption = '0', style = 'heading_1_label' }:style{ width = 120 } --- Display label for the current energy in storage -- @element vlayer_gui_display_signal_current_name local vlayer_gui_display_signal_current_name = Gui.element{ type = 'label', name = 'vlayer_display_signal_current_name', caption = '[virtual-signal=signal-E] Battery Charge', style = 'heading_1_label' }:style{ width = 200 } local vlayer_gui_display_signal_current_count = Gui.element{ type = 'label', name = 'vlayer_display_signal_current_count', caption = '0', style = 'heading_1_label' }:style{ width = 120 } --- A vertical flow containing all the displays labels and their counts -- @element vlayer_display_set local vlayer_display_set = Gui.element(function(_, parent, name) local vlayer_set = parent.add{type='flow', direction='vertical', name=name} local disp = Gui.scroll_table(vlayer_set, 320, 2, 'disp') vlayer_gui_display_item_solar_name(disp) vlayer_gui_display_item_solar_count(disp) vlayer_gui_display_item_accumulator_name(disp) vlayer_gui_display_item_accumulator_count(disp) vlayer_gui_display_signal_production_name(disp) vlayer_gui_display_signal_production_count(disp) vlayer_gui_display_signal_sustained_name(disp) vlayer_gui_display_signal_sustained_count(disp) vlayer_gui_display_signal_capacity_name(disp) vlayer_gui_display_signal_capacity_count(disp) vlayer_gui_display_signal_current_name(disp) vlayer_gui_display_signal_current_count(disp) return vlayer_set end) local function pos_to_gps_string(pos) return '[gps=' .. string.format('%.1f', pos.x) .. ',' .. string.format('%.1f', pos.y) .. ']' end --- A button used to add a new storage input interface -- @element vlayer_gui_control_storage_input local vlayer_gui_control_storage_input = Gui.element{ type = 'button', caption = 'Add Input Storage' }:style{ width = 160 }:on_click(function(player, element, _) local pos = vlayer_convert_chest(player) if (pos) then vlayer.create_input_interface(player.surface, pos, player) game.print(player.name .. ' built a vlayer storage input on ' .. pos_to_gps_string(pos)) end element.enabled = (vlayer.get_interface_counts().storage_input < config.interface_limit.storage_input) end) --- A button used to add a new storage output interface -- @element vlayer_gui_control_storage_output local vlayer_gui_control_storage_output = Gui.element{ type = 'button', caption = 'Add Output Storage' }:style{ width = 160 }:on_click(function(player, element, _) local pos = vlayer_convert_chest(player) if (pos) then vlayer.create_output_interface(player.surface, pos, player) game.print(player.name .. ' built a vlayer storage output on ' .. pos_to_gps_string(pos)) end element.enabled = (vlayer.get_interface_counts().storage_output < config.interface_limit.storage_output) end) --- A button used to add a new circuit interface -- @element vlayer_gui_control_circuit local vlayer_gui_control_circuit = Gui.element{ type = 'button', caption = 'Add Circuit' }:style{ width = 160 }:on_click(function(player, element, _) local pos = vlayer_convert_chest(player) if (pos) then vlayer.create_circuit_interface(player.surface, pos, player) game.print(player.name .. ' built a vlayer circuit on ' .. pos_to_gps_string(pos)) end element.enabled = (vlayer.get_interface_counts().circuit < config.interface_limit.circuit) end) --- A button used to add a new energy interface -- @element vlayer_gui_control_power local vlayer_gui_control_power = Gui.element{ type = 'button', caption = 'Add Power' }:style{ width = 160 }:on_click(function(player, element, _) local pos = vlayer_convert_chest(player) if (pos) then if vlayer.create_energy_interface(player.surface, pos, player) then game.print(player.name .. ' built a vlayer energy interface on ' .. pos_to_gps_string(pos)) else player.print('Unable to build vlayer energy entity') end end element.enabled = (vlayer.get_interface_counts().energy < config.interface_limit.energy) end) --- A button used to remove the closest vlayer interface -- @element vlayer_gui_control_remove local vlayer_gui_control_remove = Gui.element{ type = 'button', caption = 'Remove Special' }:style{ width = 160 }:on_click(function(player, element, _) local interface_type, interface_position = vlayer.remove_closest_interface(player.surface, player.position, 4) if not interface_type then return player.print('Interface not found in range, please move closer') end local interfaces = vlayer.get_interface_counts() game.print(player.name .. ' removed a vlayer ' .. interface_type .. ' on ' .. pos_to_gps_string(interface_position)) element.parent[vlayer_gui_control_storage_input.name].enabled = (interfaces.storage_input < config.interface_limit.storage_input) element.parent[vlayer_gui_control_storage_output.name].enabled = (interfaces.storage_output < config.interface_limit.storage_output) element.parent[vlayer_gui_control_circuit.name].enabled = (interfaces.circuit < config.interface_limit.circuit) element.parent[vlayer_gui_control_power.name].enabled = (interfaces.energy < config.interface_limit.energy) end) --- A vertical flow containing all the control buttons -- @element vlayer_control_set local vlayer_control_set = Gui.element(function(_, parent, name) local vlayer_set = parent.add{type='flow', direction='vertical', name=name} local disp = Gui.scroll_table(vlayer_set, 320, 2, 'disp') local interfaces = vlayer.get_interface_counts() vlayer_gui_control_storage_input(disp).enabled = (interfaces.storage_input < config.interface_limit.storage_input) vlayer_gui_control_storage_output(disp).enabled = (interfaces.storage_output < config.interface_limit.storage_output) vlayer_gui_control_circuit(disp).enabled = (interfaces.circuit < config.interface_limit.circuit) vlayer_gui_control_power(disp).enabled = (interfaces.energy < config.interface_limit.energy) vlayer_gui_control_remove(disp) return vlayer_set end) --- The main container for the vlayer gui -- @element vlayer_container local vlayer_container = Gui.element(function(definition, parent) local player = Gui.get_player_from_element(parent) local container = Gui.container(parent, definition.name, 320) vlayer_display_set(container, 'vlayer_st_1') vlayer_control_set(container, 'vlayer_st_2') local table = container['vlayer_st_2'].disp.table local visible = Roles.player_allowed(player, 'gui/vlayer-edit') table[vlayer_gui_control_storage_input.name].visible = visible table[vlayer_gui_control_storage_output.name].visible = visible table[vlayer_gui_control_circuit.name].visible = visible table[vlayer_gui_control_power.name].visible = visible table[vlayer_gui_control_remove.name].visible = visible return container.parent end) :static_name(Gui.unique_static_name) :add_to_left_flow() --- Button on the top flow used to toggle the task list container -- @element toggle_left_element Gui.left_toolbar_button('entity/solar-panel', {'vlayer.main-tooltip'}, vlayer_container, function(player) return Roles.player_allowed(player, 'gui/vlayer') end) --- Update the visibly of the buttons based on a players roles local function role_update_event(event) local player = game.players[event.player_index] local visible = Roles.player_allowed(player, 'gui/vlayer-edit') local frame = Gui.get_left_element(player, vlayer_container) frame.container['vlayer_st_2'].visible = visible end Event.add(Roles.events.on_role_assigned, role_update_event) Event.add(Roles.events.on_role_unassigned, role_update_event) Event.on_nth_tick(config.update_tick_gui, function(_) local stats = vlayer.get_statistics() local items = vlayer.get_items() local vlayer_display = { [vlayer_gui_display_item_solar_count.name] = format_number(items['solar-panel']), [vlayer_gui_display_item_accumulator_count.name] = format_number(items['accumulator']), [vlayer_gui_display_signal_production_count.name] = format_energy(stats.energy_production, 'W'), [vlayer_gui_display_signal_sustained_count.name] = format_energy(stats.energy_sustained, 'W'), [vlayer_gui_display_signal_capacity_count.name] = format_energy(stats.energy_capacity, 'J'), [vlayer_gui_display_signal_current_count.name] = format_energy(stats.energy_storage, 'J'), } for _, player in pairs(game.connected_players) do local frame = Gui.get_left_element(player, vlayer_container) local table = frame.container['vlayer_st_1'].disp.table for k, v in pairs(vlayer_display) do table[k].caption = v end end end)