mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2025-12-27 03:25:23 +09:00
547 lines
20 KiB
Lua
547 lines
20 KiB
Lua
--[[-- Gui Module - Virtual Layer
|
|
- Adds a virtual layer to store power to save space.
|
|
@gui Virtual Layer
|
|
@alias vlayer_container
|
|
]]
|
|
|
|
local Gui = require("modules/exp_gui")
|
|
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
|
|
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
|
|
local format_number = require("util").format_number --- @dep util
|
|
local config = require("modules.exp_legacy.config.vlayer") --- @dep config.vlayer
|
|
local vlayer = require("modules.exp_legacy.modules.control.vlayer")
|
|
|
|
local Selection = require("modules/exp_util/selection")
|
|
local SelectArea = Selection.connect("ExpGui_VLayer")
|
|
|
|
--- Align an aabb to the grid by expanding it
|
|
local function aabb_align_expand(aabb)
|
|
return {
|
|
left_top = { x = math.floor(aabb.left_top.x), y = math.floor(aabb.left_top.y) },
|
|
right_bottom = { x = math.ceil(aabb.right_bottom.x), y = math.ceil(aabb.right_bottom.y) },
|
|
}
|
|
end
|
|
|
|
local vlayer_container
|
|
local vlayer_gui_control_type
|
|
local vlayer_gui_control_list
|
|
|
|
local vlayer_control_type_list = {
|
|
[1] = "energy",
|
|
[2] = "circuit",
|
|
[3] = "storage_input",
|
|
[4] = "storage_output",
|
|
}
|
|
|
|
local function pos_to_gps_string(pos, surface_name)
|
|
return "[gps=" .. string.format("%.1f", pos.x) .. "," .. string.format("%.1f", pos.y) .. "," .. surface_name .. "]"
|
|
end
|
|
|
|
local function format_energy(amount, unit)
|
|
if amount < 1 then
|
|
return "0 " .. unit
|
|
end
|
|
|
|
local suffix = ""
|
|
local suffix_list = {
|
|
["P"] = 1000000000000000,
|
|
["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 k
|
|
local formatted = 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 ExpUtil = require("modules/exp_util")
|
|
--- When an area is selected to add protection to the area
|
|
SelectArea:on_selection(function(event)
|
|
log(ExpUtil.format_any(event))
|
|
local area = aabb_align_expand(event.area)
|
|
local player = game.players[event.player_index]
|
|
|
|
if not player then
|
|
return
|
|
end
|
|
|
|
local container = Gui.get_left_element(vlayer_container, player)
|
|
local disp = container.frame["vlayer_st_2"].disp.table
|
|
local target = vlayer_control_type_list[disp[vlayer_gui_control_type.name].selected_index]
|
|
local entities
|
|
|
|
if config.power_on_space and event.surface and event.surface.platform and target == "energy" then
|
|
if player.force.technologies[config.power_on_space_research.name].level >= config.power_on_space_research.level then
|
|
entities = event.surface.find_entities_filtered{ area = area, name = "constant-combinator", force = player.force }
|
|
else
|
|
player.print{ "vlayer.power-on-space-research", config.power_on_space_research.name, config.power_on_space_research.level }
|
|
return
|
|
end
|
|
else
|
|
entities = event.surface.find_entities_filtered{ area = area, name = "steel-chest", force = player.force }
|
|
end
|
|
|
|
if #entities == 0 then
|
|
player.print{ "vlayer.steel-chest-detect" }
|
|
return
|
|
elseif #entities > 1 then
|
|
player.print{ "vlayer.result-unable", { "vlayer.control-type-" .. target:gsub("_", "-") }, { "vlayer.result-multiple" } }
|
|
return
|
|
end
|
|
|
|
if not entities[1] then
|
|
return
|
|
end
|
|
|
|
local e = entities[1]
|
|
local e_pos = { x = string.format("%.1f", e.position.x), y = string.format("%.1f", e.position.y) }
|
|
local e_circ = nil -- e.get_wire_connectors{ or_create = false }
|
|
|
|
if e.name and e.name == "steel-chest" and (not e.get_inventory(defines.inventory.chest).is_empty()) then
|
|
player.print{ "vlayer.steel-chest-empty" }
|
|
return
|
|
end
|
|
|
|
if (vlayer.get_interface_counts()[target] >= config.interface_limit[target]) then
|
|
player.print{ "vlayer.result-unable", { "vlayer.control-type-" .. target:gsub("_", "-") }, { "vlayer.result-limit" } }
|
|
return
|
|
end
|
|
|
|
e.destroy()
|
|
|
|
if target == "energy" then
|
|
if not vlayer.create_energy_interface(event.surface, e_pos, player) then
|
|
player.print{ "vlayer.result-unable", { "vlayer.control-type-energy" }, { "vlayer.result-space" } }
|
|
return
|
|
end
|
|
elseif target == "circuit" then
|
|
vlayer.create_circuit_interface(event.surface, e_pos, e_circ, player)
|
|
elseif target == "storage_input" then
|
|
vlayer.create_input_interface(event.surface, e_pos, e_circ, player)
|
|
elseif target == "storage_output" then
|
|
vlayer.create_output_interface(event.surface, e_pos, e_circ, player)
|
|
end
|
|
|
|
game.print{ "vlayer.interface-result", player.name, pos_to_gps_string(e_pos, event.surface.name), { "vlayer.result-build" }, { "vlayer.control-type-" .. target:gsub("_", "-") } }
|
|
end)
|
|
|
|
--- Display label for the number of solar panels
|
|
-- @element vlayer_gui_display_item_solar_name
|
|
local vlayer_gui_display_item_solar_name = Gui.define("vlayer_gui_display_item_solar_name")
|
|
:draw{
|
|
type = "label",
|
|
name = Gui.from_name,
|
|
caption = { "vlayer.display-item-solar" },
|
|
style = "heading_2_label",
|
|
}:style{
|
|
width = 200,
|
|
}
|
|
|
|
local vlayer_gui_display_item_solar_count = Gui.define("vlayer_gui_display_item_solar_count")
|
|
:draw{
|
|
type = "progressbar",
|
|
name = Gui.from_name,
|
|
caption = "",
|
|
value = 0,
|
|
style = "electric_satisfaction_statistics_progressbar",
|
|
}:style{
|
|
width = 200,
|
|
font = "heading-2",
|
|
}
|
|
|
|
--- Display label for the number of accumulators
|
|
-- @element vlayer_gui_display_item_accumulator_name
|
|
local vlayer_gui_display_item_accumulator_name = Gui.define("vlayer_gui_display_item_accumulator_name")
|
|
:draw{
|
|
type = "label",
|
|
name = Gui.from_name,
|
|
caption = { "vlayer.display-item-accumulator" },
|
|
style = "heading_2_label",
|
|
}:style{
|
|
width = 200,
|
|
}
|
|
|
|
local vlayer_gui_display_item_accumulator_count = Gui.define("vlayer_gui_display_item_accumulator_count")
|
|
:draw{
|
|
type = "progressbar",
|
|
name = Gui.from_name,
|
|
caption = "",
|
|
value = 0,
|
|
style = "electric_satisfaction_statistics_progressbar",
|
|
}:style{
|
|
width = 200,
|
|
font = "heading-2",
|
|
}
|
|
|
|
--- Display label for the surface area
|
|
-- @element vlayer_gui_display_signal_surface_area_name
|
|
local vlayer_gui_display_signal_surface_area_name = Gui.define("vlayer_gui_display_signal_surface_area_name")
|
|
:draw{
|
|
type = "label",
|
|
name = Gui.from_name,
|
|
caption = { "vlayer.display-remaining-surface-area" },
|
|
tooltip = { "vlayer.display-remaining-surface-area-tooltip" },
|
|
style = "heading_2_label",
|
|
}:style{
|
|
width = 200,
|
|
}
|
|
|
|
local vlayer_gui_display_signal_surface_area_count = Gui.define("vlayer_gui_display_signal_surface_area_count")
|
|
:draw{
|
|
type = "progressbar",
|
|
name = Gui.from_name,
|
|
caption = "",
|
|
value = 0,
|
|
style = "electric_satisfaction_statistics_progressbar",
|
|
}:style{
|
|
width = 200,
|
|
font = "heading-2",
|
|
}
|
|
|
|
--- Display label for the sustained energy production
|
|
-- @element vlayer_gui_display_signal_sustained_name
|
|
local vlayer_gui_display_signal_sustained_name = Gui.define("vlayer_gui_display_signal_sustained_name")
|
|
:draw{
|
|
type = "label",
|
|
name = Gui.from_name,
|
|
caption = { "vlayer.display-sustained-production" },
|
|
tooltip = { "vlayer.display-sustained-production-tooltip" },
|
|
style = "heading_2_label",
|
|
}:style{
|
|
width = 200,
|
|
}
|
|
|
|
local vlayer_gui_display_signal_sustained_count = Gui.define("vlayer_gui_display_signal_sustained_count")
|
|
:draw{
|
|
type = "progressbar",
|
|
name = Gui.from_name,
|
|
caption = "",
|
|
value = 0,
|
|
style = "electric_satisfaction_statistics_progressbar",
|
|
}:style{
|
|
width = 200,
|
|
font = "heading-2",
|
|
}
|
|
|
|
--- Display label for the current energy production
|
|
-- @element vlayer_gui_display_signal_production_name
|
|
local vlayer_gui_display_signal_production_name = Gui.define("vlayer_gui_display_signal_production_name")
|
|
:draw{
|
|
type = "label",
|
|
name = Gui.from_name,
|
|
caption = { "vlayer.display-current-production" },
|
|
tooltip = { "vlayer.display-current-production-tooltip" },
|
|
style = "heading_2_label",
|
|
}:style{
|
|
width = 200,
|
|
}
|
|
|
|
local vlayer_gui_display_signal_production_count = Gui.define("vlayer_gui_display_signal_production_count")
|
|
:draw{
|
|
type = "progressbar",
|
|
name = Gui.from_name,
|
|
caption = "",
|
|
value = 0,
|
|
style = "electric_satisfaction_statistics_progressbar",
|
|
}:style{
|
|
width = 200,
|
|
font = "heading-2",
|
|
}
|
|
|
|
--- Display label for the sustained energy capacity
|
|
-- @element vlayer_gui_display_signal_capacity_name
|
|
local vlayer_gui_display_signal_capacity_name = Gui.define("vlayer_gui_display_signal_capacity_name")
|
|
:draw{
|
|
type = "label",
|
|
name = Gui.from_name,
|
|
caption = { "vlayer.display-current-capacity" },
|
|
tooltip = { "vlayer.display-current-capacity-tooltip" },
|
|
style = "heading_2_label",
|
|
}:style{
|
|
width = 200,
|
|
}
|
|
|
|
local vlayer_gui_display_signal_capacity_count = Gui.define("vlayer_gui_display_signal_capacity_count")
|
|
:draw{
|
|
type = "progressbar",
|
|
name = Gui.from_name,
|
|
caption = "",
|
|
value = 0,
|
|
style = "electric_satisfaction_statistics_progressbar",
|
|
}:style{
|
|
width = 200,
|
|
font = "heading-2",
|
|
}
|
|
|
|
--- A vertical flow containing all the displays labels and their counts
|
|
-- @element vlayer_display_set
|
|
local vlayer_display_set = Gui.define("vlayer_display_set")
|
|
:draw(function(_, parent, name)
|
|
local vlayer_set = parent.add{ type = "flow", direction = "vertical", name = name }
|
|
local disp = Gui.elements.scroll_table(vlayer_set, 400, 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_surface_area_name(disp)
|
|
vlayer_gui_display_signal_surface_area_count(disp)
|
|
vlayer_gui_display_signal_sustained_name(disp)
|
|
vlayer_gui_display_signal_sustained_count(disp)
|
|
vlayer_gui_display_signal_production_name(disp)
|
|
vlayer_gui_display_signal_production_count(disp)
|
|
vlayer_gui_display_signal_capacity_name(disp)
|
|
vlayer_gui_display_signal_capacity_count(disp)
|
|
|
|
return vlayer_set
|
|
end)
|
|
|
|
local function vlayer_gui_list_refresh(player)
|
|
local container = Gui.get_left_element(vlayer_container, player)
|
|
local disp = container.frame["vlayer_st_2"].disp.table
|
|
local target = disp[vlayer_gui_control_type.name].selected_index
|
|
local full_list = {}
|
|
|
|
if target then
|
|
local interface = vlayer.get_interfaces()[vlayer_control_type_list[target]]
|
|
|
|
for i = 1, vlayer.get_interface_counts()[vlayer_control_type_list[target]], 1 do
|
|
table.insert(full_list, i .. " X " .. interface[i].position.x .. " Y " .. interface[i].position.y)
|
|
end
|
|
|
|
disp[vlayer_gui_control_list.name].items = full_list
|
|
end
|
|
end
|
|
|
|
--- A drop down list filter by this type
|
|
-- @element vlayer_gui_control_type
|
|
vlayer_gui_control_type = Gui.define("vlayer_gui_control_type")
|
|
:draw{
|
|
type = "drop-down",
|
|
name = Gui.from_name,
|
|
items = { { "vlayer.control-type-energy" }, { "vlayer.control-type-circuit" }, { "vlayer.control-type-storage-input" }, { "vlayer.control-type-storage-output" } },
|
|
selected_index = 1,
|
|
}:style{
|
|
width = 200,
|
|
}:on_selection_state_changed(function(def, player, element)
|
|
vlayer_gui_list_refresh(player)
|
|
end)
|
|
|
|
--- A drop down list to see the exact item to remove
|
|
-- @element vlayer_gui_control_list
|
|
vlayer_gui_control_list = Gui.define("vlayer_gui_control_list")
|
|
:draw{
|
|
type = "drop-down",
|
|
name = Gui.from_name,
|
|
}:style{
|
|
width = 200,
|
|
}
|
|
|
|
--- A button to refresh the remove list
|
|
-- @element vlayer_gui_control_refresh
|
|
local vlayer_gui_control_refresh = Gui.define("vlayer_gui_control_refresh")
|
|
:draw{
|
|
type = "button",
|
|
name = Gui.from_name,
|
|
caption = { "vlayer.control-refresh" },
|
|
}:style{
|
|
width = 200,
|
|
}:on_click(function(def, player, element)
|
|
vlayer_gui_list_refresh(player)
|
|
end)
|
|
|
|
--- A button to check if the item is the one wanted to remove
|
|
-- @element vlayer_gui_control_see
|
|
local vlayer_gui_control_see = Gui.define("vlayer_gui_control_see")
|
|
:draw{
|
|
type = "button",
|
|
name = Gui.from_name,
|
|
caption = { "vlayer.control-see" },
|
|
}:style{
|
|
width = 200,
|
|
}:on_click(function(def, player, element, event)
|
|
local target = element.parent[vlayer_gui_control_type.name].selected_index
|
|
local n = element.parent[vlayer_gui_control_list.name].selected_index
|
|
|
|
if target and vlayer_control_type_list[target] and n > 0 then
|
|
local i = vlayer.get_interfaces()
|
|
local entity = i[vlayer_control_type_list[target]][n]
|
|
if entity and entity.valid then
|
|
local player = Gui.get_player(event)
|
|
player.set_controller{ type = defines.controllers.remote, position = entity.position, surface = entity.surface }
|
|
player.print{ "vlayer.result-interface-location", { "vlayer.control-type-" .. vlayer_control_type_list[target]:gsub("_", "-") }, pos_to_gps_string(entity.position, entity.surface.name) }
|
|
end
|
|
end
|
|
end)
|
|
|
|
--- A button used to build the vlayer interface
|
|
-- @element vlayer_gui_control_build
|
|
local vlayer_gui_control_build = Gui.define("vlayer_gui_control_build")
|
|
:draw{
|
|
type = "button",
|
|
name = Gui.from_name,
|
|
caption = { "vlayer.control-build" },
|
|
}:style{
|
|
width = 200,
|
|
}:on_click(function(def, player, element)
|
|
if SelectArea:stop(player) then
|
|
player.print{ "vlayer.exit" }
|
|
else
|
|
SelectArea:start(player)
|
|
player.print{ "vlayer.enter" }
|
|
end
|
|
|
|
vlayer_gui_list_refresh(player)
|
|
end)
|
|
|
|
--- A button used to remove the vlayer interface
|
|
-- @element vlayer_gui_control_remove
|
|
local vlayer_gui_control_remove = Gui.define("vlayer_gui_control_remove")
|
|
:draw{
|
|
type = "button",
|
|
name = Gui.from_name,
|
|
caption = { "vlayer.control-remove" },
|
|
}:style{
|
|
width = 200,
|
|
}:on_click(function(def, player, element)
|
|
local target = element.parent[vlayer_gui_control_type.name].selected_index
|
|
local n = element.parent[vlayer_gui_control_list.name].selected_index
|
|
|
|
if target and vlayer_control_type_list[target] and n > 0 then
|
|
local i = vlayer.get_interfaces()
|
|
|
|
if i and i[vlayer_control_type_list[target]] then
|
|
local interface_type, interface_surface, interface_position = vlayer.remove_interface(i[vlayer_control_type_list[target]][n].surface, i[vlayer_control_type_list[target]][n].position)
|
|
|
|
if interface_type then
|
|
game.print{ "vlayer.interface-result", player.name, pos_to_gps_string(interface_position, interface_surface.name), { "vlayer.result-remove" }, { "vlayer.control-type-" .. interface_type } }
|
|
end
|
|
end
|
|
end
|
|
|
|
vlayer_gui_list_refresh(player)
|
|
end)
|
|
|
|
--- A vertical flow containing all the control buttons
|
|
-- @element vlayer_control_set
|
|
local vlayer_control_set = Gui.define("vlayer_control_set")
|
|
:draw(function(_, parent, name)
|
|
local player = Gui.get_player(parent)
|
|
local vlayer_set = parent.add{ type = "flow", direction = "vertical", name = name }
|
|
local disp = Gui.elements.scroll_table(vlayer_set, 400, 2, "disp")
|
|
|
|
vlayer_gui_control_type(disp)
|
|
vlayer_gui_control_list(disp)
|
|
vlayer_gui_control_refresh(disp)
|
|
vlayer_gui_control_see(disp)
|
|
local b = vlayer_gui_control_build(disp)
|
|
local r = vlayer_gui_control_remove(disp)
|
|
local v = Roles.player_allowed(player, "gui/vlayer-edit")
|
|
b.visible = v
|
|
r.visible = v
|
|
|
|
return vlayer_set
|
|
end)
|
|
|
|
--- The main container for the vlayer gui
|
|
-- @element vlayer_container
|
|
vlayer_container = Gui.define("vlayer_container")
|
|
:draw(function(definition, parent)
|
|
local container = Gui.elements.container(parent, 400)
|
|
|
|
vlayer_display_set(container, "vlayer_st_1")
|
|
vlayer_control_set(container, "vlayer_st_2")
|
|
|
|
return container.parent
|
|
end)
|
|
|
|
--- Add the element to the left flow with a toolbar button
|
|
Gui.add_left_element(vlayer_container, false)
|
|
Gui.toolbar.create_button{
|
|
name = "vlayer_toggle",
|
|
left_element = vlayer_container,
|
|
sprite = "entity/solar-panel",
|
|
tooltip = { "vlayer.main-tooltip" },
|
|
visible = function(player, element)
|
|
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 container = Gui.get_left_element(vlayer_container, player)
|
|
local disp = container.frame["vlayer_st_2"].disp.table
|
|
disp[vlayer_gui_control_build.name].visible = visible
|
|
disp[vlayer_gui_control_remove.name].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 items_alloc = vlayer.get_allocated_items()
|
|
|
|
local vlayer_display = {
|
|
[vlayer_gui_display_item_solar_count.name] = {
|
|
val = (items_alloc["solar-panel"] / math.max(items["solar-panel"], 1)),
|
|
cap = format_number(items_alloc["solar-panel"], false) .. " / " .. format_number(items["solar-panel"], false),
|
|
},
|
|
[vlayer_gui_display_item_accumulator_count.name] = {
|
|
val = (items_alloc["accumulator"] / math.max(items["accumulator"], 1)),
|
|
cap = format_number(items_alloc["accumulator"], false) .. " / " .. format_number(items["accumulator"], false),
|
|
},
|
|
[vlayer_gui_display_signal_surface_area_count.name] = {
|
|
val = (stats.total_surface_area / math.max(stats.surface_area, 1)),
|
|
cap = format_number(stats.remaining_surface_area)
|
|
},
|
|
[vlayer_gui_display_signal_sustained_count.name] = {
|
|
val = (stats.energy_sustained / math.max(stats.energy_total_production, 1)),
|
|
cap = format_energy(stats.energy_sustained, "W") .. " / " .. format_energy(stats.energy_total_production, "W")
|
|
},
|
|
[vlayer_gui_display_signal_production_count.name] = {
|
|
val = (stats.energy_production / math.max(stats.energy_max, 1)),
|
|
cap = format_energy(stats.energy_production, "W") .. " / " .. format_energy(stats.energy_max, "W"),
|
|
},
|
|
[vlayer_gui_display_signal_capacity_count.name] = {
|
|
val = (stats.energy_storage / math.max(stats.energy_capacity, 1)),
|
|
cap = format_energy(stats.energy_storage, "J") .. " / " .. format_energy(stats.energy_capacity, "J"),
|
|
},
|
|
}
|
|
|
|
for _, player in pairs(game.connected_players) do
|
|
local container = Gui.get_left_element(vlayer_container, player)
|
|
local disp = container.frame["vlayer_st_1"].disp.table
|
|
|
|
for k, v in pairs(vlayer_display) do
|
|
disp[k].caption = v.cap
|
|
|
|
if v.val then
|
|
disp[k].value = v.val
|
|
end
|
|
end
|
|
end
|
|
end)
|