Move files to exp_legacy
This commit is contained in:
364
exp_legacy/module/modules/gui/autofill.lua
Normal file
364
exp_legacy/module/modules/gui/autofill.lua
Normal file
@@ -0,0 +1,364 @@
|
||||
--[[-- Gui Module - Autofill
|
||||
- Adds a button to enable Autofill
|
||||
@gui Autofill
|
||||
@alias autofill
|
||||
]]
|
||||
|
||||
local Game = require 'utils.game' -- @dep utils.game
|
||||
local Gui = require 'expcore.gui' -- @dep expcore.gui
|
||||
local Roles = require 'expcore.roles' -- @dep expcore.gui
|
||||
local Global = require 'utils.global' -- @dep utils.global
|
||||
local config = require 'config.gui.autofill' -- @dep config.gui.autofill
|
||||
local Event = require 'utils.event' -- @dep utils.event
|
||||
local table = require 'overrides.table' -- @dep overrides.table
|
||||
|
||||
local print_text = Game.print_floating_text -- (surface, position, text, color)
|
||||
|
||||
--- Table that stores if autofill is enabled or not
|
||||
local autofill_player_settings = {}
|
||||
Global.register(autofill_player_settings, function(tbl)
|
||||
autofill_player_settings = tbl
|
||||
end)
|
||||
|
||||
local autofill_container
|
||||
|
||||
local function rich_img(type, value)
|
||||
return '[img='..type..'/'..value..']'
|
||||
end
|
||||
|
||||
--- Toggle entity section visibility
|
||||
-- @element toggle_item_button
|
||||
local toggle_section =
|
||||
Gui.element{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/expand_dark',
|
||||
hovered_sprite = 'utility/expand',
|
||||
tooltip = {'autofill.toggle-section-tooltip'},
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style(Gui.sprite_style(20))
|
||||
:on_click(function(_, element, _)
|
||||
local header_flow = element.parent
|
||||
local flow_name = header_flow.caption
|
||||
local flow = header_flow.parent.parent[flow_name]
|
||||
if Gui.toggle_visible_state(flow) then
|
||||
element.sprite = 'utility/collapse_dark'
|
||||
element.hovered_sprite = 'utility/collapse'
|
||||
element.tooltip = {'autofill.toggle-section-collapse-tooltip'}
|
||||
else
|
||||
element.sprite = 'utility/expand_dark'
|
||||
element.hovered_sprite = 'utility/expand'
|
||||
element.tooltip = {'autofill.toggle-section-tooltip'}
|
||||
end
|
||||
end)
|
||||
|
||||
--- Toggle enitity button, used for toggling autofill for the specific entity
|
||||
-- All entity autofill settings will be ignored if its disabled
|
||||
-- @element entity_toggle
|
||||
local entity_toggle =
|
||||
Gui.element(function(_, parent, entity_name)
|
||||
return parent.add{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/confirm_slot',
|
||||
tooltip = {'autofill.toggle-entity-tooltip', rich_img('item', entity_name)},
|
||||
style = 'shortcut_bar_button_green'
|
||||
}
|
||||
end)
|
||||
:style(Gui.sprite_style(22))
|
||||
:on_click(function(player, element, _)
|
||||
local entity_name = string.match(element.parent.parent.name,'(.*)%-header')
|
||||
if not autofill_player_settings[player.name] then return end
|
||||
local setting = autofill_player_settings[player.name][entity_name]
|
||||
if not setting then return end
|
||||
if setting.enabled then
|
||||
setting.enabled = false
|
||||
element.sprite = 'utility/close_black'
|
||||
element.style = 'shortcut_bar_button_red'
|
||||
else
|
||||
setting.enabled = true
|
||||
element.sprite = 'utility/confirm_slot'
|
||||
element.style = 'shortcut_bar_button_green'
|
||||
end
|
||||
-- Correct the button size
|
||||
local style = element.style
|
||||
style.padding = -2
|
||||
style.height = 22
|
||||
style.width = 22
|
||||
end)
|
||||
|
||||
--- Draw a section header and main scroll
|
||||
-- @element autofill_section_container
|
||||
local section =
|
||||
Gui.element(function(definition, parent, section_name, table_size)
|
||||
-- Draw the header for the section
|
||||
local header = Gui.header(
|
||||
parent,
|
||||
{'autofill.toggle-section-caption', rich_img('item', section_name), {'entity-name.'..section_name}},
|
||||
{'autofill.toggle-section-tooltip'},
|
||||
true,
|
||||
section_name..'-header'
|
||||
)
|
||||
|
||||
definition:triggers_events(header.parent.header_label)
|
||||
|
||||
-- Right aligned button to toggle the section
|
||||
header.caption = section_name
|
||||
entity_toggle(header, section_name)
|
||||
toggle_section(header)
|
||||
|
||||
local section_table = parent.add{
|
||||
type = 'table',
|
||||
name = section_name,
|
||||
column_count = table_size
|
||||
}
|
||||
|
||||
section_table.visible = false
|
||||
|
||||
return definition:no_events(section_table)
|
||||
end)
|
||||
:on_click(function(_, element, event)
|
||||
event.element = element.parent.alignment[toggle_section.name]
|
||||
toggle_section:raise_event(event)
|
||||
end)
|
||||
|
||||
--- Toggle item button, used for toggling autofill for the specific item
|
||||
-- @element toggle_item_button
|
||||
local toggle_item_button =
|
||||
Gui.element(function(_, parent, item)
|
||||
return parent.add{
|
||||
type = 'sprite-button',
|
||||
sprite = 'item/'..item.name,
|
||||
tooltip = {'autofill.toggle-tooltip', rich_img('item', item.name), item.category},
|
||||
style = 'shortcut_bar_button_red'
|
||||
}
|
||||
end)
|
||||
:style(Gui.sprite_style(32, nil, { right_margin = -3 }))
|
||||
:on_click(function(player, element)
|
||||
local item_name = element.parent.tooltip
|
||||
local entity_name = element.parent.parent.parent.name
|
||||
if not autofill_player_settings[player.name] then return end
|
||||
local setting = autofill_player_settings[player.name][entity_name]
|
||||
if not setting then return end
|
||||
local item = setting.items[item_name]
|
||||
if not item then return end
|
||||
if item.enabled then
|
||||
item.enabled = false
|
||||
element.style = 'shortcut_bar_button_red'
|
||||
else
|
||||
item.enabled = true
|
||||
element.style = 'shortcut_bar_button_green'
|
||||
end
|
||||
-- Correct the button size
|
||||
local style = element.style
|
||||
style.right_margin = -3
|
||||
style.padding = -2
|
||||
style.height = 32
|
||||
style.width = 32
|
||||
end)
|
||||
|
||||
|
||||
--- Amount text field for a autofill item
|
||||
-- @element amount_textfield
|
||||
local amount_textfield =
|
||||
Gui.element(function(_, parent, item)
|
||||
return parent.add{
|
||||
type = 'textfield',
|
||||
text = item.amount,
|
||||
tooltip = {'autofill.amount-tooltip', item.category },
|
||||
clear_and_focus_on_right_click = true,
|
||||
numeric = true,
|
||||
allow_decimal = false,
|
||||
allow_negative = false
|
||||
}
|
||||
end)
|
||||
:style{
|
||||
maximal_width = 40,
|
||||
height = 31,
|
||||
padding = -2
|
||||
}
|
||||
:on_text_changed(function(player, element, _)
|
||||
local value = tonumber(element.text)
|
||||
if not value then value = 0 end
|
||||
local clamped = math.clamp(value, 0, 1000)
|
||||
local item_name = element.parent.tooltip
|
||||
local entity_name = element.parent.parent.parent.name
|
||||
if not autofill_player_settings[player.name] then return end
|
||||
local setting = autofill_player_settings[player.name][entity_name]
|
||||
if not setting then return end
|
||||
local item = setting.items[item_name]
|
||||
if not item then return end
|
||||
item.amount = clamped
|
||||
if clamped ~= value then
|
||||
element.text = clamped
|
||||
player.print{'autofill.invalid', item.amount, rich_img('item', item.name), rich_img('entity', entity_name) }
|
||||
return
|
||||
end
|
||||
end)
|
||||
|
||||
--- Autofill setting, contains a button and a textbox
|
||||
-- @element add_autofill_setting
|
||||
local add_autofill_setting =
|
||||
Gui.element(function(_, parent, item)
|
||||
local toggle_flow = parent.add{ type = 'flow', name = 'toggle-setting-'..item.name, tooltip = item.name }
|
||||
local amount_flow = parent.add{ type = 'flow', name = 'amount-setting-'..item.name, tooltip = item.name }
|
||||
toggle_flow.style.padding = 0
|
||||
amount_flow.style.padding = 0
|
||||
toggle_item_button(toggle_flow, item)
|
||||
amount_textfield(amount_flow, item)
|
||||
end)
|
||||
|
||||
--- Autofill setting empty, contains filler button and textfield gui elements
|
||||
-- @element add_empty_autofill_setting
|
||||
local add_empty_autofill_setting =
|
||||
Gui.element(function(_, parent)
|
||||
local toggle_element = parent.add{
|
||||
type = 'sprite-button'
|
||||
}
|
||||
toggle_element.style.right_margin = -3
|
||||
toggle_element.style.width = 32
|
||||
toggle_element.style.height = 32
|
||||
toggle_element.enabled = false
|
||||
local amount_element = parent.add{
|
||||
type = 'textfield'
|
||||
}
|
||||
amount_element.style.maximal_width = 40
|
||||
amount_element.style.height = 31
|
||||
amount_element.style.padding = -2
|
||||
amount_element.enabled = false
|
||||
end)
|
||||
|
||||
|
||||
--- Main gui container for the left flow
|
||||
-- @element autofill_container
|
||||
autofill_container =
|
||||
Gui.element(function(definition, parent)
|
||||
-- Draw the internal container
|
||||
local container = Gui.container(parent, definition.name)
|
||||
-- Draw the scroll container
|
||||
local scroll_table = Gui.scroll_table(container, 400, 1, 'autofill-scroll-table')
|
||||
-- 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'
|
||||
-- Scroll panel has by default padding
|
||||
scroll_table.parent.style.padding = 0
|
||||
-- Remove the default gap that is added in a table between elements
|
||||
scroll_table.style.vertical_spacing = 0
|
||||
-- Center the first collumn in the table
|
||||
scroll_table.style.column_alignments[1] = 'center'
|
||||
-- Loop over each default entity config
|
||||
for _, setting in pairs(config.default_entities) do
|
||||
local table_sizes = {}
|
||||
local tables = {}
|
||||
-- Draw a section for the element
|
||||
local entity_table = section(scroll_table, setting.entity, 3)
|
||||
-- Add some padding around the table
|
||||
entity_table.style.padding = 3
|
||||
-- Make sure each collumn is alignment top center
|
||||
entity_table.style.column_alignments[1] = 'top-center'
|
||||
entity_table.style.column_alignments[2] = 'top-center'
|
||||
entity_table.style.column_alignments[3] = 'top-center'
|
||||
-- Loop over each item category
|
||||
for _, category in pairs(config.categories) do
|
||||
if not table_sizes[category] then table_sizes[category] = 0 end
|
||||
-- Draw table
|
||||
local category_table = entity_table.add{
|
||||
type = 'table',
|
||||
name = category..'-category',
|
||||
column_count = 2
|
||||
}
|
||||
-- Add padding between each item
|
||||
category_table.style.vertical_spacing = 1
|
||||
tables[category] = category_table
|
||||
-- Add item autofill setting gui elements to the table
|
||||
for _, item in pairs(setting.items) do
|
||||
if item.category == category then
|
||||
add_autofill_setting(category_table, item)
|
||||
table_sizes[category] = table_sizes[category] + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Add empty gui elements for the categories with less items than the other categories
|
||||
local t = table.get_values(table_sizes)
|
||||
table.sort(t)
|
||||
local biggest = t[#t]
|
||||
for category, size in pairs(table_sizes) do
|
||||
for i=biggest-size,1,-1 do
|
||||
add_empty_autofill_setting(tables[category])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Return the external container
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow()
|
||||
|
||||
--- Button on the top flow used to toggle autofill container
|
||||
-- @element autofill_toggle
|
||||
Gui.left_toolbar_button(config.icon, {'autofill.main-tooltip'}, autofill_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/autofill')
|
||||
end)
|
||||
|
||||
--- When a player is created make sure they have the default autofill settings
|
||||
Event.add(defines.events.on_player_created, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
if not autofill_player_settings[player.name] then
|
||||
autofill_player_settings[player.name] = table.deep_copy(config.default_entities)
|
||||
end
|
||||
end)
|
||||
|
||||
local function entity_build(event)
|
||||
-- Check if player exists
|
||||
local player = game.players[event.player_index]
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
-- Check if the entity is in the config and enabled
|
||||
local entity = event.created_entity
|
||||
|
||||
-- Check if player has settings
|
||||
if not autofill_player_settings[player.name] then return end
|
||||
|
||||
local entity_settings = autofill_player_settings[player.name][entity.name]
|
||||
-- Check if autofill for the entity is enabled
|
||||
if not entity_settings then return end
|
||||
if not entity_settings.enabled then return end
|
||||
|
||||
-- Get the inventory of the player
|
||||
local player_inventory = player.get_main_inventory()
|
||||
|
||||
local text_position = { x = entity.position.x, y = entity.position.y }
|
||||
-- Loop over all possible items to insert into the entity
|
||||
for _, item in pairs(entity_settings.items) do
|
||||
-- Check if the item is enabled or goto next item
|
||||
if not item.enabled then goto end_item end
|
||||
|
||||
-- Get the inventory of the entity or goto next item
|
||||
local entity_inventory = entity.get_inventory(item.inv)
|
||||
if not entity_inventory then goto end_item end
|
||||
|
||||
local preferd_amount = item.amount
|
||||
local item_amount = player_inventory.get_item_count(item.name)
|
||||
if item_amount ~= 0 then
|
||||
local inserted
|
||||
text_position.y = text_position.y - 0.5
|
||||
local color = { r = 0, g = 255, b = 0, a = 1}
|
||||
if item_amount >= preferd_amount then
|
||||
-- Can item be inserted? no, goto next item!
|
||||
if not entity_inventory.can_insert{name=item.name, count=preferd_amount} then
|
||||
goto end_item
|
||||
end
|
||||
inserted = entity_inventory.insert{name=item.name, count=preferd_amount}
|
||||
else
|
||||
inserted = entity_inventory.insert{name=item.name, count=item_amount}
|
||||
color = { r = 255, g = 165, b = 0, a = 1}
|
||||
end
|
||||
player_inventory.remove{name=item.name, count=inserted}
|
||||
print_text(entity.surface, text_position, {'autofill.inserted', inserted, rich_img('item', item.name), rich_img('entity', entity.name) }, color)
|
||||
end
|
||||
::end_item::
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_built_entity, entity_build)
|
||||
392
exp_legacy/module/modules/gui/bonus.lua
Normal file
392
exp_legacy/module/modules/gui/bonus.lua
Normal file
@@ -0,0 +1,392 @@
|
||||
--[[-- Gui Module - Bonus
|
||||
@gui Bonus
|
||||
@alias bonus_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 config = require 'config.bonus' --- @dep config.bonus
|
||||
local vlayer = require 'modules.control.vlayer'
|
||||
|
||||
local format_number = require('util').format_number --- @dep util
|
||||
local bonus_container
|
||||
|
||||
local function bonus_gui_pts_needed(player)
|
||||
local frame = Gui.get_left_element(player, bonus_container)
|
||||
local disp = frame.container['bonus_st_2'].disp.table
|
||||
local total = 0
|
||||
|
||||
for k, v in pairs(config.conversion) do
|
||||
total = total + (disp['bonus_display_' .. k .. '_slider'].slider_value / config.player_bonus[v].cost_scale * config.player_bonus[v].cost)
|
||||
end
|
||||
|
||||
total = total + (disp['bonus_display_personal_battery_recharge_slider'].slider_value / config.player_special_bonus['personal_battery_recharge'].cost_scale * config.player_special_bonus['personal_battery_recharge'].cost)
|
||||
|
||||
return total
|
||||
end
|
||||
|
||||
local function apply_bonus(player)
|
||||
if not Roles.player_allowed(player, 'gui/bonus') then
|
||||
for k, v in pairs(config.player_bonus) do
|
||||
player[k] = 0
|
||||
|
||||
if v.combined_bonus then
|
||||
for i=1, #v.combined_bonus do
|
||||
player[v.combined_bonus[i]] = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if not player.character then
|
||||
return
|
||||
end
|
||||
|
||||
local frame = Gui.get_left_element(player, bonus_container)
|
||||
local disp = frame.container['bonus_st_2'].disp.table
|
||||
|
||||
for k, v in pairs(config.conversion) do
|
||||
player[v] = disp['bonus_display_' .. k .. '_slider'].slider_value
|
||||
|
||||
if config.player_bonus[v].combined_bonus then
|
||||
for i=1, #config.player_bonus[v].combined_bonus do
|
||||
player[config.player_bonus[v].combined_bonus[i]] = disp['bonus_display_' .. k .. '_slider'].slider_value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function apply_periodic_bonus(player)
|
||||
if not Roles.player_allowed(player, 'gui/bonus') then
|
||||
return
|
||||
end
|
||||
|
||||
if not player.character then
|
||||
return
|
||||
end
|
||||
|
||||
local frame = Gui.get_left_element(player, bonus_container)
|
||||
local disp = frame.container['bonus_st_2'].disp.table
|
||||
|
||||
if vlayer.get_statistics()['energy_sustained'] > 0 then
|
||||
local armor = player.get_inventory(defines.inventory.character_armor)[1].grid
|
||||
|
||||
if armor then
|
||||
local slider = disp['bonus_display_personal_battery_recharge_slider'].slider_value * 100000 * config.player_special_bonus_rate / 6
|
||||
|
||||
for i=1, #armor.equipment do
|
||||
if armor.equipment[i].energy < armor.equipment[i].max_energy then
|
||||
local energy_required = math.min(math.floor(armor.equipment[i].max_energy - armor.equipment[i].energy), vlayer.get_statistics()['energy_storage'], slider)
|
||||
armor.equipment[i].energy = armor.equipment[i].energy + energy_required
|
||||
vlayer.energy_changed(- energy_required)
|
||||
|
||||
slider = slider - energy_required
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Control label for the bonus points available
|
||||
-- @element bonus_gui_control_pts_a
|
||||
local bonus_gui_control_pts_a =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
name = 'bonus_control_pts_a',
|
||||
caption = {'bonus.control-pts-a'},
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width = config.gui_display_width['half']
|
||||
}
|
||||
|
||||
local bonus_gui_control_pts_a_count =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
name = 'bonus_control_pts_a_count',
|
||||
caption = config.pts.base,
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width = config.gui_display_width['half']
|
||||
}
|
||||
|
||||
--- Control label for the bonus points needed
|
||||
-- @element bonus_gui_control_pts_n
|
||||
local bonus_gui_control_pts_n =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
name = 'bonus_control_pts_n',
|
||||
caption = {'bonus.control-pts-n'},
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width = config.gui_display_width['half']
|
||||
}
|
||||
|
||||
local bonus_gui_control_pts_n_count =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
name = 'bonus_control_pts_n_count',
|
||||
caption = '0',
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width =config.gui_display_width['half']
|
||||
}
|
||||
|
||||
--- Control label for the bonus points remaining
|
||||
-- @element bonus_gui_control_pts_r
|
||||
local bonus_gui_control_pts_r =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
name = 'bonus_control_pts_r',
|
||||
caption = {'bonus.control-pts-r'},
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width = config.gui_display_width['half']
|
||||
}
|
||||
|
||||
local bonus_gui_control_pts_r_count =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
name = 'bonus_control_pts_r_count',
|
||||
caption = '0',
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width = config.gui_display_width['half']
|
||||
}
|
||||
|
||||
--- A button used for pts calculations
|
||||
-- @element bonus_gui_control_refresh
|
||||
local bonus_gui_control_reset =
|
||||
Gui.element{
|
||||
type = 'button',
|
||||
name = Gui.unique_static_name,
|
||||
caption = {'bonus.control-reset'}
|
||||
}:style{
|
||||
width = config.gui_display_width['half']
|
||||
}:on_click(function(player, element, _)
|
||||
local frame = Gui.get_left_element(player, bonus_container)
|
||||
local disp = frame.container['bonus_st_2'].disp.table
|
||||
|
||||
for k, v in pairs(config.conversion) do
|
||||
local s = 'bonus_display_' .. k .. '_slider'
|
||||
disp[s].slider_value = config.player_bonus[v].value
|
||||
|
||||
if config.player_bonus[v].is_percentage then
|
||||
disp[disp[s].tags.counter].caption = format_number(disp[s].slider_value * 100) .. ' %'
|
||||
|
||||
else
|
||||
disp[disp[s].tags.counter].caption = format_number(disp[s].slider_value)
|
||||
end
|
||||
end
|
||||
|
||||
local slider = disp['bonus_display_personal_battery_recharge_slider']
|
||||
slider.slider_value = config.player_special_bonus['personal_battery_recharge'].value
|
||||
disp[slider.tags.counter].caption = format_number(slider.slider_value)
|
||||
|
||||
local r = bonus_gui_pts_needed(player)
|
||||
element.parent[bonus_gui_control_pts_n_count.name].caption = r
|
||||
element.parent[bonus_gui_control_pts_r_count.name].caption = tonumber(element.parent[bonus_gui_control_pts_a_count.name].caption) - r
|
||||
end)
|
||||
|
||||
--- A button used for pts apply
|
||||
-- @element bonus_gui_control_apply
|
||||
local bonus_gui_control_apply =
|
||||
Gui.element{
|
||||
type = 'button',
|
||||
name = Gui.unique_static_name,
|
||||
caption = {'bonus.control-apply'}
|
||||
}:style{
|
||||
width = config.gui_display_width['half']
|
||||
}:on_click(function(player, element, _)
|
||||
local n = bonus_gui_pts_needed(player)
|
||||
element.parent[bonus_gui_control_pts_n_count.name].caption = n
|
||||
local r = tonumber(element.parent[bonus_gui_control_pts_a_count.name].caption) - n
|
||||
element.parent[bonus_gui_control_pts_r_count.name].caption = r
|
||||
|
||||
if r >= 0 then
|
||||
apply_bonus(player)
|
||||
end
|
||||
end)
|
||||
|
||||
--- A vertical flow containing all the bonus control
|
||||
-- @element bonus_control_set
|
||||
local bonus_control_set =
|
||||
Gui.element(function(_, parent, name)
|
||||
local bonus_set = parent.add{type='flow', direction='vertical', name=name}
|
||||
local disp = Gui.scroll_table(bonus_set, config.gui_display_width['half'] * 2, 2, 'disp')
|
||||
|
||||
bonus_gui_control_pts_a(disp)
|
||||
bonus_gui_control_pts_a_count(disp)
|
||||
|
||||
bonus_gui_control_pts_n(disp)
|
||||
bonus_gui_control_pts_n_count(disp)
|
||||
|
||||
bonus_gui_control_pts_r(disp)
|
||||
bonus_gui_control_pts_r_count(disp)
|
||||
|
||||
bonus_gui_control_reset(disp)
|
||||
bonus_gui_control_apply(disp)
|
||||
|
||||
return bonus_set
|
||||
end)
|
||||
|
||||
--- Display group
|
||||
-- @element bonus_gui_slider
|
||||
local bonus_gui_slider =
|
||||
Gui.element(function(_definition, parent, name, caption, tooltip, bonus)
|
||||
local label = parent.add{
|
||||
type = 'label',
|
||||
caption = caption,
|
||||
tooltip = tooltip,
|
||||
style = 'heading_2_label'
|
||||
}
|
||||
label.style.width = config.gui_display_width['label']
|
||||
|
||||
local value = bonus.value
|
||||
|
||||
if bonus.is_percentage then
|
||||
value = format_number(value * 100) .. ' %'
|
||||
|
||||
else
|
||||
value = format_number(value)
|
||||
end
|
||||
|
||||
local slider = parent.add{
|
||||
type = 'slider',
|
||||
name = name .. '_slider',
|
||||
value = bonus.value,
|
||||
maximum_value = bonus.max,
|
||||
value_step = bonus.scale,
|
||||
discrete_values = true,
|
||||
style = 'notched_slider',
|
||||
tags = {
|
||||
counter = name .. '_count',
|
||||
is_percentage = bonus.is_percentage
|
||||
}
|
||||
}
|
||||
slider.style.width = config.gui_display_width['slider']
|
||||
slider.style.horizontally_stretchable = true
|
||||
|
||||
local count = parent.add{
|
||||
type = 'label',
|
||||
name = name .. '_count',
|
||||
caption = value,
|
||||
style = 'heading_2_label',
|
||||
}
|
||||
count.style.width = config.gui_display_width['count']
|
||||
|
||||
return slider
|
||||
end)
|
||||
:on_value_changed(function(player, element, _event)
|
||||
if element.tags.is_percentage then
|
||||
element.parent[element.tags.counter].caption = format_number(element.slider_value * 100) .. ' %'
|
||||
|
||||
else
|
||||
element.parent[element.tags.counter].caption = format_number(element.slider_value)
|
||||
end
|
||||
|
||||
local r = bonus_gui_pts_needed(player)
|
||||
local frame = Gui.get_left_element(player, bonus_container)
|
||||
local disp = frame.container['bonus_st_1'].disp.table
|
||||
disp[bonus_gui_control_pts_n_count.name].caption = r
|
||||
disp[bonus_gui_control_pts_r_count.name].caption = tonumber(disp[bonus_gui_control_pts_a_count.name].caption) - r
|
||||
end)
|
||||
|
||||
--- A vertical flow containing all the bonus data
|
||||
-- @element bonus_data_set
|
||||
local bonus_data_set =
|
||||
Gui.element(function(_, parent, name)
|
||||
local bonus_set = parent.add{type='flow', direction='vertical', name=name}
|
||||
local disp = Gui.scroll_table(bonus_set, config.gui_display_width['half'] * 2, 3, 'disp')
|
||||
|
||||
for k, v in pairs(config.conversion) do
|
||||
bonus_gui_slider(disp, 'bonus_display_' .. k, {'bonus.display-' .. k}, {'bonus.display-' .. k .. '-tooltip'}, config.player_bonus[v])
|
||||
end
|
||||
|
||||
bonus_gui_slider(disp, 'bonus_display_personal_battery_recharge', {'bonus.display-personal-battery-recharge'}, {'bonus.display-personal-battery-recharge-tooltip'}, config.player_special_bonus['personal_battery_recharge'])
|
||||
|
||||
return bonus_set
|
||||
end)
|
||||
|
||||
--- The main container for the bonus gui
|
||||
-- @element bonus_container
|
||||
bonus_container =
|
||||
Gui.element(function(definition, parent)
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
local container = Gui.container(parent, definition.name, config.gui_display_width['half'] * 2)
|
||||
|
||||
bonus_control_set(container, 'bonus_st_1')
|
||||
bonus_data_set(container, 'bonus_st_2')
|
||||
|
||||
local frame = Gui.get_left_element(player, bonus_container)
|
||||
local disp = frame.container['bonus_st_1'].disp.table
|
||||
local n = bonus_gui_pts_needed(player)
|
||||
disp[bonus_gui_control_pts_n_count.name].caption = n
|
||||
local r = tonumber(disp[bonus_gui_control_pts_a_count.name].caption) - n
|
||||
disp[bonus_gui_control_pts_r_count.name].caption = r
|
||||
|
||||
apply_bonus(player)
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow()
|
||||
|
||||
--- Button on the top flow used to toggle the bonus container
|
||||
-- @element toggle_left_element
|
||||
Gui.left_toolbar_button('item/exoskeleton-equipment', {'bonus.main-tooltip'}, bonus_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/bonus')
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_player_created, function(event)
|
||||
if event.player_index ~= 1 then
|
||||
return
|
||||
end
|
||||
|
||||
for k, v in pairs(config.force_bonus) do
|
||||
game.players[event.player_index].force[k] = v.value
|
||||
end
|
||||
|
||||
for k, v in pairs(config.surface_bonus) do
|
||||
game.players[event.player_index].surface[k] = v.value
|
||||
end
|
||||
end)
|
||||
|
||||
Event.add(Roles.events.on_role_assigned, function(event)
|
||||
apply_bonus(game.players[event.player_index])
|
||||
end)
|
||||
|
||||
Event.add(Roles.events.on_role_unassigned, function(event)
|
||||
apply_bonus(game.players[event.player_index])
|
||||
end)
|
||||
|
||||
--- When a player respawns re-apply bonus
|
||||
Event.add(defines.events.on_player_respawned, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
local frame = Gui.get_left_element(player, bonus_container)
|
||||
local disp = frame.container['bonus_st_1'].disp.table
|
||||
local n = bonus_gui_pts_needed(player)
|
||||
disp[bonus_gui_control_pts_n_count.name].caption = n
|
||||
local r = tonumber(disp[bonus_gui_control_pts_a_count.name].caption) - n
|
||||
disp[bonus_gui_control_pts_r_count.name].caption = r
|
||||
|
||||
if r >= 0 then
|
||||
apply_bonus(player)
|
||||
end
|
||||
end)
|
||||
|
||||
--- When a player dies allow them to have instant respawn
|
||||
Event.add(defines.events.on_player_died, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
|
||||
if Roles.player_has_flag(player, 'instant-respawn') then
|
||||
player.ticks_to_respawn = 120
|
||||
end
|
||||
end)
|
||||
|
||||
Event.on_nth_tick(config.player_special_bonus_rate, function(_)
|
||||
for _, player in pairs(game.connected_players) do
|
||||
apply_periodic_bonus(player)
|
||||
end
|
||||
end)
|
||||
114
exp_legacy/module/modules/gui/debug/_g_view.lua
Normal file
114
exp_legacy/module/modules/gui/debug/_g_view.lua
Normal file
@@ -0,0 +1,114 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local Model = require 'modules.gui.debug.model' --- @dep modules.gui.debug.model
|
||||
local Color = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
|
||||
local dump = Model.dump
|
||||
|
||||
local Public = {}
|
||||
|
||||
local ignore = {
|
||||
_G = true,
|
||||
assert = true,
|
||||
collectgarbage = true,
|
||||
error = true,
|
||||
getmetatable = true,
|
||||
ipairs = true,
|
||||
load = true,
|
||||
loadstring = true,
|
||||
next = true,
|
||||
pairs = true,
|
||||
pcall = true,
|
||||
print = true,
|
||||
rawequal = true,
|
||||
rawlen = true,
|
||||
rawget = true,
|
||||
rawset = true,
|
||||
select = true,
|
||||
setmetatable = true,
|
||||
tonumber = true,
|
||||
tostring = true,
|
||||
type = true,
|
||||
xpcall = true,
|
||||
_VERSION = true,
|
||||
['module'] = true,
|
||||
require = true,
|
||||
package = true,
|
||||
unpack = true,
|
||||
table = true,
|
||||
string = true,
|
||||
bit32 = true,
|
||||
math = true,
|
||||
debug = true,
|
||||
serpent = true,
|
||||
log = true,
|
||||
table_size = true,
|
||||
global = true,
|
||||
remote = true,
|
||||
commands = true,
|
||||
settings = true,
|
||||
rcon = true,
|
||||
script = true,
|
||||
util = true,
|
||||
mod_gui = true,
|
||||
game = true,
|
||||
rendering = true
|
||||
}
|
||||
|
||||
local header_name = Gui.uid_name()
|
||||
local left_panel_name = Gui.uid_name()
|
||||
local right_panel_name = Gui.uid_name()
|
||||
|
||||
Public.name = '_G'
|
||||
|
||||
function Public.show(container)
|
||||
local main_flow = container.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name}
|
||||
local left_panel_style = left_panel.style
|
||||
left_panel_style.width = 300
|
||||
|
||||
for key, value in pairs(_G) do
|
||||
if not ignore[key] then
|
||||
local header =
|
||||
left_panel.add({type = 'flow'}).add {type = 'label', name = header_name, caption = tostring(key)}
|
||||
Gui.set_data(header, value)
|
||||
end
|
||||
end
|
||||
|
||||
local right_panel = main_flow.add {type = 'text-box', name = right_panel_name}
|
||||
right_panel.read_only = true
|
||||
right_panel.selectable = true
|
||||
|
||||
local right_panel_style = right_panel.style
|
||||
right_panel_style.vertically_stretchable = true
|
||||
right_panel_style.horizontally_stretchable = true
|
||||
right_panel_style.maximal_width = 1000
|
||||
right_panel_style.maximal_height = 1000
|
||||
|
||||
Gui.set_data(left_panel, {right_panel = right_panel, selected_header = nil})
|
||||
end
|
||||
|
||||
Gui.on_click(
|
||||
header_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local value = Gui.get_data(element)
|
||||
|
||||
local left_panel = element.parent.parent
|
||||
local left_panel_data = Gui.get_data(left_panel)
|
||||
local right_panel = left_panel_data.right_panel
|
||||
local selected_header = left_panel_data.selected_header
|
||||
|
||||
if selected_header then
|
||||
selected_header.style.font_color = Color.white
|
||||
end
|
||||
|
||||
element.style.font_color = Color.orange
|
||||
left_panel_data.selected_header = element
|
||||
|
||||
local content = dump(value)
|
||||
right_panel.text = content
|
||||
end
|
||||
)
|
||||
|
||||
return Public
|
||||
169
exp_legacy/module/modules/gui/debug/event_view.lua
Normal file
169
exp_legacy/module/modules/gui/debug/event_view.lua
Normal file
@@ -0,0 +1,169 @@
|
||||
local Event = require 'utils.event'
|
||||
local table = require 'overrides.table'
|
||||
local Gui = require 'utils.gui'
|
||||
local Model = require 'modules.gui.debug.model'
|
||||
|
||||
local format = string.format
|
||||
local insert = table.insert
|
||||
|
||||
local events = defines.events
|
||||
|
||||
-- Constants
|
||||
local events_to_keep = 10
|
||||
|
||||
-- Local vars
|
||||
local Public = {
|
||||
name = 'Events'
|
||||
}
|
||||
local name_lookup = {}
|
||||
|
||||
-- GUI names
|
||||
local checkbox_name = Gui.uid_name()
|
||||
local filter_name = Gui.uid_name()
|
||||
local clear_filter_name = Gui.uid_name()
|
||||
|
||||
-- global tables
|
||||
local enabled = {}
|
||||
local last_events = {}
|
||||
global.debug_event_view = {
|
||||
enabled = enabled,
|
||||
last_events = last_events,
|
||||
filter = ''
|
||||
}
|
||||
|
||||
function Public.on_open_debug()
|
||||
local tbl = global.debug_event_view
|
||||
if tbl then
|
||||
enabled = tbl.enabled
|
||||
last_events = tbl.last_events
|
||||
else
|
||||
enabled = {}
|
||||
last_events = {}
|
||||
|
||||
global.debug_event_view = {
|
||||
enabled = enabled,
|
||||
last_events = last_events
|
||||
}
|
||||
end
|
||||
|
||||
Public.on_open_debug = nil
|
||||
end
|
||||
|
||||
-- Local functions
|
||||
local function event_callback(event)
|
||||
local id = event.name
|
||||
if not enabled[id] then
|
||||
return
|
||||
end
|
||||
local name = name_lookup[id]
|
||||
|
||||
if not last_events[name] then
|
||||
last_events[name] = {}
|
||||
end
|
||||
|
||||
insert(last_events[name], 1, event)
|
||||
last_events[name][events_to_keep + 1] = nil
|
||||
event.name = nil
|
||||
|
||||
local str = format('%s (id = %s): %s', name, id, Model.dump(event))
|
||||
game.print(str)
|
||||
log(str)
|
||||
end
|
||||
|
||||
local function on_gui_checked_state_changed(event)
|
||||
local element = event.element
|
||||
local name = element.caption
|
||||
local id = events[name]
|
||||
local state = element.state and true or false
|
||||
element.state = state
|
||||
if state then
|
||||
enabled[id] = true
|
||||
else
|
||||
enabled[id] = false
|
||||
end
|
||||
end
|
||||
|
||||
-- GUI
|
||||
|
||||
-- Create a table with events sorted by their names
|
||||
local grid_builder = {}
|
||||
for name, _ in pairs(events) do
|
||||
grid_builder[#grid_builder + 1] = name
|
||||
end
|
||||
|
||||
table.sort(grid_builder)
|
||||
|
||||
local function redraw_event_table(gui_table, filter)
|
||||
for _, event_name in pairs(grid_builder) do
|
||||
if filter == '' or event_name:find(filter) then
|
||||
local index = events[event_name]
|
||||
gui_table.add({type = 'flow'}).add {
|
||||
name = checkbox_name,
|
||||
type = 'checkbox',
|
||||
state = enabled[index] or false,
|
||||
caption = event_name
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Public.show(container)
|
||||
local filter = global.debug_event_view.filter
|
||||
|
||||
local main_frame_flow = container.add({type = 'flow', direction = 'vertical'})
|
||||
|
||||
local filter_flow = main_frame_flow.add({type = 'flow', direction = 'horizontal'})
|
||||
filter_flow.add({type = 'label', caption = 'filter'})
|
||||
local filter_textfield = filter_flow.add({type = 'textfield', name = filter_name, text = filter})
|
||||
local clear_button = filter_flow.add({type = 'button', name = clear_filter_name, caption = 'clear'})
|
||||
|
||||
local scroll_pane = main_frame_flow.add({type = 'scroll-pane'})
|
||||
local gui_table = scroll_pane.add({type = 'table', column_count = 3, draw_horizontal_lines = true})
|
||||
|
||||
Gui.set_data(filter_textfield, gui_table)
|
||||
Gui.set_data(clear_button, {gui_table = gui_table, filter_textfield = filter_textfield})
|
||||
|
||||
redraw_event_table(gui_table, filter)
|
||||
end
|
||||
|
||||
Gui.on_checked_state_changed(checkbox_name, on_gui_checked_state_changed)
|
||||
|
||||
Gui.on_text_changed(
|
||||
filter_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local gui_table = Gui.get_data(element)
|
||||
|
||||
local filter = element.text:gsub(' ', '_')
|
||||
|
||||
global.debug_event_view.filter = filter
|
||||
element.text = filter
|
||||
|
||||
gui_table.clear()
|
||||
redraw_event_table(gui_table, filter)
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
clear_filter_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
local filter_textfield = data.filter_textfield
|
||||
local gui_table = data.gui_table
|
||||
|
||||
filter_textfield.text = ''
|
||||
global.debug_event_view.filter = ''
|
||||
|
||||
gui_table.clear()
|
||||
redraw_event_table(gui_table, '')
|
||||
end
|
||||
)
|
||||
|
||||
-- Event registers (TODO: turn to removable hooks.. maybe)
|
||||
for name, id in pairs(events) do
|
||||
name_lookup[id] = name
|
||||
Event.add(id, event_callback)
|
||||
end
|
||||
|
||||
return Public
|
||||
130
exp_legacy/module/modules/gui/debug/expcore_datastore_view.lua
Normal file
130
exp_legacy/module/modules/gui/debug/expcore_datastore_view.lua
Normal file
@@ -0,0 +1,130 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local Datastore = require 'expcore.datastore' --- @dep expcore.datastore
|
||||
local Color = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
local Model = require 'modules.gui.debug.model' --- @dep modules.gui.debug.model
|
||||
|
||||
local dump = Model.dump
|
||||
local concat = table.concat
|
||||
|
||||
local Public = {}
|
||||
|
||||
local header_name = Gui.uid_name()
|
||||
local left_panel_name = Gui.uid_name()
|
||||
local right_panel_name = Gui.uid_name()
|
||||
local input_text_box_name = Gui.uid_name()
|
||||
local refresh_name = Gui.uid_name()
|
||||
|
||||
Public.name = 'Datastore'
|
||||
|
||||
function Public.show(container)
|
||||
local main_flow = container.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name}
|
||||
local left_panel_style = left_panel.style
|
||||
left_panel_style.width = 300
|
||||
|
||||
for name in pairs(table.keysort(Datastore.debug())) do
|
||||
local header = left_panel.add({type = 'flow'}).add {type = 'label', name = header_name, caption = name}
|
||||
Gui.set_data(header, name)
|
||||
end
|
||||
|
||||
local right_flow = main_flow.add {type = 'flow', direction = 'vertical'}
|
||||
|
||||
local right_top_flow = right_flow.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local input_text_box = right_top_flow.add {type = 'text-box', name = input_text_box_name}
|
||||
local input_text_box_style = input_text_box.style
|
||||
input_text_box_style.horizontally_stretchable = true
|
||||
input_text_box_style.height = 32
|
||||
input_text_box_style.maximal_width = 1000
|
||||
|
||||
local refresh_button =
|
||||
right_top_flow.add {type = 'sprite-button', name = refresh_name, sprite = 'utility/reset', tooltip = 'refresh'}
|
||||
local refresh_button_style = refresh_button.style
|
||||
refresh_button_style.width = 32
|
||||
refresh_button_style.height = 32
|
||||
|
||||
local right_panel = right_flow.add {type = 'text-box', name = right_panel_name}
|
||||
right_panel.read_only = true
|
||||
right_panel.selectable = true
|
||||
|
||||
local right_panel_style = right_panel.style
|
||||
right_panel_style.vertically_stretchable = true
|
||||
right_panel_style.horizontally_stretchable = true
|
||||
right_panel_style.maximal_width = 1000
|
||||
right_panel_style.maximal_height = 1000
|
||||
|
||||
local data = {
|
||||
right_panel = right_panel,
|
||||
input_text_box = input_text_box,
|
||||
selected_header = nil
|
||||
}
|
||||
|
||||
Gui.set_data(input_text_box, data)
|
||||
Gui.set_data(left_panel, data)
|
||||
Gui.set_data(refresh_button, data)
|
||||
end
|
||||
|
||||
Gui.on_click(
|
||||
header_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local tableName = Gui.get_data(element)
|
||||
|
||||
local left_panel = element.parent.parent
|
||||
local data = Gui.get_data(left_panel)
|
||||
local right_panel = data.right_panel
|
||||
local selected_header = data.selected_header
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
if selected_header then
|
||||
selected_header.style.font_color = Color.white
|
||||
end
|
||||
|
||||
element.style.font_color = Color.orange
|
||||
data.selected_header = element
|
||||
|
||||
input_text_box.text = tableName
|
||||
input_text_box.style.font_color = Color.black
|
||||
|
||||
local content = Datastore.debug(tableName)
|
||||
local content_string = {}
|
||||
for key, value in pairs(content) do
|
||||
content_string[#content_string+1] = key:gsub('^%l', string.upper)..' = '..dump(value)
|
||||
end
|
||||
right_panel.text = concat(content_string, '\n')
|
||||
end
|
||||
)
|
||||
|
||||
local function update_dump(text_input, data)
|
||||
local content = Datastore.debug(text_input.text)
|
||||
local content_string = {}
|
||||
for key, value in pairs(content) do
|
||||
content_string[#content_string+1] = key:gsub('^%l', string.upper)..' = '..dump(value)
|
||||
end
|
||||
data.right_panel.text = concat(content_string, '\n')
|
||||
end
|
||||
|
||||
Gui.on_text_changed(
|
||||
input_text_box_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
update_dump(element, data)
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
refresh_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
update_dump(input_text_box, data)
|
||||
end
|
||||
)
|
||||
|
||||
return Public
|
||||
128
exp_legacy/module/modules/gui/debug/expcore_gui_view.lua
Normal file
128
exp_legacy/module/modules/gui/debug/expcore_gui_view.lua
Normal file
@@ -0,0 +1,128 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local ExpGui = require 'expcore.gui' --- @dep utils.global
|
||||
local Color = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
local Model = require 'modules.gui.debug.model' --- @dep modules.gui.debug.model
|
||||
|
||||
local dump = Model.dump
|
||||
local dump_text = Model.dump_text
|
||||
local concat = table.concat
|
||||
|
||||
local Public = {}
|
||||
|
||||
local header_name = Gui.uid_name()
|
||||
local left_panel_name = Gui.uid_name()
|
||||
local right_panel_name = Gui.uid_name()
|
||||
local input_text_box_name = Gui.uid_name()
|
||||
local refresh_name = Gui.uid_name()
|
||||
|
||||
Public.name = 'Elements'
|
||||
|
||||
function Public.show(container)
|
||||
local main_flow = container.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name}
|
||||
local left_panel_style = left_panel.style
|
||||
left_panel_style.width = 300
|
||||
|
||||
for element_id, file_path in pairs(ExpGui.file_paths) do
|
||||
local header = left_panel.add({type = 'flow'}).add {type = 'label', name = header_name, caption = element_id..' - '..file_path}
|
||||
Gui.set_data(header, element_id)
|
||||
end
|
||||
|
||||
local right_flow = main_flow.add {type = 'flow', direction = 'vertical'}
|
||||
|
||||
local right_top_flow = right_flow.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local input_text_box = right_top_flow.add {type = 'text-box', name = input_text_box_name}
|
||||
local input_text_box_style = input_text_box.style
|
||||
input_text_box_style.horizontally_stretchable = true
|
||||
input_text_box_style.height = 32
|
||||
input_text_box_style.maximal_width = 1000
|
||||
|
||||
local refresh_button =
|
||||
right_top_flow.add {type = 'sprite-button', name = refresh_name, sprite = 'utility/reset', tooltip = 'refresh'}
|
||||
local refresh_button_style = refresh_button.style
|
||||
refresh_button_style.width = 32
|
||||
refresh_button_style.height = 32
|
||||
|
||||
local right_panel = right_flow.add {type = 'text-box', name = right_panel_name}
|
||||
right_panel.read_only = true
|
||||
right_panel.selectable = true
|
||||
|
||||
local right_panel_style = right_panel.style
|
||||
right_panel_style.vertically_stretchable = true
|
||||
right_panel_style.horizontally_stretchable = true
|
||||
right_panel_style.maximal_width = 1000
|
||||
right_panel_style.maximal_height = 1000
|
||||
|
||||
local data = {
|
||||
right_panel = right_panel,
|
||||
input_text_box = input_text_box,
|
||||
selected_header = nil
|
||||
}
|
||||
|
||||
Gui.set_data(input_text_box, data)
|
||||
Gui.set_data(left_panel, data)
|
||||
Gui.set_data(refresh_button, data)
|
||||
end
|
||||
|
||||
Gui.on_click(
|
||||
header_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local element_id = Gui.get_data(element)
|
||||
|
||||
local left_panel = element.parent.parent
|
||||
local data = Gui.get_data(left_panel)
|
||||
local right_panel = data.right_panel
|
||||
local selected_header = data.selected_header
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
if selected_header then
|
||||
selected_header.style.font_color = Color.white
|
||||
end
|
||||
|
||||
element.style.font_color = Color.orange
|
||||
data.selected_header = element
|
||||
|
||||
input_text_box.text = concat {'Gui.defines[', element_id, ']'}
|
||||
input_text_box.style.font_color = Color.black
|
||||
|
||||
local content = dump(ExpGui.debug_info[element_id]) or 'nil'
|
||||
right_panel.text = content
|
||||
end
|
||||
)
|
||||
|
||||
local function update_dump(text_input, data, player)
|
||||
local suc, ouput = dump_text(text_input.text, player)
|
||||
if not suc then
|
||||
text_input.style.font_color = Color.red
|
||||
else
|
||||
text_input.style.font_color = Color.black
|
||||
data.right_panel.text = ouput
|
||||
end
|
||||
end
|
||||
|
||||
Gui.on_text_changed(
|
||||
input_text_box_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
update_dump(element, data, event.player)
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
refresh_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
update_dump(input_text_box, data, event.player)
|
||||
end
|
||||
)
|
||||
|
||||
return Public
|
||||
133
exp_legacy/module/modules/gui/debug/global_view.lua
Normal file
133
exp_legacy/module/modules/gui/debug/global_view.lua
Normal file
@@ -0,0 +1,133 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local Model = require 'modules.gui.debug.model' --- @dep modules.gui.debug.model
|
||||
local Color = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
|
||||
local dump = Model.dump
|
||||
local dump_text = Model.dump_text
|
||||
local concat = table.concat
|
||||
|
||||
local Public = {}
|
||||
|
||||
local ignore = {tokens = true, data_store = true, datastores = true}
|
||||
|
||||
local header_name = Gui.uid_name()
|
||||
local left_panel_name = Gui.uid_name()
|
||||
local right_panel_name = Gui.uid_name()
|
||||
local input_text_box_name = Gui.uid_name()
|
||||
local refresh_name = Gui.uid_name()
|
||||
|
||||
Public.name = 'global'
|
||||
|
||||
function Public.show(container)
|
||||
local main_flow = container.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name}
|
||||
local left_panel_style = left_panel.style
|
||||
left_panel_style.width = 300
|
||||
|
||||
for key, _ in pairs(global) do
|
||||
if not ignore[key] then
|
||||
local header =
|
||||
left_panel.add({type = 'flow'}).add {type = 'label', name = header_name, caption = tostring(key)}
|
||||
Gui.set_data(header, key)
|
||||
end
|
||||
end
|
||||
|
||||
local right_flow = main_flow.add {type = 'flow', direction = 'vertical'}
|
||||
|
||||
local right_top_flow = right_flow.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local input_text_box = right_top_flow.add {type = 'text-box', name = input_text_box_name}
|
||||
local input_text_box_style = input_text_box.style
|
||||
input_text_box_style.horizontally_stretchable = true
|
||||
input_text_box_style.height = 32
|
||||
input_text_box_style.maximal_width = 1000
|
||||
|
||||
local refresh_button =
|
||||
right_top_flow.add {type = 'sprite-button', name = refresh_name, sprite = 'utility/reset', tooltip = 'refresh'}
|
||||
local refresh_button_style = refresh_button.style
|
||||
refresh_button_style.width = 32
|
||||
refresh_button_style.height = 32
|
||||
|
||||
local right_panel = right_flow.add {type = 'text-box', name = right_panel_name}
|
||||
right_panel.read_only = true
|
||||
right_panel.selectable = true
|
||||
|
||||
local right_panel_style = right_panel.style
|
||||
right_panel_style.vertically_stretchable = true
|
||||
right_panel_style.horizontally_stretchable = true
|
||||
right_panel_style.maximal_width = 1000
|
||||
right_panel_style.maximal_height = 1000
|
||||
|
||||
local data = {
|
||||
right_panel = right_panel,
|
||||
input_text_box = input_text_box,
|
||||
selected_header = nil,
|
||||
selected_token_id = nil
|
||||
}
|
||||
|
||||
Gui.set_data(input_text_box, data)
|
||||
Gui.set_data(left_panel, data)
|
||||
Gui.set_data(refresh_button, data)
|
||||
end
|
||||
|
||||
Gui.on_click(
|
||||
header_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local key = Gui.get_data(element)
|
||||
|
||||
local left_panel = element.parent.parent
|
||||
local data = Gui.get_data(left_panel)
|
||||
local right_panel = data.right_panel
|
||||
local selected_header = data.selected_header
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
if selected_header then
|
||||
selected_header.style.font_color = Color.white
|
||||
end
|
||||
|
||||
element.style.font_color = Color.orange
|
||||
data.selected_header = element
|
||||
|
||||
input_text_box.text = concat {"global['", key, "']"}
|
||||
input_text_box.style.font_color = Color.black
|
||||
|
||||
local content = dump(global[key]) or 'nil'
|
||||
right_panel.text = content
|
||||
end
|
||||
)
|
||||
|
||||
local function update_dump(text_input, data, player)
|
||||
local suc, ouput = dump_text(text_input.text, player)
|
||||
if not suc then
|
||||
text_input.style.font_color = Color.red
|
||||
else
|
||||
text_input.style.font_color = Color.black
|
||||
data.right_panel.text = ouput
|
||||
end
|
||||
end
|
||||
|
||||
Gui.on_text_changed(
|
||||
input_text_box_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
update_dump(element, data, event.player)
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
refresh_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
update_dump(input_text_box, data, event.player)
|
||||
end
|
||||
)
|
||||
|
||||
return Public
|
||||
113
exp_legacy/module/modules/gui/debug/main_view.lua
Normal file
113
exp_legacy/module/modules/gui/debug/main_view.lua
Normal file
@@ -0,0 +1,113 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local Color = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
|
||||
local Public = {}
|
||||
|
||||
local pages = {
|
||||
require 'modules.gui.debug.redmew_global_view',
|
||||
require 'modules.gui.debug.expcore_datastore_view',
|
||||
require 'modules.gui.debug.expcore_gui_view',
|
||||
require 'modules.gui.debug.global_view',
|
||||
require 'modules.gui.debug.package_view',
|
||||
require 'modules.gui.debug._g_view',
|
||||
require 'modules.gui.debug.event_view'
|
||||
}
|
||||
|
||||
local main_frame_name = Gui.uid_name()
|
||||
local close_name = Gui.uid_name()
|
||||
local tab_name = Gui.uid_name()
|
||||
|
||||
function Public.open_dubug(player)
|
||||
for i = 1, #pages do
|
||||
local page = pages[i]
|
||||
local callback = page.on_open_debug
|
||||
if callback then
|
||||
callback()
|
||||
end
|
||||
end
|
||||
|
||||
local center = player.gui.center
|
||||
local frame = center[main_frame_name]
|
||||
if frame then
|
||||
return
|
||||
end
|
||||
|
||||
--[[
|
||||
local screen_element = player.gui.screen
|
||||
|
||||
frame = screen_element.add{type = 'frame', name = main_frame_name, caption = 'Debuggertron 3000'}
|
||||
frame.style.size = {900, 600}
|
||||
frame.auto_center = true
|
||||
]]
|
||||
|
||||
frame = center.add {type = 'frame', name = main_frame_name, caption = 'Debuggertron 3002', direction = 'vertical'}
|
||||
local frame_style = frame.style
|
||||
frame_style.height = 600
|
||||
frame_style.width = 900
|
||||
|
||||
local tab_flow = frame.add {type = 'flow', direction = 'horizontal'}
|
||||
local container = frame.add {type = 'flow'}
|
||||
container.style.vertically_stretchable = true
|
||||
|
||||
local data = {}
|
||||
|
||||
for i = 1, #pages do
|
||||
local page = pages[i]
|
||||
local tab_button = tab_flow.add({type = 'flow'}).add {type = 'button', name = tab_name, caption = page.name}
|
||||
local tab_button_style = tab_button.style
|
||||
|
||||
Gui.set_data(tab_button, {index = i, frame_data = data})
|
||||
|
||||
if i == 1 then
|
||||
tab_button_style.font_color = Color.orange
|
||||
|
||||
data.selected_index = i
|
||||
data.selected_tab_button = tab_button
|
||||
data.container = container
|
||||
|
||||
Gui.set_data(frame, data)
|
||||
page.show(container)
|
||||
end
|
||||
end
|
||||
|
||||
frame.add {type = 'button', name = close_name, caption = 'Close'}
|
||||
end
|
||||
|
||||
Gui.on_click(
|
||||
tab_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
local index = data.index
|
||||
local frame_data = data.frame_data
|
||||
local selected_index = frame_data.selected_index
|
||||
|
||||
if selected_index == index then
|
||||
return
|
||||
end
|
||||
|
||||
local selected_tab_button = frame_data.selected_tab_button
|
||||
selected_tab_button.style.font_color = Color.black
|
||||
|
||||
frame_data.selected_tab_button = element
|
||||
frame_data.selected_index = index
|
||||
element.style.font_color = Color.orange
|
||||
|
||||
local container = frame_data.container
|
||||
Gui.clear(container)
|
||||
pages[index].show(container)
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
close_name,
|
||||
function(event)
|
||||
local frame = event.player.gui.center[main_frame_name]
|
||||
if frame then
|
||||
Gui.destroy(frame)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
return Public
|
||||
147
exp_legacy/module/modules/gui/debug/model.lua
Normal file
147
exp_legacy/module/modules/gui/debug/model.lua
Normal file
@@ -0,0 +1,147 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local table = require 'overrides.table' --- @dep overrides.table
|
||||
|
||||
local gui_names = Gui.names
|
||||
local type = type
|
||||
local concat = table.concat
|
||||
local inspect = table.inspect
|
||||
local pcall = pcall
|
||||
local loadstring = loadstring ---@diagnostic disable-line
|
||||
local rawset = rawset
|
||||
|
||||
local Public = {}
|
||||
|
||||
local luaObject = {'{', nil, ", name = '", nil, "'}"}
|
||||
local luaPlayer = {"{LuaPlayer, name = '", nil, "', index = ", nil, '}'}
|
||||
local luaEntity = {"{LuaEntity, name = '", nil, "', unit_number = ", nil, '}'}
|
||||
local luaGuiElement = {"{LuaGuiElement, name = '", nil, "'}"}
|
||||
|
||||
local function get(obj, prop)
|
||||
return obj[prop]
|
||||
end
|
||||
|
||||
local function get_name_safe(obj)
|
||||
local s, r = pcall(get, obj, 'name')
|
||||
if not s then
|
||||
return 'nil'
|
||||
else
|
||||
return r or 'nil'
|
||||
end
|
||||
end
|
||||
|
||||
local function get_lua_object_type_safe(obj)
|
||||
local s, r = pcall(get, obj, 'help')
|
||||
|
||||
if not s then
|
||||
return
|
||||
end
|
||||
|
||||
return r():match('Lua%a+')
|
||||
end
|
||||
|
||||
local function inspect_process(item)
|
||||
if type(item) ~= 'table' or type(item.__self) ~= 'userdata' then
|
||||
return item
|
||||
end
|
||||
|
||||
local suc, valid = pcall(get, item, 'valid')
|
||||
if not suc then
|
||||
-- no 'valid' property
|
||||
return get_lua_object_type_safe(item) or '{NoHelp LuaObject}'
|
||||
end
|
||||
|
||||
if not valid then
|
||||
return '{Invalid LuaObject}'
|
||||
end
|
||||
|
||||
local obj_type = get_lua_object_type_safe(item)
|
||||
if not obj_type then
|
||||
return '{NoHelp LuaObject}'
|
||||
end
|
||||
|
||||
if obj_type == 'LuaPlayer' then
|
||||
luaPlayer[2] = item.name or 'nil'
|
||||
luaPlayer[4] = item.index or 'nil'
|
||||
|
||||
return concat(luaPlayer)
|
||||
elseif obj_type == 'LuaEntity' then
|
||||
luaEntity[2] = item.name or 'nil'
|
||||
luaEntity[4] = item.unit_number or 'nil'
|
||||
|
||||
return concat(luaEntity)
|
||||
elseif obj_type == 'LuaGuiElement' then
|
||||
local name = item.name
|
||||
luaGuiElement[2] = gui_names and gui_names[name] or name or 'nil'
|
||||
|
||||
return concat(luaGuiElement)
|
||||
else
|
||||
luaObject[2] = obj_type
|
||||
luaObject[4] = get_name_safe(item)
|
||||
|
||||
return concat(luaObject)
|
||||
end
|
||||
end
|
||||
|
||||
local inspect_options = {process = inspect_process}
|
||||
function Public.dump(data)
|
||||
return inspect(data, inspect_options)
|
||||
end
|
||||
local dump = Public.dump
|
||||
|
||||
function Public.dump_ignore_builder(ignore)
|
||||
local function process(item)
|
||||
if ignore[item] then
|
||||
return nil
|
||||
end
|
||||
|
||||
return inspect_process(item)
|
||||
end
|
||||
|
||||
local options = {process = process}
|
||||
return function(data)
|
||||
return inspect(data, options)
|
||||
end
|
||||
end
|
||||
|
||||
function Public.dump_function(func)
|
||||
local res = {'upvalues:\n'}
|
||||
|
||||
local i = 1
|
||||
while true do
|
||||
local n, v = debug.getupvalue(func, i)
|
||||
|
||||
if n == nil then
|
||||
break
|
||||
elseif n ~= '_ENV' then
|
||||
res[#res + 1] = n
|
||||
res[#res + 1] = ' = '
|
||||
res[#res + 1] = dump(v)
|
||||
res[#res + 1] = '\n'
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
return concat(res)
|
||||
end
|
||||
|
||||
function Public.dump_text(text, player)
|
||||
local func = loadstring('return ' .. text)
|
||||
if not func then
|
||||
return false
|
||||
end
|
||||
|
||||
rawset(game, 'player', player)
|
||||
|
||||
local suc, var = pcall(func)
|
||||
|
||||
rawset(game, 'player', nil)
|
||||
|
||||
if not suc then
|
||||
return false
|
||||
end
|
||||
|
||||
return true, dump(var)
|
||||
end
|
||||
|
||||
return Public
|
||||
161
exp_legacy/module/modules/gui/debug/package_view.lua
Normal file
161
exp_legacy/module/modules/gui/debug/package_view.lua
Normal file
@@ -0,0 +1,161 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local Color = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
local Model = require 'modules.gui.debug.model' --- @dep modules.gui.debug.model
|
||||
|
||||
local dump_function = Model.dump_function
|
||||
local loaded = _G.package.loaded
|
||||
|
||||
local Public = {}
|
||||
|
||||
local ignore = {
|
||||
_G = true,
|
||||
package = true,
|
||||
coroutine = true,
|
||||
table = true,
|
||||
string = true,
|
||||
bit32 = true,
|
||||
math = true,
|
||||
debug = true,
|
||||
serpent = true,
|
||||
['overrides.math'] = true,
|
||||
util = true,
|
||||
['overrides.inspect'] = true,
|
||||
['mod-gui'] = true
|
||||
}
|
||||
|
||||
local file_label_name = Gui.uid_name()
|
||||
local left_panel_name = Gui.uid_name()
|
||||
local breadcrumbs_name = Gui.uid_name()
|
||||
local top_panel_name = Gui.uid_name()
|
||||
local variable_label_name = Gui.uid_name()
|
||||
local text_box_name = Gui.uid_name()
|
||||
|
||||
Public.name = 'package'
|
||||
|
||||
function Public.show(container)
|
||||
local main_flow = container.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name}
|
||||
local left_panel_style = left_panel.style
|
||||
left_panel_style.width = 300
|
||||
|
||||
for name, file in pairs(loaded) do
|
||||
if not ignore[name] then
|
||||
local file_label =
|
||||
left_panel.add({type = 'flow'}).add {type = 'label', name = file_label_name, caption = name}
|
||||
Gui.set_data(file_label, file)
|
||||
end
|
||||
end
|
||||
|
||||
local right_flow = main_flow.add {type = 'flow', direction = 'vertical'}
|
||||
|
||||
local breadcrumbs = right_flow.add {type = 'label', name = breadcrumbs_name}
|
||||
|
||||
local top_panel = right_flow.add {type = 'scroll-pane', name = top_panel_name}
|
||||
local top_panel_style = top_panel.style
|
||||
top_panel_style.height = 200
|
||||
top_panel_style.maximal_width = 1000
|
||||
top_panel_style.horizontally_stretchable = true
|
||||
|
||||
local text_box = right_flow.add {type = 'text-box', name = text_box_name}
|
||||
text_box.read_only = true
|
||||
text_box.selectable = true
|
||||
|
||||
local text_box_style = text_box.style
|
||||
text_box_style.vertically_stretchable = true
|
||||
text_box_style.horizontally_stretchable = true
|
||||
text_box_style.maximal_width = 1000
|
||||
text_box_style.maximal_height = 1000
|
||||
|
||||
local data = {
|
||||
left_panel = left_panel,
|
||||
breadcrumbs = breadcrumbs,
|
||||
top_panel = top_panel,
|
||||
text_box = text_box,
|
||||
selected_file_label = nil,
|
||||
selected_variable_label = nil
|
||||
}
|
||||
|
||||
Gui.set_data(left_panel, data)
|
||||
Gui.set_data(top_panel, data)
|
||||
end
|
||||
|
||||
Gui.on_click(
|
||||
file_label_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local file = Gui.get_data(element)
|
||||
|
||||
local left_panel = element.parent.parent
|
||||
local data = Gui.get_data(left_panel)
|
||||
|
||||
local selected_file_label = data.selected_file_label
|
||||
|
||||
if selected_file_label then
|
||||
selected_file_label.style.font_color = Color.white
|
||||
end
|
||||
|
||||
element.style.font_color = Color.orange
|
||||
data.selected_file_label = element
|
||||
|
||||
local top_panel = data.top_panel
|
||||
local text_box = data.text_box
|
||||
|
||||
Gui.clear(top_panel)
|
||||
|
||||
local file_type = type(file)
|
||||
|
||||
if file_type == 'table' then
|
||||
for k, v in pairs(file) do
|
||||
local label =
|
||||
top_panel.add({type = 'flow'}).add {type = 'label', name = variable_label_name, caption = k}
|
||||
Gui.set_data(label, v)
|
||||
end
|
||||
elseif file_type == 'function' then
|
||||
text_box.text = dump_function(file)
|
||||
else
|
||||
text_box.text = tostring(file)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
variable_label_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local variable = Gui.get_data(element)
|
||||
|
||||
local top_panel = element.parent.parent
|
||||
local data = Gui.get_data(top_panel)
|
||||
local text_box = data.text_box
|
||||
|
||||
local variable_type = type(variable)
|
||||
|
||||
if variable_type == 'table' then
|
||||
Gui.clear(top_panel)
|
||||
for k, v in pairs(variable) do
|
||||
local label =
|
||||
top_panel.add({type = 'flow'}).add {type = 'label', name = variable_label_name, caption = k}
|
||||
Gui.set_data(label, v)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local selected_label = data.selected_variable_label
|
||||
|
||||
if selected_label and selected_label.valid then
|
||||
selected_label.style.font_color = Color.white
|
||||
end
|
||||
|
||||
element.style.font_color = Color.orange
|
||||
data.selected_variable_label = element
|
||||
|
||||
if variable_type == 'function' then
|
||||
text_box.text = dump_function(variable)
|
||||
else
|
||||
text_box.text = tostring(variable)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
return Public
|
||||
129
exp_legacy/module/modules/gui/debug/redmew_global_view.lua
Normal file
129
exp_legacy/module/modules/gui/debug/redmew_global_view.lua
Normal file
@@ -0,0 +1,129 @@
|
||||
local Gui = require 'utils.gui' --- @dep utils.gui
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Token = require 'utils.token' --- @dep utils.token
|
||||
local Color = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
local Model = require 'modules.gui.debug.model' --- @dep modules.gui.debug.model
|
||||
|
||||
local dump = Model.dump
|
||||
local dump_text = Model.dump_text
|
||||
local concat = table.concat
|
||||
|
||||
local Public = {}
|
||||
|
||||
local header_name = Gui.uid_name()
|
||||
local left_panel_name = Gui.uid_name()
|
||||
local right_panel_name = Gui.uid_name()
|
||||
local input_text_box_name = Gui.uid_name()
|
||||
local refresh_name = Gui.uid_name()
|
||||
|
||||
Public.name = 'Global'
|
||||
|
||||
function Public.show(container)
|
||||
local main_flow = container.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name}
|
||||
local left_panel_style = left_panel.style
|
||||
left_panel_style.width = 300
|
||||
|
||||
for token_id, token_name in pairs(Global.names) do
|
||||
local header = left_panel.add({type = 'flow'}).add {type = 'label', name = header_name, caption = token_name}
|
||||
Gui.set_data(header, token_id)
|
||||
end
|
||||
|
||||
local right_flow = main_flow.add {type = 'flow', direction = 'vertical'}
|
||||
|
||||
local right_top_flow = right_flow.add {type = 'flow', direction = 'horizontal'}
|
||||
|
||||
local input_text_box = right_top_flow.add {type = 'text-box', name = input_text_box_name}
|
||||
local input_text_box_style = input_text_box.style
|
||||
input_text_box_style.horizontally_stretchable = true
|
||||
input_text_box_style.height = 32
|
||||
input_text_box_style.maximal_width = 1000
|
||||
|
||||
local refresh_button =
|
||||
right_top_flow.add {type = 'sprite-button', name = refresh_name, sprite = 'utility/reset', tooltip = 'refresh'}
|
||||
local refresh_button_style = refresh_button.style
|
||||
refresh_button_style.width = 32
|
||||
refresh_button_style.height = 32
|
||||
|
||||
local right_panel = right_flow.add {type = 'text-box', name = right_panel_name}
|
||||
right_panel.read_only = true
|
||||
right_panel.selectable = true
|
||||
|
||||
local right_panel_style = right_panel.style
|
||||
right_panel_style.vertically_stretchable = true
|
||||
right_panel_style.horizontally_stretchable = true
|
||||
right_panel_style.maximal_width = 1000
|
||||
right_panel_style.maximal_height = 1000
|
||||
|
||||
local data = {
|
||||
right_panel = right_panel,
|
||||
input_text_box = input_text_box,
|
||||
selected_header = nil
|
||||
}
|
||||
|
||||
Gui.set_data(input_text_box, data)
|
||||
Gui.set_data(left_panel, data)
|
||||
Gui.set_data(refresh_button, data)
|
||||
end
|
||||
|
||||
Gui.on_click(
|
||||
header_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local token_id = Gui.get_data(element)
|
||||
|
||||
local left_panel = element.parent.parent
|
||||
local data = Gui.get_data(left_panel)
|
||||
local right_panel = data.right_panel
|
||||
local selected_header = data.selected_header
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
if selected_header then
|
||||
selected_header.style.font_color = Color.white
|
||||
end
|
||||
|
||||
element.style.font_color = Color.orange
|
||||
data.selected_header = element
|
||||
|
||||
input_text_box.text = concat {'global.tokens[', token_id, ']'}
|
||||
input_text_box.style.font_color = Color.black
|
||||
|
||||
local content = dump(Token.get_global(token_id)) or 'nil'
|
||||
right_panel.text = content
|
||||
end
|
||||
)
|
||||
|
||||
local function update_dump(text_input, data, player)
|
||||
local suc, ouput = dump_text(text_input.text, player)
|
||||
if not suc then
|
||||
text_input.style.font_color = Color.red
|
||||
else
|
||||
text_input.style.font_color = Color.black
|
||||
data.right_panel.text = ouput
|
||||
end
|
||||
end
|
||||
|
||||
Gui.on_text_changed(
|
||||
input_text_box_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
update_dump(element, data, event.player)
|
||||
end
|
||||
)
|
||||
|
||||
Gui.on_click(
|
||||
refresh_name,
|
||||
function(event)
|
||||
local element = event.element
|
||||
local data = Gui.get_data(element)
|
||||
|
||||
local input_text_box = data.input_text_box
|
||||
|
||||
update_dump(input_text_box, data, event.player)
|
||||
end
|
||||
)
|
||||
|
||||
return Public
|
||||
185
exp_legacy/module/modules/gui/landfill.lua
Normal file
185
exp_legacy/module/modules/gui/landfill.lua
Normal file
@@ -0,0 +1,185 @@
|
||||
--[[-- Gui Module - Landfill
|
||||
- Landfill blueprint
|
||||
@gui Landfill
|
||||
@alias landfill_container
|
||||
]]
|
||||
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
|
||||
local rolling_stocks = {}
|
||||
|
||||
local function landfill_init()
|
||||
for name, _ in pairs(game.get_filtered_entity_prototypes({{filter = 'rolling-stock'}})) do
|
||||
rolling_stocks[name] = true
|
||||
end
|
||||
end
|
||||
|
||||
local function rotate_bounding_box(box)
|
||||
return {
|
||||
left_top = {
|
||||
x = -box.right_bottom.y,
|
||||
y = box.left_top.x
|
||||
},
|
||||
right_bottom = {
|
||||
x = -box.left_top.y,
|
||||
y = box.right_bottom.x
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local function curve_flip_lr(oc)
|
||||
local nc = table.deepcopy(oc)
|
||||
|
||||
for r=1, 8 do
|
||||
for c=1, 8 do
|
||||
nc[r][c] = oc[r][9 - c]
|
||||
end
|
||||
end
|
||||
|
||||
return nc
|
||||
end
|
||||
|
||||
local function curve_flip_d(oc)
|
||||
local nc = table.deepcopy(oc)
|
||||
|
||||
for r=1, 8 do
|
||||
for c=1, 8 do
|
||||
nc[r][c] = oc[c][r]
|
||||
end
|
||||
end
|
||||
|
||||
return nc
|
||||
end
|
||||
|
||||
local curves = {}
|
||||
|
||||
curves[1] = {
|
||||
{0, 0, 0, 0, 0, 1, 0, 0},
|
||||
{0, 0, 0, 0, 1, 1, 1, 0},
|
||||
{0, 0, 0, 1, 1, 1, 1, 0},
|
||||
{0, 0, 0, 1, 1, 1, 0, 0},
|
||||
{0, 0, 1, 1, 1, 0, 0, 0},
|
||||
{0, 0, 1, 1, 1, 0, 0, 0},
|
||||
{0, 0, 1, 1, 0, 0, 0, 0},
|
||||
{0, 0, 1, 1, 0, 0, 0, 0}
|
||||
}
|
||||
curves[6] = curve_flip_d(curves[1])
|
||||
curves[3] = curve_flip_lr(curves[6])
|
||||
curves[4] = curve_flip_d(curves[3])
|
||||
curves[5] = curve_flip_lr(curves[4])
|
||||
curves[2] = curve_flip_d(curves[5])
|
||||
curves[7] = curve_flip_lr(curves[2])
|
||||
curves[8] = curve_flip_d(curves[7])
|
||||
|
||||
local curve_n = {}
|
||||
|
||||
for i, map in ipairs(curves) do
|
||||
curve_n[i] = {}
|
||||
local index = 1
|
||||
|
||||
for r=1, 8 do
|
||||
for c=1, 8 do
|
||||
if map[r][c] == 1 then
|
||||
curve_n[i][index] = {
|
||||
['x'] = c - 5,
|
||||
['y'] = r - 5
|
||||
}
|
||||
|
||||
index = index + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function landfill_gui_add_landfill(blueprint)
|
||||
local entities = blueprint.get_blueprint_entities()
|
||||
local tile_index = 0
|
||||
local new_tiles = {}
|
||||
|
||||
for _, ent in pairs(entities) do
|
||||
-- vehicle
|
||||
if not (rolling_stocks[ent.name] or ent.name == 'offshore-pump') then
|
||||
-- curved rail, special
|
||||
if ent.name ~= 'curved-rail' then
|
||||
local box = game.entity_prototypes[ent.name].collision_box or game.entity_prototypes[ent.name].selection_box
|
||||
|
||||
if game.entity_prototypes[ent.name].collision_mask['ground-tile'] == nil then
|
||||
if ent.direction then
|
||||
if ent.direction ~= defines.direction.north then
|
||||
box = rotate_bounding_box(box)
|
||||
|
||||
if ent.direction ~= defines.direction.east then
|
||||
box = rotate_bounding_box(box)
|
||||
|
||||
if ent.direction ~= defines.direction.south then
|
||||
box = rotate_bounding_box(box)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for y = math.floor(ent.position.y + box.left_top.y), math.floor(ent.position.y + box.right_bottom.y), 1 do
|
||||
for x = math.floor(ent.position.x + box.left_top.x), math.floor(ent.position.x + box.right_bottom.x), 1 do
|
||||
tile_index = tile_index + 1
|
||||
new_tiles[tile_index] = {
|
||||
name = 'landfill',
|
||||
position = {x, y}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- curved rail
|
||||
else
|
||||
local curve_mask = curve_n[ent.direction or 8]
|
||||
|
||||
for m=1, #curve_mask do
|
||||
new_tiles[tile_index + 1] = {
|
||||
name = 'landfill',
|
||||
position = {curve_mask[m].x + ent.position.x, curve_mask[m].y + ent.position.y}
|
||||
}
|
||||
|
||||
tile_index = tile_index + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local old_tiles = blueprint.get_blueprint_tiles()
|
||||
|
||||
if old_tiles then
|
||||
for _, old_tile in pairs(old_tiles) do
|
||||
new_tiles[tile_index + 1] = {
|
||||
name = 'landfill',
|
||||
position = {old_tile.position.x, old_tile.position.y}
|
||||
}
|
||||
|
||||
tile_index = tile_index + 1
|
||||
end
|
||||
end
|
||||
|
||||
return {tiles = new_tiles}
|
||||
end
|
||||
|
||||
-- @element toolbar_button
|
||||
Gui.toolbar_button('item/landfill', {'landfill.main-tooltip'}, function(player)
|
||||
return Roles.player_allowed(player, 'gui/landfill')
|
||||
end)
|
||||
:on_click(function(player, _, _)
|
||||
if player.cursor_stack and player.cursor_stack.valid_for_read then
|
||||
if player.cursor_stack.type == 'blueprint' and player.cursor_stack.is_blueprint_setup() then
|
||||
local modified = landfill_gui_add_landfill(player.cursor_stack)
|
||||
|
||||
if modified and next(modified.tiles) then
|
||||
player.cursor_stack.set_blueprint_tiles(modified.tiles)
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
player.print{'landfill.cursor-none'}
|
||||
end
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_player_joined_game, landfill_init)
|
||||
BIN
exp_legacy/module/modules/gui/logo.png
Normal file
BIN
exp_legacy/module/modules/gui/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
315
exp_legacy/module/modules/gui/module.lua
Normal file
315
exp_legacy/module/modules/gui/module.lua
Normal file
@@ -0,0 +1,315 @@
|
||||
---- module inserter
|
||||
-- @gui Module
|
||||
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local config = require 'config.module' --- @dep config.module
|
||||
local Selection = require 'modules.control.selection' --- @dep modules.control.selection
|
||||
local SelectionModuleArea = 'ModuleArea'
|
||||
|
||||
--- 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 module_container
|
||||
local machine_name = {}
|
||||
|
||||
for k, _ in pairs(config.machine) do
|
||||
table.insert(machine_name, k)
|
||||
end
|
||||
|
||||
local prod_module_names = {}
|
||||
|
||||
local function get_module_name()
|
||||
for name, item in pairs(game.item_prototypes) do
|
||||
if item.module_effects and item.module_effects.productivity and item.module_effects.productivity.bonus > 0 then
|
||||
prod_module_names[#prod_module_names + 1] = name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local elem_filter = {
|
||||
name = {{
|
||||
filter = 'name',
|
||||
name = machine_name
|
||||
}},
|
||||
normal = {{
|
||||
filter = 'type',
|
||||
type = 'module'
|
||||
}, {
|
||||
filter = 'name',
|
||||
name = prod_module_names,
|
||||
mode = 'and',
|
||||
invert = true
|
||||
}},
|
||||
prod = {{
|
||||
filter = 'type',
|
||||
type = 'module'
|
||||
}}
|
||||
}
|
||||
|
||||
local function clear_module(player, area, machine)
|
||||
for _, entity in pairs(player.surface.find_entities_filtered{area=area, name=machine, force=player.force}) do
|
||||
for _, r in pairs(player.surface.find_entities_filtered{position=entity.position, name='item-request-proxy', force=player.force}) do
|
||||
if r then
|
||||
r.destroy{raise_destroy=true}
|
||||
end
|
||||
end
|
||||
|
||||
local m_current_module = entity.get_module_inventory()
|
||||
|
||||
if m_current_module then
|
||||
local m_current_module_content = m_current_module.get_contents()
|
||||
|
||||
if m_current_module_content then
|
||||
for k, m in pairs(m_current_module_content) do
|
||||
player.surface.spill_item_stack(entity.bounding_box.left_top, {name=k, count=m}, true, player.force, false)
|
||||
end
|
||||
end
|
||||
|
||||
m_current_module.clear()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function apply_module(player, area, machine, modules)
|
||||
for _, entity in pairs(player.surface.find_entities_filtered{area=area, name=machine, force=player.force}) do
|
||||
local m_current_recipe
|
||||
|
||||
if entity.prototype.crafting_speed then
|
||||
m_current_recipe= entity.get_recipe()
|
||||
end
|
||||
|
||||
if m_current_recipe then
|
||||
if config.module_allowed[m_current_recipe.name] then
|
||||
entity.surface.create_entity{name='item-request-proxy', target=entity, position=entity.position, force=entity.force, modules=modules['n']}
|
||||
entity.last_user = player
|
||||
|
||||
else
|
||||
entity.surface.create_entity{name='item-request-proxy', target=entity, position=entity.position, force=entity.force, modules=modules['p']}
|
||||
entity.last_user = player
|
||||
end
|
||||
|
||||
else
|
||||
entity.surface.create_entity{name='item-request-proxy', target=entity, position=entity.position, force=entity.force, modules=modules['n']}
|
||||
entity.last_user = player
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- when an area is selected to add protection to the area
|
||||
Selection.on_selection(SelectionModuleArea, function(event)
|
||||
local area = aabb_align_expand(event.area)
|
||||
local player = game.get_player(event.player_index)
|
||||
local frame = Gui.get_left_element(player, module_container)
|
||||
local scroll_table = frame.container.scroll.table
|
||||
|
||||
for i=1, config.default_module_row_count do
|
||||
local mma = scroll_table['module_mm_' .. i .. '_0'].elem_value
|
||||
|
||||
if mma then
|
||||
local mm = {
|
||||
['n'] = {},
|
||||
['p'] = {}
|
||||
}
|
||||
|
||||
for j=1, game.entity_prototypes[mma].module_inventory_size, 1 do
|
||||
local mmo = scroll_table['module_mm_' .. i .. '_' .. j].elem_value
|
||||
|
||||
if mmo then
|
||||
if mm['n'][mmo] then
|
||||
mm['n'][mmo] = mm['n'][mmo] + 1
|
||||
mm['p'][mmo] = mm['p'][mmo] + 1
|
||||
|
||||
else
|
||||
mm['n'][mmo] = 1
|
||||
mm['p'][mmo] = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in pairs(mm['p']) do
|
||||
if k:find('productivity') then
|
||||
local module_name = k:gsub('productivity', 'effectivity')
|
||||
mm['p'][module_name] = (mm['p'][module_name] or 0) + v
|
||||
mm['p'][k] = nil
|
||||
end
|
||||
end
|
||||
|
||||
if mm then
|
||||
clear_module(player, area, mma)
|
||||
apply_module(player, area, mma, mm)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local function row_set(player, element)
|
||||
local frame = Gui.get_left_element(player, module_container)
|
||||
local scroll_table = frame.container.scroll.table
|
||||
|
||||
if scroll_table[element .. '0'].elem_value then
|
||||
for i=1, config.module_slot_max do
|
||||
if i <= game.entity_prototypes[scroll_table[element .. '0'].elem_value].module_inventory_size then
|
||||
if config.machine[scroll_table[element .. '0'].elem_value].prod then
|
||||
scroll_table[element .. i].elem_filters = elem_filter.prod
|
||||
|
||||
else
|
||||
scroll_table[element .. i].elem_filters = elem_filter.normal
|
||||
end
|
||||
|
||||
scroll_table[element .. i].enabled = true
|
||||
scroll_table[element .. i].elem_value = config.machine[scroll_table[element .. '0'].elem_value].module
|
||||
|
||||
else
|
||||
scroll_table[element .. i].enabled = false
|
||||
scroll_table[element .. i].elem_value = nil
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
local mf = elem_filter.normal
|
||||
|
||||
for i=1, config.module_slot_max do
|
||||
scroll_table[element .. i].enabled = false
|
||||
scroll_table[element .. i].elem_filters = mf
|
||||
scroll_table[element .. i].elem_value = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local button_apply =
|
||||
Gui.element{
|
||||
type = 'button',
|
||||
caption = 'Apply',
|
||||
style = 'button'
|
||||
}:on_click(function(player)
|
||||
if Selection.is_selecting(player, SelectionModuleArea) then
|
||||
Selection.stop(player)
|
||||
else
|
||||
Selection.start(player, SelectionModuleArea)
|
||||
end
|
||||
end)
|
||||
|
||||
module_container =
|
||||
Gui.element(function(definition, parent)
|
||||
local container = Gui.container(parent, definition.name, (config.module_slot_max + 2) * 36)
|
||||
Gui.header(container, 'Module Inserter', '', true)
|
||||
|
||||
local scroll_table = Gui.scroll_table(container, (config.module_slot_max + 2) * 36, config.module_slot_max + 1)
|
||||
|
||||
for i=1, config.default_module_row_count do
|
||||
scroll_table.add{
|
||||
name = 'module_mm_' .. i .. '_0',
|
||||
type = 'choose-elem-button',
|
||||
elem_type = 'entity',
|
||||
elem_filters = elem_filter.name,
|
||||
style = 'slot_button'
|
||||
}
|
||||
|
||||
for j=1, config.module_slot_max do
|
||||
scroll_table.add{
|
||||
name = 'module_mm_' .. i .. '_' .. j,
|
||||
type = 'choose-elem-button',
|
||||
elem_type = 'item',
|
||||
elem_filters = elem_filter.normal,
|
||||
style = 'slot_button',
|
||||
enabled = false
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
button_apply(container)
|
||||
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow()
|
||||
|
||||
Gui.left_toolbar_button('item/productivity-module-3', {'module.main-tooltip'}, module_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/module')
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_gui_elem_changed, function(event)
|
||||
if event.element.name:sub(1, 10) == 'module_mm_' then
|
||||
if event.element.name:sub(-1) == '0' then
|
||||
row_set(game.players[event.player_index], 'module_mm_' .. event.element.name:sub(-3):sub(1, 1) .. '_')
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_player_joined_game, get_module_name)
|
||||
|
||||
Event.add(defines.events.on_entity_settings_pasted, function(event)
|
||||
local source = event.source
|
||||
local destination = event.destination
|
||||
local player = game.players[event.player_index]
|
||||
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
|
||||
if not source or not source.valid then
|
||||
return
|
||||
end
|
||||
|
||||
if not destination or not destination.valid then
|
||||
return
|
||||
end
|
||||
|
||||
-- rotate machine also
|
||||
if config.copy_paste_rotation then
|
||||
if (source.name == destination.name or source.prototype.fast_replaceable_group == destination.prototype.fast_replaceable_group) then
|
||||
if source.supports_direction and destination.supports_direction and source.type ~= 'transport-belt' then
|
||||
local destination_box = destination.bounding_box
|
||||
|
||||
local ltx = destination_box.left_top.x
|
||||
local lty = destination_box.left_top.y
|
||||
local rbx = destination_box.right_bottom.x
|
||||
local rby = destination_box.right_bottom.y
|
||||
|
||||
local old_direction = destination.direction
|
||||
destination.direction = source.direction
|
||||
|
||||
if ltx ~= destination_box.left_top.x or lty ~= destination_box.left_top.y or rbx ~= destination_box.right_bottom.x or rby ~= destination_box.right_bottom.y then
|
||||
destination.direction = old_direction
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if config.copy_paste_module then
|
||||
if source.name ~= destination.name then
|
||||
return
|
||||
end
|
||||
|
||||
local source_inventory = source.get_module_inventory()
|
||||
|
||||
if not source_inventory then
|
||||
return
|
||||
end
|
||||
|
||||
local source_inventory_content = source_inventory.get_contents()
|
||||
|
||||
if not source_inventory_content then
|
||||
return
|
||||
end
|
||||
|
||||
clear_module(player, destination.bounding_box, destination.name)
|
||||
|
||||
if next(source_inventory_content) ~= nil then
|
||||
apply_module(player, destination.bounding_box, destination.name, {['n']=source_inventory_content, ['p']=source_inventory_content})
|
||||
end
|
||||
end
|
||||
end)
|
||||
434
exp_legacy/module/modules/gui/player-list.lua
Normal file
434
exp_legacy/module/modules/gui/player-list.lua
Normal file
@@ -0,0 +1,434 @@
|
||||
--[[-- Gui Module - Player List
|
||||
- Adds a player list to show names and play time; also includes action buttons which can preform actions to players
|
||||
@gui Player-List
|
||||
@alias player_list
|
||||
]]
|
||||
|
||||
-- luacheck:ignore 211/Colors
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local Datastore = require 'expcore.datastore' --- @dep expcore.datastore
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.gui.player_list_actions' --- @dep config.gui.player_list_actions
|
||||
local Colors = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
local format_time = _C.format_time --- @dep expcore.common
|
||||
|
||||
--- Stores all data for the warp gui
|
||||
local PlayerListData = Datastore.connect('PlayerListData')
|
||||
PlayerListData:set_serializer(Datastore.name_serializer)
|
||||
local SelectedPlayer = PlayerListData:combine('SelectedPlayer')
|
||||
local SelectedAction = PlayerListData:combine('SelectedAction')
|
||||
|
||||
-- Set the config to use these stores
|
||||
config.set_datastores(SelectedPlayer, SelectedAction)
|
||||
|
||||
--- Button used to open the action bar
|
||||
-- @element open_action_bar
|
||||
local open_action_bar =
|
||||
Gui.element{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/expand_dots_white',
|
||||
tooltip = {'player-list.open-action-bar'},
|
||||
style = 'frame_button',
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style{
|
||||
padding = -2,
|
||||
width = 8,
|
||||
height = 14
|
||||
}
|
||||
:on_click(function(player, element, _)
|
||||
local selected_player_name = element.parent.name
|
||||
local old_selected_player_name = SelectedPlayer:get(player)
|
||||
if selected_player_name == old_selected_player_name then
|
||||
SelectedPlayer:remove(player)
|
||||
else
|
||||
SelectedPlayer:set(player, selected_player_name)
|
||||
end
|
||||
end)
|
||||
|
||||
--- Button used to close the action bar
|
||||
-- @element close_action_bar
|
||||
local close_action_bar =
|
||||
Gui.element{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/close_black',
|
||||
tooltip = {'player-list.close-action-bar'},
|
||||
style = 'slot_sized_button_red'
|
||||
}
|
||||
:style(Gui.sprite_style(30, -1, { top_margin = -1, right_margin = -1 }))
|
||||
:on_click(function(player, _)
|
||||
SelectedPlayer:remove(player)
|
||||
SelectedAction:remove(player)
|
||||
end)
|
||||
|
||||
--- Button used to confirm a reason
|
||||
-- @element reason_confirm
|
||||
local reason_confirm =
|
||||
Gui.element{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/confirm_slot',
|
||||
tooltip = {'player-list.reason-confirm'},
|
||||
style = 'slot_sized_button_green'
|
||||
}
|
||||
:style(Gui.sprite_style(30, -1, { left_margin = -2, right_margin = -1 }))
|
||||
:on_click(function(player, element)
|
||||
local reason = element.parent.entry.text
|
||||
local action_name = SelectedAction:get(player)
|
||||
local reason_callback = config.buttons[action_name].reason_callback
|
||||
if reason == nil or not reason:find("%S") then reason = 'no reason given' end
|
||||
reason_callback(player, reason)
|
||||
SelectedPlayer:remove(player)
|
||||
SelectedAction:remove(player)
|
||||
element.parent.entry.text = ''
|
||||
end)
|
||||
|
||||
--- Set of elements that are used to make up a row of the player table
|
||||
-- @element add_player_base
|
||||
local add_player_base =
|
||||
Gui.element(function(_, parent, player_data)
|
||||
-- Add the button to open the action bar
|
||||
local toggle_action_bar_flow = parent.add{ type = 'flow', name = player_data.name }
|
||||
open_action_bar(toggle_action_bar_flow)
|
||||
|
||||
-- Add the player name
|
||||
local player_name = parent.add{
|
||||
type = 'label',
|
||||
name = 'player-name-'..player_data.index,
|
||||
caption = player_data.name,
|
||||
tooltip = {'player-list.open-map', player_data.name, player_data.tag, player_data.role_name}
|
||||
}
|
||||
player_name.style.padding = {0, 2,0, 0}
|
||||
player_name.style.font_color = player_data.chat_color
|
||||
|
||||
-- Add the time played label
|
||||
local alignment = Gui.alignment(parent, 'player-time-'..player_data.index)
|
||||
local time_label = alignment.add{
|
||||
name = 'label',
|
||||
type = 'label',
|
||||
caption = player_data.caption,
|
||||
tooltip = player_data.tooltip
|
||||
}
|
||||
time_label.style.padding = 0
|
||||
|
||||
return player_name
|
||||
end)
|
||||
:on_click(function(player, element, event)
|
||||
local selected_player_name = element.caption
|
||||
local selected_player = game.players[selected_player_name]
|
||||
if event.button == defines.mouse_button_type.left then
|
||||
-- LMB will open the map to the selected player
|
||||
local position = selected_player.position
|
||||
event.player.zoom_to_world(position, 1.75)
|
||||
else
|
||||
-- RMB will toggle the settings
|
||||
local old_selected_player_name = SelectedPlayer:get(player)
|
||||
if selected_player_name == old_selected_player_name then
|
||||
SelectedPlayer:remove(player)
|
||||
SelectedAction:remove(player)
|
||||
else
|
||||
SelectedPlayer:set(player, selected_player_name)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Removes the three elements that are added as part of the base
|
||||
local function remove_player_base(parent, player)
|
||||
Gui.destroy_if_valid(parent[player.name])
|
||||
Gui.destroy_if_valid(parent['player-name-'..player.index])
|
||||
Gui.destroy_if_valid(parent['player-time-'..player.index])
|
||||
end
|
||||
|
||||
-- Update the time label for a player using there player time data
|
||||
local function update_player_base(parent, player_time)
|
||||
local time_element = parent[player_time.element_name]
|
||||
if time_element and time_element.valid then
|
||||
time_element.label.caption = player_time.caption
|
||||
time_element.label.tooltip = player_time.tooltip
|
||||
end
|
||||
end
|
||||
|
||||
--- Adds all the buttons and flows that make up the action bar
|
||||
-- @element add_action_bar
|
||||
local add_action_bar_buttons =
|
||||
Gui.element(function(_, parent)
|
||||
close_action_bar(parent)
|
||||
-- Loop over all the buttons in the config
|
||||
for action_name, button_data in pairs(config.buttons) do
|
||||
-- Added the permission flow
|
||||
local permission_flow = parent.add{ type = 'flow', name = action_name }
|
||||
permission_flow.visible = false
|
||||
-- Add the buttons under that permission
|
||||
for _, button in ipairs(button_data) do
|
||||
button(permission_flow)
|
||||
end
|
||||
end
|
||||
|
||||
return parent
|
||||
end)
|
||||
|
||||
--- Updates the visible state of the action bar buttons
|
||||
local function update_action_bar(element)
|
||||
local player = Gui.get_player_from_element(element)
|
||||
local selected_player_name = SelectedPlayer:get(player)
|
||||
|
||||
if not selected_player_name then
|
||||
-- Hide the action bar when no player is selected
|
||||
element.visible = false
|
||||
|
||||
else
|
||||
local selected_player = game.players[selected_player_name]
|
||||
if not selected_player.connected then
|
||||
-- If the player is offline then reest stores
|
||||
element.visible = false
|
||||
SelectedPlayer:remove(player)
|
||||
SelectedAction:remove(player)
|
||||
|
||||
else
|
||||
-- Otherwise check what actions the player is allowed to use
|
||||
element.visible = true
|
||||
for action_name, buttons in pairs(config.buttons) do
|
||||
if buttons.auth and not buttons.auth(player, selected_player) then
|
||||
element[action_name].visible = false
|
||||
elseif Roles.player_allowed(player, action_name) then
|
||||
element[action_name].visible = true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Main player list container for the left flow
|
||||
-- @element player_list_container
|
||||
local player_list_container =
|
||||
Gui.element(function(definition, parent)
|
||||
-- Draw the internal container
|
||||
local container = Gui.container(parent, definition.name, 200)
|
||||
|
||||
-- Draw the scroll table for the players
|
||||
local scroll_table = Gui.scroll_table(container, 184, 3)
|
||||
|
||||
-- Change the style of the scroll table
|
||||
local scroll_table_style = scroll_table.style
|
||||
scroll_table_style.padding = {1, 0,1, 2}
|
||||
|
||||
-- Add the action bar
|
||||
local action_bar = Gui.footer(container, nil, nil, false, 'action_bar')
|
||||
|
||||
-- Change the style of the action bar
|
||||
local action_bar_style = action_bar.style
|
||||
action_bar_style.height = 35
|
||||
action_bar_style.padding = {1, 3}
|
||||
action_bar.visible = false
|
||||
|
||||
-- Add the buttons to the action bar
|
||||
add_action_bar_buttons(action_bar)
|
||||
|
||||
-- Add the reason bar
|
||||
local reason_bar = Gui.footer(container, nil, nil, false, 'reason_bar')
|
||||
|
||||
-- Change the style of the reason bar
|
||||
local reason_bar_style = reason_bar.style
|
||||
reason_bar_style.height = 35
|
||||
reason_bar_style.padding = {-1, 3}
|
||||
reason_bar.visible = false
|
||||
|
||||
-- Add the text entry for the reason bar
|
||||
local reason_field =
|
||||
reason_bar.add{
|
||||
name = 'entry',
|
||||
type = 'textfield',
|
||||
style = 'stretchable_textfield',
|
||||
tooltip = {'player-list.reason-entry'}
|
||||
}
|
||||
|
||||
-- Change the style of the text entry
|
||||
local reason_entry_style = reason_field.style
|
||||
reason_entry_style.padding = 0
|
||||
reason_entry_style.height = 28
|
||||
reason_entry_style.minimal_width = 160
|
||||
|
||||
-- Add the confirm reason button
|
||||
reason_confirm(reason_bar)
|
||||
|
||||
-- Return the exteral container
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow(true)
|
||||
|
||||
--- Button on the top flow used to toggle the player list container
|
||||
-- @element toggle_player_list
|
||||
Gui.left_toolbar_button('entity/character', {'player-list.main-tooltip'}, player_list_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/player-list')
|
||||
end)
|
||||
|
||||
-- Get caption and tooltip format for a player
|
||||
local function get_time_formats(online_time, afk_time)
|
||||
local tick = game.tick > 0 and game.tick or 1
|
||||
local percent = math.round(online_time/tick, 3)*100
|
||||
local caption = format_time(online_time)
|
||||
local tooltip = {'player-list.afk-time', percent, format_time(afk_time, {minutes=true, long=true})}
|
||||
return caption, tooltip
|
||||
end
|
||||
|
||||
-- Get the player time to be used to update time label
|
||||
local function get_player_times()
|
||||
local ctn = 0
|
||||
local player_times = {}
|
||||
for _, player in pairs(game.connected_players) do
|
||||
ctn = ctn + 1
|
||||
-- Add the player time details to the array
|
||||
local caption, tooltip = get_time_formats(player.online_time, player.afk_time)
|
||||
player_times[ctn] = {
|
||||
element_name = 'player-time-'..player.index,
|
||||
caption = caption,
|
||||
tooltip = tooltip
|
||||
}
|
||||
end
|
||||
|
||||
return player_times
|
||||
end
|
||||
|
||||
-- Get a sorted list of all online players
|
||||
local function get_player_list_order()
|
||||
-- Sort all the online players into roles
|
||||
local players = {}
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local highest_role = Roles.get_player_highest_role(player)
|
||||
if not players[highest_role.name] then
|
||||
players[highest_role.name] = {}
|
||||
end
|
||||
table.insert(players[highest_role.name], player)
|
||||
end
|
||||
|
||||
-- Sort the players from roles into a set order
|
||||
local ctn = 0
|
||||
local player_list_order = {}
|
||||
for _, role_name in pairs(Roles.config.order) do
|
||||
if players[role_name] then
|
||||
for _, player in pairs(players[role_name]) do
|
||||
ctn = ctn + 1
|
||||
-- Add the player data to the array
|
||||
local caption, tooltip = get_time_formats(player.online_time, player.afk_time)
|
||||
player_list_order[ctn] = {
|
||||
name = player.name,
|
||||
index = player.index,
|
||||
tag = player.tag,
|
||||
role_name = role_name,
|
||||
chat_color = player.chat_color,
|
||||
caption = caption,
|
||||
tooltip = tooltip
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[Adds fake players to the player list
|
||||
local tick = game.tick+1
|
||||
for i = 1, 10 do
|
||||
local online_time = math.random(1, tick)
|
||||
local afk_time = math.random(online_time-(tick/10), tick)
|
||||
local caption, tooltip = get_time_formats(online_time, afk_time)
|
||||
player_list_order[ctn+i] = {
|
||||
name='Player '..i,
|
||||
index=0-i,
|
||||
tag='',
|
||||
role_name = 'Fake Player',
|
||||
chat_color = table.get_random_dictionary_entry(Colors),
|
||||
caption = caption,
|
||||
tooltip = tooltip
|
||||
}
|
||||
end--]]
|
||||
|
||||
return player_list_order
|
||||
end
|
||||
|
||||
--- Update the play times every 30 sections
|
||||
Event.on_nth_tick(1800, function()
|
||||
local player_times = get_player_times()
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local frame = Gui.get_left_element(player, player_list_container)
|
||||
local scroll_table = frame.container.scroll.table
|
||||
for _, player_time in pairs(player_times) do
|
||||
update_player_base(scroll_table, player_time)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--- When a player leaves only remove they entry
|
||||
Event.add(defines.events.on_player_left_game, function(event)
|
||||
local remove_player = game.players[event.player_index]
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local frame = Gui.get_left_element(player, player_list_container)
|
||||
local scroll_table = frame.container.scroll.table
|
||||
remove_player_base(scroll_table, remove_player)
|
||||
|
||||
local selected_player_name = SelectedPlayer:get(player)
|
||||
if selected_player_name == remove_player.name then
|
||||
SelectedPlayer:remove(player)
|
||||
SelectedAction:remove(player)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--- All other events require a full redraw of the table
|
||||
local function redraw_player_list()
|
||||
local player_list_order = get_player_list_order()
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local frame = Gui.get_left_element(player, player_list_container)
|
||||
local scroll_table = frame.container.scroll.table
|
||||
scroll_table.clear()
|
||||
for _, next_player_data in ipairs(player_list_order) do
|
||||
add_player_base(scroll_table, next_player_data)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_player_joined_game, redraw_player_list)
|
||||
Event.add(Roles.events.on_role_assigned, redraw_player_list)
|
||||
Event.add(Roles.events.on_role_unassigned, redraw_player_list)
|
||||
|
||||
--- When the action player is changed the action bar will update
|
||||
SelectedPlayer:on_update(function(player_name, selected_player)
|
||||
local player = game.players[player_name]
|
||||
local frame = Gui.get_left_element(player, player_list_container)
|
||||
local scroll_table = frame.container.scroll.table
|
||||
update_action_bar(frame.container.action_bar)
|
||||
for _, next_player in pairs(game.connected_players) do
|
||||
local element = scroll_table[next_player.name][open_action_bar.name]
|
||||
local style = 'frame_button'
|
||||
if next_player.name == selected_player then
|
||||
style = 'tool_button'
|
||||
end
|
||||
element.style = style
|
||||
local element_style = element.style
|
||||
element_style.padding = -2
|
||||
element_style.width = 8
|
||||
element_style.height = 14
|
||||
end
|
||||
end)
|
||||
|
||||
--- When the action name is changed the reason input will update
|
||||
SelectedAction:on_update(function(player_name, selected_action)
|
||||
local player = game.players[player_name]
|
||||
local frame = Gui.get_left_element(player, player_list_container)
|
||||
local element = frame.container.reason_bar
|
||||
if selected_action then
|
||||
-- if there is a new value then check the player is still online
|
||||
local selected_player_name = SelectedPlayer:get(player_name)
|
||||
local selected_player = game.players[selected_player_name]
|
||||
if selected_player.connected then
|
||||
element.visible = true
|
||||
else
|
||||
-- Clear if the player is offline
|
||||
SelectedPlayer:remove(player)
|
||||
SelectedAction:remove(player)
|
||||
end
|
||||
|
||||
else
|
||||
element.visible = false
|
||||
|
||||
end
|
||||
end)
|
||||
219
exp_legacy/module/modules/gui/playerdata.lua
Normal file
219
exp_legacy/module/modules/gui/playerdata.lua
Normal file
@@ -0,0 +1,219 @@
|
||||
---- module pd
|
||||
-- @gui PlayerData
|
||||
|
||||
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 PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
require 'modules.data.statistics'
|
||||
local format_time = _C.format_time --- @dep expcore.common
|
||||
local format_number = require('util').format_number --- @dep util
|
||||
|
||||
local pd_container
|
||||
local label_width = {
|
||||
['name'] = 135,
|
||||
['count'] = 105,
|
||||
['total'] = 480
|
||||
}
|
||||
|
||||
local function format_time_short(value)
|
||||
return format_time(value*3600, {
|
||||
hours=true,
|
||||
minutes=true,
|
||||
seconds=false
|
||||
})
|
||||
end
|
||||
|
||||
local function format_number_n(n)
|
||||
return format_number(math.floor(n)) .. string.format('%.2f', n % 1):sub(2)
|
||||
end
|
||||
|
||||
local playerStats = PlayerData.Statistics
|
||||
local computed_stats = {
|
||||
DamageDeathRatio = {
|
||||
default = format_number_n(0),
|
||||
calculate = function(player_name)
|
||||
return format_number_n(playerStats['DamageDealt']:get(player_name, 0) / playerStats['Deaths']:get(player_name, 1))
|
||||
end
|
||||
},
|
||||
KillDeathRatio = {
|
||||
default = format_number_n(0),
|
||||
calculate = function(player_name)
|
||||
return format_number_n(playerStats['Kills']:get(player_name, 0) / playerStats['Deaths']:get(player_name, 1))
|
||||
end
|
||||
},
|
||||
SessionTime = {
|
||||
default = format_time_short(0),
|
||||
calculate = function(player_name)
|
||||
return format_time_short((playerStats['Playtime']:get(player_name, 0) - playerStats['AfkTime']:get(player_name, 0)) / playerStats['JoinCount']:get(player_name, 1))
|
||||
end
|
||||
},
|
||||
BuildRatio = {
|
||||
default = format_number_n(0),
|
||||
calculate = function(player_name)
|
||||
return format_number_n(playerStats['MachinesBuilt']:get(player_name, 0) / playerStats['MachinesRemoved']:get(player_name, 1))
|
||||
end
|
||||
},
|
||||
RocketPerHour = {
|
||||
default = format_number_n(0),
|
||||
calculate = function(player_name)
|
||||
return format_number_n(playerStats['RocketsLaunched']:get(player_name, 0) * 60 / playerStats['Playtime']:get(player_name, 1))
|
||||
end
|
||||
},
|
||||
TreeKillPerMinute = {
|
||||
default = format_number_n(0),
|
||||
calculate = function(player_name)
|
||||
return format_number_n(playerStats['TreesDestroyed']:get(player_name, 0) / playerStats['Playtime']:get(player_name, 1))
|
||||
end
|
||||
},
|
||||
NetPlayTime = {
|
||||
default = format_time_short(0),
|
||||
calculate = function(player_name)
|
||||
return format_time_short((playerStats['Playtime']:get(player_name, 0) - playerStats['AfkTime']:get(player_name, 0)))
|
||||
end
|
||||
},
|
||||
AFKTimeRatio = {
|
||||
default = format_number_n(0),
|
||||
calculate = function(player_name)
|
||||
return format_number_n(playerStats['AfkTime']:get(player_name, 0) * 100 / playerStats['Playtime']:get(player_name, 1))
|
||||
end
|
||||
},
|
||||
}
|
||||
|
||||
local label =
|
||||
Gui.element(function(_, parent, width, caption, tooltip, name)
|
||||
local new_label = parent.add{
|
||||
type = 'label',
|
||||
caption = caption,
|
||||
tooltip = tooltip,
|
||||
name = name,
|
||||
style = 'heading_2_label'
|
||||
}
|
||||
|
||||
new_label.style.width = width
|
||||
return new_label
|
||||
end)
|
||||
|
||||
local pd_data_set =
|
||||
Gui.element(function(_, parent, name)
|
||||
local pd_data_set = parent.add{type='flow', direction='vertical', name=name}
|
||||
local disp = Gui.scroll_table(pd_data_set, label_width['total'], 4, 'disp')
|
||||
|
||||
for _, stat_name in pairs(PlayerData.Statistics.metadata.display_order) do
|
||||
local child = PlayerData.Statistics[stat_name]
|
||||
local metadata = child.metadata
|
||||
local value = metadata.stringify_short and metadata.stringify_short(0) or metadata.stringify and metadata.stringify(0) or format_number(0)
|
||||
label(disp, label_width['name'], metadata.name or {'exp-statistics.'..stat_name}, metadata.tooltip or {'exp-statistics.'..stat_name..'-tooltip'})
|
||||
label(disp, label_width['count'], {'readme.data-format', value, metadata.unit or ''}, metadata.value_tooltip or {'exp-statistics.'..stat_name..'-tooltip'}, stat_name)
|
||||
end
|
||||
|
||||
for stat_name, data in pairs(computed_stats) do
|
||||
label(disp, label_width['name'], {'exp-statistics.'..stat_name}, {'exp-statistics.'..stat_name..'-tooltip'})
|
||||
label(disp, label_width['count'], {'readme.data-format', data.default, ''}, {'exp-statistics.'..stat_name..'-tooltip'}, stat_name)
|
||||
end
|
||||
|
||||
return pd_data_set
|
||||
end)
|
||||
|
||||
local function pd_update(table, player_name)
|
||||
for _, stat_name in pairs(PlayerData.Statistics.metadata.display_order) do
|
||||
local child = PlayerData.Statistics[stat_name]
|
||||
local metadata = child.metadata
|
||||
local value = child:get(player_name)
|
||||
if metadata.stringify_short then
|
||||
value = metadata.stringify_short(value or 0)
|
||||
elseif metadata.stringify then
|
||||
value = metadata.stringify(value or 0)
|
||||
else
|
||||
value = format_number(value or 0)
|
||||
end
|
||||
table[stat_name].caption = {'readme.data-format', value, metadata.unit or ''}
|
||||
end
|
||||
|
||||
for stat_name, data in pairs(computed_stats) do
|
||||
table[stat_name].caption = {'readme.data-format', data.calculate(player_name), ''}
|
||||
end
|
||||
end
|
||||
|
||||
local pd_username_player =
|
||||
Gui.element(function(definition, parent, player_list)
|
||||
return parent.add{
|
||||
name = definition.name,
|
||||
type = 'drop-down',
|
||||
items = player_list,
|
||||
selected_index = #player_list > 0 and 1
|
||||
}
|
||||
end)
|
||||
:style{
|
||||
horizontally_stretchable = true
|
||||
}:on_selection_changed(function(_, element, _)
|
||||
local player_name = game.connected_players[element.selected_index]
|
||||
local table = element.parent.parent.parent.parent['pd_st_2'].disp.table
|
||||
pd_update(table, player_name)
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
|
||||
local pd_username_update =
|
||||
Gui.element{
|
||||
type = 'button',
|
||||
name = Gui.unique_static_name,
|
||||
caption = 'update'
|
||||
}:style{
|
||||
width = 128
|
||||
}:on_click(function(_, element, _)
|
||||
local player_index = element.parent[pd_username_player.name].selected_index
|
||||
|
||||
if player_index > 0 then
|
||||
local player_name = game.connected_players[player_index]
|
||||
local table = element.parent.parent.parent.parent['pd_st_2'].disp.table
|
||||
pd_update(table, player_name)
|
||||
end
|
||||
end)
|
||||
|
||||
local pd_username_set =
|
||||
Gui.element(function(_, parent, name, player_list)
|
||||
local pd_username_set = parent.add{type='flow', direction='vertical', name=name}
|
||||
local disp = Gui.scroll_table(pd_username_set, label_width['total'], 2, 'disp')
|
||||
|
||||
pd_username_player(disp, player_list)
|
||||
pd_username_update(disp)
|
||||
|
||||
return pd_username_set
|
||||
end)
|
||||
|
||||
pd_container =
|
||||
Gui.element(function(definition, parent)
|
||||
local container = Gui.container(parent, definition.name, label_width['total'])
|
||||
local player_list = {}
|
||||
|
||||
for _, player in pairs(game.connected_players) do
|
||||
table.insert(player_list, player.name)
|
||||
end
|
||||
|
||||
pd_username_set(container, 'pd_st_1', player_list)
|
||||
pd_data_set(container, 'pd_st_2')
|
||||
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow()
|
||||
|
||||
Gui.left_toolbar_button('item/power-armor-mk2', 'Player Data GUI', pd_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/playerdata')
|
||||
end)
|
||||
|
||||
local function gui_player_list_update()
|
||||
local player_list = {}
|
||||
|
||||
for _, player in pairs(game.connected_players) do
|
||||
table.insert(player_list, player.name)
|
||||
end
|
||||
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local frame = Gui.get_left_element(player, pd_container)
|
||||
frame.container['pd_st_1'].disp.table[pd_username_player.name].items = player_list
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_player_joined_game, gui_player_list_update)
|
||||
Event.add(defines.events.on_player_left_game, gui_player_list_update)
|
||||
165
exp_legacy/module/modules/gui/production.lua
Normal file
165
exp_legacy/module/modules/gui/production.lua
Normal file
@@ -0,0 +1,165 @@
|
||||
---- Production Data
|
||||
-- @gui Production
|
||||
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
|
||||
local production_container
|
||||
|
||||
local precision = {
|
||||
[1] = defines.flow_precision_index.five_seconds,
|
||||
[2] = defines.flow_precision_index.one_minute,
|
||||
[3] = defines.flow_precision_index.ten_minutes,
|
||||
[4] = defines.flow_precision_index.one_hour,
|
||||
[5] = defines.flow_precision_index.ten_hours
|
||||
}
|
||||
|
||||
local font_color = {
|
||||
-- positive
|
||||
[1] = {r = 0.3, g = 1, b = 0.3},
|
||||
-- negative
|
||||
[2] = {r = 1, g = 0.3, b = 0.3}
|
||||
}
|
||||
|
||||
local function format_n(n)
|
||||
local _i, _j, m, i, f = string.format('%.1f', n):find('([-]?)(%d+)([.]?%d*)')
|
||||
i = i:reverse():gsub('(%d%d%d)', '%1,')
|
||||
|
||||
if f ~= '' then
|
||||
return m .. i:reverse():gsub('^,', '') .. f
|
||||
else
|
||||
return m .. i:reverse():gsub('^,', '') .. '.0'
|
||||
end
|
||||
end
|
||||
|
||||
--- Display group
|
||||
-- @element production_data_group
|
||||
local production_data_group =
|
||||
Gui.element(function(_definition, parent, i)
|
||||
local item
|
||||
|
||||
if i == 0 then
|
||||
item = parent.add{
|
||||
type = 'drop-down',
|
||||
name = 'production_0_e',
|
||||
items = {'5s', '1m', '10m', '1h', '10h'},
|
||||
selected_index = 3
|
||||
}
|
||||
item.style.width = 80
|
||||
|
||||
else
|
||||
item = parent.add{
|
||||
type = 'choose-elem-button',
|
||||
name = 'production_' .. i .. '_e',
|
||||
elem_type = 'item',
|
||||
style = 'slot_button'
|
||||
}
|
||||
item.style.height = 32
|
||||
item.style.width = 32
|
||||
end
|
||||
|
||||
local data_1 = parent.add{
|
||||
type = 'label',
|
||||
name = 'production_' .. i .. '_1',
|
||||
caption = '0.0',
|
||||
style = 'heading_2_label'
|
||||
}
|
||||
data_1.style.width = 90
|
||||
data_1.style.horizontal_align = 'right'
|
||||
data_1.style.font_color = font_color[1]
|
||||
|
||||
local data_2 = parent.add{
|
||||
type = 'label',
|
||||
name = 'production_' .. i .. '_2',
|
||||
caption = '0.0',
|
||||
style = 'heading_2_label'
|
||||
}
|
||||
data_2.style.width = 90
|
||||
data_2.style.horizontal_align = 'right'
|
||||
data_2.style.font_color = font_color[2]
|
||||
|
||||
local data_3 = parent.add{
|
||||
type = 'label',
|
||||
name = 'production_' .. i .. '_3',
|
||||
caption = '0.0',
|
||||
style = 'heading_2_label'
|
||||
}
|
||||
data_3.style.width = 90
|
||||
data_3.style.horizontal_align = 'right'
|
||||
data_3.style.font_color = font_color[1]
|
||||
|
||||
return item
|
||||
end)
|
||||
|
||||
--- A vertical flow containing all the production data
|
||||
-- @element production_data_set
|
||||
local production_data_set =
|
||||
Gui.element(function(_, parent, name)
|
||||
local production_set = parent.add{type='flow', direction='vertical', name=name}
|
||||
local disp = Gui.scroll_table(production_set, 350, 4, 'disp')
|
||||
|
||||
production_data_group(disp, 0)
|
||||
|
||||
disp['production_0_1'].caption = {'production.label-prod'}
|
||||
disp['production_0_2'].caption = {'production.label-con'}
|
||||
disp['production_0_3'].caption = {'production.label-bal'}
|
||||
|
||||
for i=1, 8 do
|
||||
production_data_group(disp, i)
|
||||
end
|
||||
|
||||
return production_set
|
||||
end)
|
||||
|
||||
production_container =
|
||||
Gui.element(function(definition, parent)
|
||||
local container = Gui.container(parent, definition.name, 350)
|
||||
|
||||
production_data_set(container, 'production_st')
|
||||
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow()
|
||||
|
||||
Gui.left_toolbar_button('entity/assembling-machine-3', {'production.main-tooltip'}, production_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/production')
|
||||
end)
|
||||
|
||||
Event.on_nth_tick(60, function()
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local frame = Gui.get_left_element(player, production_container)
|
||||
local stat = player.force.item_production_statistics
|
||||
local precision_value = precision[frame.container['production_st'].disp.table['production_0_e'].selected_index]
|
||||
local table = frame.container['production_st'].disp.table
|
||||
|
||||
for i=1, 8 do
|
||||
local production_prefix = 'production_' .. i
|
||||
local item = table[production_prefix .. '_e'].elem_value
|
||||
|
||||
if item then
|
||||
local add = stat.get_flow_count{name=item, input=true, precision_index=precision_value, count=false} / 60
|
||||
local minus = stat.get_flow_count{name=item, input=false, precision_index=precision_value, count=false} / 60
|
||||
local sum = add - minus
|
||||
|
||||
table[production_prefix .. '_1'].caption = format_n(add)
|
||||
table[production_prefix .. '_2'].caption = format_n(minus)
|
||||
table[production_prefix .. '_3'].caption = format_n(sum)
|
||||
|
||||
if sum < 0 then
|
||||
table[production_prefix .. '_3'].style.font_color = font_color[2]
|
||||
|
||||
else
|
||||
table[production_prefix .. '_3'].style.font_color = font_color[1]
|
||||
end
|
||||
|
||||
else
|
||||
table[production_prefix .. '_1'].caption = '0.0'
|
||||
table[production_prefix .. '_2'].caption = '0.0'
|
||||
table[production_prefix .. '_3'].caption = '0.0'
|
||||
table[production_prefix .. '_3'].style.font_color = font_color[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
485
exp_legacy/module/modules/gui/readme.lua
Normal file
485
exp_legacy/module/modules/gui/readme.lua
Normal file
@@ -0,0 +1,485 @@
|
||||
--[[-- Gui Module - Readme
|
||||
- Adds a main gui that contains lots of important information about our server
|
||||
@gui Readme
|
||||
@alias readme
|
||||
]]
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
local External = require 'expcore.external' --- @dep expcore.external
|
||||
local format_time = _C.format_time --- @dep expcore.common
|
||||
local format_number = require('util').format_number --- @dep util
|
||||
|
||||
local tabs = {}
|
||||
local function Tab(caption, tooltip, element_define)
|
||||
tabs[#tabs+1] = {caption, tooltip, element_define}
|
||||
end
|
||||
|
||||
local frame_width = 595 -- controls width of top descriptions
|
||||
local title_width = 270 -- controls the centering of the titles
|
||||
local scroll_height = 275 -- controls the height of the scrolls
|
||||
|
||||
--- Sub content area used within the content areas
|
||||
-- @element sub_content
|
||||
local sub_content =
|
||||
Gui.element{
|
||||
type = 'frame',
|
||||
direction = 'vertical',
|
||||
style = 'inside_deep_frame'
|
||||
}
|
||||
:style{
|
||||
horizontally_stretchable = true,
|
||||
horizontal_align = 'center',
|
||||
padding = {2, 2},
|
||||
top_margin = 2
|
||||
}
|
||||
|
||||
--- Table which has a title above it above it
|
||||
-- @element title_table
|
||||
local title_table =
|
||||
Gui.element(function(_, parent, bar_size, caption, column_count)
|
||||
Gui.title_label(parent, bar_size, caption)
|
||||
|
||||
return parent.add{
|
||||
type = 'table',
|
||||
column_count = column_count,
|
||||
style = 'bordered_table'
|
||||
}
|
||||
end)
|
||||
:style{
|
||||
padding = 0,
|
||||
cell_padding = 0,
|
||||
vertical_align = 'center',
|
||||
horizontally_stretchable = true
|
||||
}
|
||||
|
||||
--- Scroll to be used with Gui.title_label tables
|
||||
-- @element title_table_scroll
|
||||
local title_table_scroll =
|
||||
Gui.element{
|
||||
type = 'scroll-pane',
|
||||
direction = 'vertical',
|
||||
horizontal_scroll_policy = 'never',
|
||||
vertical_scroll_policy = 'auto',
|
||||
style = 'scroll_pane_under_subheader'
|
||||
}
|
||||
:style{
|
||||
padding = {1, 3},
|
||||
maximal_height = scroll_height,
|
||||
horizontally_stretchable = true,
|
||||
}
|
||||
|
||||
--- Used to connect to servers in server list
|
||||
-- @element join_server
|
||||
local join_server =
|
||||
Gui.element(function(_, parent, server_id, wrong_version)
|
||||
local status = External.get_server_status(server_id) or 'Offline'
|
||||
if wrong_version then status = 'Version' end
|
||||
local flow = parent.add{ name = server_id, type = 'flow' }
|
||||
local button = flow.add{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/circuit_network_panel_white', --- network panel white, warning white, download white
|
||||
hovered_sprite = 'utility/circuit_network_panel_black', --- network panel black, warning black, download black
|
||||
tooltip = {'readme.servers-connect-'..status, wrong_version}
|
||||
}
|
||||
|
||||
if status == 'Offline' or status == 'Current' then
|
||||
button.enabled = false
|
||||
button.sprite = 'utility/circuit_network_panel_black'
|
||||
elseif status == 'Version' then
|
||||
button.enabled = false
|
||||
button.sprite = 'utility/shuffle'
|
||||
elseif status == 'Password' then
|
||||
button.sprite = 'utility/warning_white'
|
||||
button.hovered_sprite = 'utility/warning'
|
||||
elseif status == 'Modded' then
|
||||
button.sprite = 'utility/downloading_white'
|
||||
button.hovered_sprite = 'utility/downloading'
|
||||
end
|
||||
|
||||
return button
|
||||
end)
|
||||
:style(Gui.sprite_style(20, -1))
|
||||
:on_click(function(player, element, _)
|
||||
local server_id = element.parent.name
|
||||
External.request_connection(player, server_id, true)
|
||||
end)
|
||||
|
||||
--- Content area for the welcome tab
|
||||
-- @element welcome_content
|
||||
Tab({'readme.welcome-tab'}, {'readme.welcome-tooltip'},
|
||||
Gui.element(function(_, parent)
|
||||
local server_details = { name='ExpGaming S0 - Local', welcome='Failed to load description: disconnected from external api.', reset_time='Non Set', branch='Unknown'}
|
||||
if External.valid() then server_details = External.get_current_server() end
|
||||
local container = parent.add{ type='flow', direction='vertical' }
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
|
||||
-- Set up the top flow with logos
|
||||
local top_flow = container.add{ type='flow' }
|
||||
top_flow.add{ type='sprite', sprite='file/modules/gui/logo.png' }
|
||||
local top_vertical_flow = top_flow.add{ type='flow', direction='vertical' }
|
||||
top_flow.add{ type='sprite', sprite='file/modules/gui/logo.png' }
|
||||
top_vertical_flow.style.horizontal_align = 'center'
|
||||
|
||||
-- Add the title and description to the top flow
|
||||
Gui.title_label(top_vertical_flow, 62, 'Welcome to '..server_details.name)
|
||||
Gui.centered_label(top_vertical_flow, 380, server_details.welcome)
|
||||
Gui.bar(container)
|
||||
|
||||
-- Get the names of the roles the player has
|
||||
local player_roles = Roles.get_player_roles(player)
|
||||
local role_names = {}
|
||||
for i, role in ipairs(player_roles) do
|
||||
role_names[i] = role.name
|
||||
end
|
||||
|
||||
-- Add the other information to the gui
|
||||
container.add{ type='flow' }.style.height = 4
|
||||
local online_time = format_time(game.tick, {days=true, hours=true, minutes=true, long=true})
|
||||
Gui.centered_label(sub_content(container), frame_width, {'readme.welcome-general', server_details.reset_time, online_time})
|
||||
Gui.centered_label(sub_content(container), frame_width, {'readme.welcome-roles', table.concat(role_names, ', ')})
|
||||
Gui.centered_label(sub_content(container), frame_width, {'readme.welcome-chat'})
|
||||
|
||||
return container
|
||||
end))
|
||||
|
||||
--- Content area for the rules tab
|
||||
-- @element rules_content
|
||||
Tab({'readme.rules-tab'}, {'readme.rules-tooltip'},
|
||||
Gui.element(function(_, parent)
|
||||
local container = parent.add{ type='flow', direction='vertical' }
|
||||
|
||||
-- Add the title and description to the content
|
||||
Gui.title_label(container, title_width-3, {'readme.rules-tab'})
|
||||
Gui.centered_label(container, frame_width, {'readme.rules-general'})
|
||||
Gui.bar(container)
|
||||
container.add{ type='flow' }
|
||||
|
||||
-- Add a table for the rules
|
||||
local rules = Gui.scroll_table(container, scroll_height, 1)
|
||||
rules.style = 'bordered_table'
|
||||
rules.style.cell_padding = 4
|
||||
|
||||
-- Add the rules to the table
|
||||
for i = 1, 15 do
|
||||
Gui.centered_label(rules, 565, {'readme.rules-'..i})
|
||||
end
|
||||
|
||||
return container
|
||||
end))
|
||||
|
||||
--- Content area for the commands tab
|
||||
-- @element commands_content
|
||||
Tab({'readme.commands-tab'}, {'readme.commands-tooltip'},
|
||||
Gui.element(function(_, parent)
|
||||
local container = parent.add{ type='flow', direction='vertical' }
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
|
||||
-- Add the title and description to the content
|
||||
Gui.title_label(container, title_width-20, {'readme.commands-tab'})
|
||||
Gui.centered_label(container, frame_width, {'readme.commands-general'})
|
||||
Gui.bar(container)
|
||||
container.add{ type='flow' }
|
||||
|
||||
-- Add a table for the commands
|
||||
local commands = Gui.scroll_table(container, scroll_height, 2)
|
||||
commands.style = 'bordered_table'
|
||||
commands.style.cell_padding = 0
|
||||
|
||||
-- Add the rules to the table
|
||||
for name, command in pairs(Commands.get(player)) do
|
||||
Gui.centered_label(commands, 120, name)
|
||||
Gui.centered_label(commands, 450, command.help)
|
||||
end
|
||||
|
||||
return container
|
||||
end))
|
||||
|
||||
--- Content area for the servers tab
|
||||
-- @element servers_content
|
||||
Tab({'readme.servers-tab'}, {'readme.servers-tooltip'},
|
||||
Gui.element(function(_, parent)
|
||||
local container = parent.add{ type='flow', direction='vertical' }
|
||||
|
||||
-- Add the title and description to the content
|
||||
Gui.title_label(container, title_width-10, {'readme.servers-tab'})
|
||||
Gui.centered_label(container, frame_width, {'readme.servers-general'})
|
||||
Gui.bar(container)
|
||||
container.add{ type='flow' }
|
||||
|
||||
-- Draw the scroll
|
||||
local scroll_pane = title_table_scroll(container)
|
||||
scroll_pane.style.maximal_height = scroll_height + 20 -- the text is a bit shorter
|
||||
|
||||
-- Add the factorio servers
|
||||
if External.valid() then
|
||||
local factorio_servers = title_table(scroll_pane, 225, {'readme.servers-factorio'}, 3)
|
||||
local current_version = External.get_current_server().version
|
||||
for server_id, server in pairs(External.get_servers()) do
|
||||
Gui.centered_label(factorio_servers, 110, server.short_name)
|
||||
Gui.centered_label(factorio_servers, 436, server.description)
|
||||
join_server(factorio_servers, server_id, current_version ~= server.version and server.version)
|
||||
end
|
||||
else
|
||||
local factorio_servers = title_table(scroll_pane, 225, {'readme.servers-factorio'}, 2)
|
||||
for i = 1, 8 do
|
||||
Gui.centered_label(factorio_servers, 110, {'readme.servers-'..i})
|
||||
Gui.centered_label(factorio_servers, 460, {'readme.servers-d'..i})
|
||||
end
|
||||
end
|
||||
|
||||
-- Add the external links
|
||||
local external_links = title_table(scroll_pane, 235, {'readme.servers-external'}, 2)
|
||||
for _, key in ipairs{'discord', 'website', 'patreon', 'status', 'github'} do
|
||||
local upper_key = key:gsub("^%l", string.upper)
|
||||
Gui.centered_label(external_links, 110, upper_key)
|
||||
Gui.centered_label(external_links, 460, {'links.'..key}, {'readme.servers-open-in-browser'})
|
||||
end
|
||||
|
||||
return container
|
||||
end))
|
||||
|
||||
--- Content area for the servers tab
|
||||
-- @element backers_content
|
||||
Tab({'readme.backers-tab'}, {'readme.backers-tooltip'},
|
||||
Gui.element(function(_, parent)
|
||||
local container = parent.add{ type='flow', direction='vertical' }
|
||||
|
||||
-- Add the title and description to the content
|
||||
Gui.title_label(container, title_width-10, {'readme.backers-tab'})
|
||||
Gui.centered_label(container, frame_width, {'readme.backers-general'})
|
||||
Gui.bar(container)
|
||||
container.add{ type='flow' }
|
||||
|
||||
-- Find which players will go where
|
||||
local done = {}
|
||||
local groups = {
|
||||
{ _roles={'Senior Administrator', 'Administrator'}, _title={'readme.backers-management'}, _width=230 },
|
||||
{ _roles={'Board Member', 'Senior Backer'}, _title={'readme.backers-board'}, _width=145 }, -- change role to board
|
||||
{ _roles={'Sponsor', 'Supporter'}, _title={'readme.backers-backers'}, _width=196 }, -- change to backer
|
||||
{ _roles={'Moderator', 'Trainee'}, _title={'readme.backers-staff'}, _width=235 },
|
||||
{ _roles={}, _time=3*3600*60, _title={'readme.backers-active'}, _width=235 },
|
||||
}
|
||||
|
||||
-- Fill by player roles
|
||||
for player_name, player_roles in pairs(Roles.config.players) do
|
||||
for _, players in ipairs(groups) do
|
||||
for _, role_name in pairs(players._roles) do
|
||||
if table.contains(player_roles, role_name) then
|
||||
done[player_name] = true
|
||||
table.insert(players, player_name)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Fill by active times
|
||||
for _, player in pairs(game.players) do
|
||||
if not done[player.name] then
|
||||
for _, players in ipairs(groups) do
|
||||
if players._time and player.online_time > players._time then
|
||||
table.insert(players, player.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Add the different tables
|
||||
local scroll_pane = title_table_scroll(container)
|
||||
for _, players in ipairs(groups) do
|
||||
local table = title_table(scroll_pane, players._width, players._title, 4)
|
||||
for _, player_name in ipairs(players) do
|
||||
Gui.centered_label(table, 140, player_name)
|
||||
end
|
||||
|
||||
if #players < 4 then
|
||||
for i = 1, 4-#players do
|
||||
Gui.centered_label(table, 140)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return container
|
||||
end))
|
||||
|
||||
--- Content area for the player data tab
|
||||
-- @element commands_content
|
||||
Tab({'readme.data-tab'}, {'readme.data-tooltip'},
|
||||
Gui.element(function(_, parent)
|
||||
local container = parent.add{ type='flow', direction='vertical' }
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
local player_name = player.name
|
||||
|
||||
local enum = PlayerData.PreferenceEnum
|
||||
local preference = PlayerData.DataSavingPreference:get(player_name)
|
||||
local preference_meta = PlayerData.DataSavingPreference.metadata
|
||||
preference = enum[preference]
|
||||
|
||||
-- Add the title and description to the content
|
||||
Gui.title_label(container, title_width, {'readme.data-tab'})
|
||||
Gui.centered_label(container, frame_width, {'readme.data-general'})
|
||||
Gui.bar(container)
|
||||
container.add{ type='flow' }
|
||||
local scroll_pane = title_table_scroll(container)
|
||||
|
||||
-- Add the required area
|
||||
local required = title_table(scroll_pane, 250, {'readme.data-required'}, 2)
|
||||
Gui.centered_label(required, 150, preference_meta.name, preference_meta.tooltip)
|
||||
Gui.centered_label(required, 420, {'expcore-data.preference-'..enum[preference]}, preference_meta.value_tooltip)
|
||||
|
||||
for name, child in pairs(PlayerData.Required.children) do
|
||||
local metadata = child.metadata
|
||||
local value = child:get(player_name)
|
||||
if value ~= nil or metadata.show_always then
|
||||
if metadata.stringify then value = metadata.stringify(value) end
|
||||
Gui.centered_label(required, 150, metadata.name or {'exp-required.'..name}, metadata.tooltip or {'exp-required.'..name..'-tooltip'})
|
||||
Gui.centered_label(required, 420, tostring(value), metadata.value_tooltip or {'exp-required.'..name..'-value-tooltip'})
|
||||
end
|
||||
end
|
||||
|
||||
-- Add the settings area
|
||||
if preference <= enum.Settings then
|
||||
local settings = title_table(scroll_pane, 255, {'readme.data-settings'}, 2)
|
||||
for name, child in pairs(PlayerData.Settings.children) do
|
||||
local metadata = child.metadata
|
||||
local value = child:get(player_name)
|
||||
if not metadata.permission or Roles.player_allowed(player, metadata.permission) then
|
||||
if metadata.stringify then value = metadata.stringify(value) end
|
||||
if value == nil then value = 'None set' end
|
||||
Gui.centered_label(settings, 150, metadata.name or {'exp-settings.'..name}, metadata.tooltip or {'exp-settings.'..name..'-tooltip'})
|
||||
Gui.centered_label(settings, 420, tostring(value), metadata.value_tooltip or {'exp-settings.'..name..'-value-tooltip'})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Add the statistics area
|
||||
if preference <= enum.Statistics then
|
||||
local count = 4
|
||||
local statistics = title_table(scroll_pane, 250, {'readme.data-statistics'}, 4)
|
||||
for _, name in pairs(PlayerData.Statistics.metadata.display_order) do
|
||||
local child = PlayerData.Statistics[name]
|
||||
local metadata = child.metadata
|
||||
local value = child:get(player_name)
|
||||
if value ~= nil or metadata.show_always then
|
||||
count = count - 2
|
||||
if metadata.stringify then value = metadata.stringify(value)
|
||||
else value = format_number(value or 0) end
|
||||
Gui.centered_label(statistics, 150, metadata.name or {'exp-statistics.'..name}, metadata.tooltip or {'exp-statistics.'..name..'-tooltip'})
|
||||
Gui.centered_label(statistics, 130, {'readme.data-format', value, metadata.unit or ''}, metadata.value_tooltip or {'exp-statistics.'..name..'-tooltip'})
|
||||
end
|
||||
end
|
||||
if count > 0 then for i = 1, count do Gui.centered_label(statistics, 140) end end
|
||||
end
|
||||
|
||||
-- Add the misc area
|
||||
local skip = {DataSavingPreference=true, Settings=true, Statistics=true, Required=true}
|
||||
local count = 0; for _ in pairs(PlayerData.All.children) do count = count + 1 end
|
||||
if preference <= enum.All and count > 4 then
|
||||
local misc = title_table(scroll_pane, 232, {'readme.data-misc'}, 2)
|
||||
for name, child in pairs(PlayerData.All.children) do
|
||||
if not skip[name] then
|
||||
local metadata = child.metadata
|
||||
local value = child:get(player_name)
|
||||
if value ~= nil or metadata.show_always then
|
||||
if metadata.stringify then value = metadata.stringify(value) end
|
||||
Gui.centered_label(misc, 150, metadata.name or name, metadata.tooltip)
|
||||
Gui.centered_label(misc, 420, tostring(value), metadata.value_tooltip)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return container
|
||||
end))
|
||||
|
||||
|
||||
--- Main readme container for the center flow
|
||||
-- @element readme
|
||||
local readme_toggle
|
||||
local readme =
|
||||
Gui.element(function(definition, parent)
|
||||
local container = parent.add{
|
||||
name = definition.name,
|
||||
type = 'frame',
|
||||
style = 'invisible_frame'
|
||||
}
|
||||
|
||||
-- Add the left hand side of the frame back, removed because of frame_tabbed_pane style
|
||||
local left_alignment = Gui.alignment(container, nil, nil, 'bottom')
|
||||
left_alignment.style.padding = {32, 0,0, 0}
|
||||
|
||||
local left_side =
|
||||
left_alignment.add{
|
||||
type = 'frame',
|
||||
style = 'frame_without_right_side'
|
||||
}
|
||||
left_side.style.vertically_stretchable = true
|
||||
left_side.style.padding = 0
|
||||
left_side.style.width = 5
|
||||
|
||||
-- Add the tab pane
|
||||
local tab_pane = container.add{
|
||||
name = 'pane',
|
||||
type = 'tabbed-pane',
|
||||
style = 'frame_tabbed_pane'
|
||||
}
|
||||
|
||||
-- Add the different content areas
|
||||
for _, tab_details in ipairs(tabs) do
|
||||
local tab = tab_pane.add{ type = 'tab', style = 'frame_tab', caption = tab_details[1], tooltip = tab_details[2] }
|
||||
tab_pane.add_tab(tab, tab_details[3](tab_pane))
|
||||
end
|
||||
|
||||
return container
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:on_open(function(player)
|
||||
Gui.toggle_toolbar_button(player, readme_toggle, true)
|
||||
end)
|
||||
:on_close(function(player, element)
|
||||
Gui.toggle_toolbar_button(player, readme_toggle, false)
|
||||
Gui.destroy_if_valid(element)
|
||||
end)
|
||||
|
||||
--- Toggle button for the readme gui
|
||||
-- @element readme_toggle
|
||||
readme_toggle =
|
||||
Gui.toolbar_button('virtual-signal/signal-info', {'readme.main-tooltip'}, function(player)
|
||||
return Roles.player_allowed(player, 'gui/readme')
|
||||
end)
|
||||
:on_click(function(player, _)
|
||||
local center = player.gui.center
|
||||
if center[readme.name] then
|
||||
player.opened = nil
|
||||
else
|
||||
player.opened = readme(center)
|
||||
end
|
||||
end)
|
||||
|
||||
--- When a player joins the game for the first time show this gui
|
||||
Event.add(defines.events.on_player_created, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
local element = readme(player.gui.center)
|
||||
element.pane.selected_tab_index = 1
|
||||
player.opened = element
|
||||
end)
|
||||
|
||||
--- When a player joins clear center unless the player has something open
|
||||
Event.add(defines.events.on_player_joined_game, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
if not player.opened then
|
||||
player.gui.center.clear()
|
||||
end
|
||||
end)
|
||||
|
||||
--- When a player respawns clear center unless the player has something open
|
||||
Event.add(defines.events.on_player_respawned, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
if not player.opened then
|
||||
player.gui.center.clear()
|
||||
end
|
||||
end)
|
||||
328
exp_legacy/module/modules/gui/research.lua
Normal file
328
exp_legacy/module/modules/gui/research.lua
Normal file
@@ -0,0 +1,328 @@
|
||||
--- research gui
|
||||
-- @gui Research
|
||||
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local config = require 'config.research' --- @dep config.research
|
||||
local format_time = _C.format_time --- @dep expcore.common
|
||||
|
||||
local research = {}
|
||||
Global.register(research, function(tbl)
|
||||
research = tbl
|
||||
end)
|
||||
|
||||
research.time = {}
|
||||
research.res_queue_enable = false
|
||||
|
||||
local research_time_format = {
|
||||
hours=true,
|
||||
minutes=true,
|
||||
seconds=true,
|
||||
time=true,
|
||||
string=true
|
||||
}
|
||||
|
||||
local empty_time = format_time(0, {
|
||||
hours=true,
|
||||
minutes=true,
|
||||
seconds=true,
|
||||
time=true,
|
||||
string=true,
|
||||
null=true
|
||||
})
|
||||
|
||||
local font_color = {
|
||||
-- positive
|
||||
[1] = {r = 0.3, g = 1, b = 0.3},
|
||||
-- negative
|
||||
[2] = {r = 1, g = 0.3, b = 0.3}
|
||||
}
|
||||
|
||||
local res = {
|
||||
['lookup_name'] = {},
|
||||
['disp'] = {}
|
||||
}
|
||||
|
||||
do
|
||||
local res_total = 0
|
||||
local i = 1
|
||||
|
||||
for k, v in pairs(config.milestone) do
|
||||
research.time[i] = 0
|
||||
res['lookup_name'][k] = i
|
||||
res_total = res_total + v * 60
|
||||
|
||||
res['disp'][i] = {
|
||||
raw_name = k,
|
||||
target = res_total,
|
||||
target_disp = format_time(res_total, research_time_format),
|
||||
}
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
local function research_add_log()
|
||||
local result_data = {}
|
||||
|
||||
for i=1, #research.time, 1 do
|
||||
result_data[res['disp'][i]['raw_name']] = research.time[i]
|
||||
end
|
||||
|
||||
game.write_file(config.file_name, game.table_to_json(result_data) .. '\n', true, 0)
|
||||
end
|
||||
|
||||
local function research_res_n(res_)
|
||||
local res_n = 1
|
||||
|
||||
for k, _ in pairs(res_) do
|
||||
if research.time[k] == 0 then
|
||||
res_n = k - 1
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if research.time[#res_] and research.time[#res_] > 0 then
|
||||
if res_n == 1 then
|
||||
res_n = #res_
|
||||
end
|
||||
end
|
||||
|
||||
if res_n < 3 then
|
||||
res_n = 3
|
||||
|
||||
elseif res_n > (#research.time - 5) then
|
||||
res_n = #research.time - 5
|
||||
end
|
||||
|
||||
return res_n
|
||||
end
|
||||
|
||||
local function research_notification(event)
|
||||
if config.inf_res[event.research.name] then
|
||||
if event.research.name == 'mining-productivity-4' then
|
||||
if event.research.level == 5 then
|
||||
-- Add run result to log
|
||||
research_add_log()
|
||||
end
|
||||
|
||||
if config.bonus_inventory.enabled then
|
||||
if (event.research.level - 1) <= math.ceil(config.bonus_inventory.limit / config.bonus_inventory.rate) then
|
||||
event.research.force[config.bonus_inventory.name] = math.max((event.research.level - 1) * config.bonus_inventory.rate, config.bonus_inventory.limit)
|
||||
end
|
||||
end
|
||||
|
||||
if config.pollution_ageing_by_research then
|
||||
game.map_settings.pollution.ageing = math.min(10, event.research.level / 5)
|
||||
end
|
||||
|
||||
else
|
||||
if not (event.by_script) then
|
||||
game.print{'expcom-res.inf', format_time(game.tick, research_time_format), event.research.name, event.research.level - 1}
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
if not (event.by_script) then
|
||||
game.print{'expcom-res.msg', format_time(game.tick, research_time_format), event.research.name}
|
||||
end
|
||||
|
||||
if config.bonus_inventory.enabled then
|
||||
if event.research.name == 'mining-productivity-1' or event.research.name == 'mining-productivity-2' or event.research.name == 'mining-productivity-3' then
|
||||
event.research.force[config.bonus_inventory.name] = event.research.level * config.bonus_inventory.rate
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function research_gui_update()
|
||||
local res_disp = {}
|
||||
local res_n = research_res_n(res['disp'])
|
||||
|
||||
for i=1, 8, 1 do
|
||||
res_disp[i] = {
|
||||
['name'] = '',
|
||||
['target'] = '',
|
||||
['attempt'] = '',
|
||||
['difference'] = '',
|
||||
['difference_color'] = font_color[1]
|
||||
}
|
||||
|
||||
local res_i = res_n + i - 3
|
||||
|
||||
if res['disp'][res_i] then
|
||||
res_disp[i]['name'] = {'expcom-res.res-name', res['disp'][res_i]['raw_name'], game.technology_prototypes[res['disp'][res_i]['raw_name']].localised_name}
|
||||
|
||||
if research.time[res_i] == 0 then
|
||||
res_disp[i]['target'] = res['disp'][res_i].target_disp
|
||||
res_disp[i]['attempt'] = empty_time
|
||||
res_disp[i]['difference'] = empty_time
|
||||
res_disp[i]['difference_color'] = font_color[1]
|
||||
|
||||
else
|
||||
res_disp[i]['target'] = res['disp'][res_i].target_disp
|
||||
res_disp[i]['attempt'] = format_time(research.time[res_i], research_time_format)
|
||||
|
||||
if research.time[res_i] < res['disp'][res_i].target then
|
||||
res_disp[i]['difference'] = '-' .. format_time(res['disp'][res_i].target - research.time[res_i], research_time_format)
|
||||
res_disp[i]['difference_color'] = font_color[1]
|
||||
|
||||
else
|
||||
res_disp[i]['difference'] = format_time(research.time[res_i] - res['disp'][res_i].target, research_time_format)
|
||||
res_disp[i]['difference_color'] = font_color[2]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return res_disp
|
||||
end
|
||||
|
||||
--- Display label for the clock display
|
||||
-- @element research_gui_clock_display
|
||||
local research_gui_clock =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
name = Gui.unique_static_name,
|
||||
caption = empty_time,
|
||||
style = 'heading_2_label'
|
||||
}
|
||||
|
||||
--- A vertical flow containing the clock
|
||||
-- @element research_clock_set
|
||||
local research_clock_set =
|
||||
Gui.element(function(_, parent, name)
|
||||
local research_set = parent.add{type='flow', direction='vertical', name=name}
|
||||
local disp = Gui.scroll_table(research_set, 390, 1, 'disp')
|
||||
|
||||
research_gui_clock(disp)
|
||||
|
||||
return research_set
|
||||
end)
|
||||
|
||||
--- Display group
|
||||
-- @element research_data_group
|
||||
local research_data_group =
|
||||
Gui.element(function(_definition, parent, i)
|
||||
local name = parent.add{
|
||||
type = 'label',
|
||||
name = 'research_' .. i .. '_name',
|
||||
caption = '',
|
||||
style = 'heading_2_label'
|
||||
}
|
||||
name.style.width = 180
|
||||
name.style.horizontal_align = 'left'
|
||||
|
||||
local target = parent.add{
|
||||
type = 'label',
|
||||
name = 'research_' .. i .. '_target',
|
||||
caption = '',
|
||||
style = 'heading_2_label'
|
||||
}
|
||||
target.style.width = 70
|
||||
target.style.horizontal_align = 'right'
|
||||
|
||||
local attempt = parent.add{
|
||||
type = 'label',
|
||||
name = 'research_' .. i .. '_attempt',
|
||||
caption = '',
|
||||
style = 'heading_2_label'
|
||||
}
|
||||
attempt.style.width = 70
|
||||
attempt.style.horizontal_align = 'right'
|
||||
|
||||
local difference = parent.add{
|
||||
type = 'label',
|
||||
name = 'research_' .. i .. '_difference',
|
||||
caption = '',
|
||||
style = 'heading_2_label'
|
||||
}
|
||||
difference.style.width = 70
|
||||
difference.style.horizontal_align = 'right'
|
||||
difference.style.font_color = font_color[1]
|
||||
end)
|
||||
|
||||
--- A vertical flow containing the data
|
||||
-- @element research_data_set
|
||||
local research_data_set =
|
||||
Gui.element(function(_, parent, name)
|
||||
local research_set = parent.add{type='flow', direction='vertical', name=name}
|
||||
local disp = Gui.scroll_table(research_set, 390, 4, 'disp')
|
||||
local res_disp = research_gui_update()
|
||||
|
||||
research_data_group(disp, 0)
|
||||
disp['research_0_name'].caption = {'expcom-res.name'}
|
||||
disp['research_0_target'].caption = {'expcom-res.target'}
|
||||
disp['research_0_attempt'].caption = {'expcom-res.attempt'}
|
||||
disp['research_0_difference'].caption = {'expcom-res.difference'}
|
||||
|
||||
for i=1, 8, 1 do
|
||||
research_data_group(disp, i)
|
||||
|
||||
local research_name_i = 'research_' .. i
|
||||
|
||||
disp[research_name_i .. '_name'].caption = res_disp[i]['name']
|
||||
disp[research_name_i .. '_target'].caption = res_disp[i]['target']
|
||||
disp[research_name_i .. '_attempt'].caption = res_disp[i]['attempt']
|
||||
disp[research_name_i .. '_difference'].caption = res_disp[i]['difference']
|
||||
disp[research_name_i .. '_difference'].style.font_color = res_disp[i]['difference_color']
|
||||
end
|
||||
|
||||
return research_set
|
||||
end)
|
||||
|
||||
local research_container =
|
||||
Gui.element(function(definition, parent)
|
||||
local container = Gui.container(parent, definition.name, 390)
|
||||
|
||||
research_clock_set(container, 'research_st_1')
|
||||
research_data_set(container, 'research_st_2')
|
||||
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow()
|
||||
|
||||
Gui.left_toolbar_button('item/space-science-pack', {'expcom-res.main-tooltip'}, research_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/research')
|
||||
end)
|
||||
|
||||
Event.add(defines.events.on_research_finished, function(event)
|
||||
research_notification(event)
|
||||
|
||||
if res['lookup_name'][event.research.name] == nil then
|
||||
return
|
||||
end
|
||||
|
||||
local n_i = res['lookup_name'][event.research.name]
|
||||
research.time[n_i] = game.tick
|
||||
|
||||
local res_disp = research_gui_update()
|
||||
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local frame = Gui.get_left_element(player, research_container)
|
||||
local disp = frame.container['research_st_2'].disp.table
|
||||
|
||||
for i=1, 8, 1 do
|
||||
local research_name_i = 'research_' .. i
|
||||
|
||||
disp[research_name_i .. '_name'].caption = res_disp[i]['name']
|
||||
disp[research_name_i .. '_target'].caption = res_disp[i]['target']
|
||||
disp[research_name_i .. '_attempt'].caption = res_disp[i]['attempt']
|
||||
disp[research_name_i .. '_difference'].caption = res_disp[i]['difference']
|
||||
disp[research_name_i .. '_difference'].style.font_color = res_disp[i]['difference_color']
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Event.on_nth_tick(60, function()
|
||||
local current_time = format_time(game.tick, research_time_format)
|
||||
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local frame = Gui.get_left_element(player, research_container)
|
||||
local disp = frame.container['research_st_1'].disp.table
|
||||
disp[research_gui_clock.name].caption = current_time
|
||||
end
|
||||
end)
|
||||
610
exp_legacy/module/modules/gui/rocket-info.lua
Normal file
610
exp_legacy/module/modules/gui/rocket-info.lua
Normal file
@@ -0,0 +1,610 @@
|
||||
--[[-- Gui Module - Rocket Info
|
||||
- Adds a rocket infomation gui which shows general stats, milestones and build progress of rockets
|
||||
@gui Rocket-Info
|
||||
@alias rocket_info
|
||||
]]
|
||||
|
||||
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 config = require 'config.gui.rockets' --- @dep config.gui.rockets
|
||||
local Colors = require 'utils.color_presets' --- @dep utils.color_presets
|
||||
local Rockets = require 'modules.control.rockets' --- @dep modules.control.rockets
|
||||
local format_time = _C.format_time --- @dep expcore.common
|
||||
|
||||
local time_formats = {
|
||||
caption = function(value) return format_time(value, {minutes=true, seconds=true}) end,
|
||||
caption_hours = function(value) return format_time(value) end,
|
||||
tooltip = function(value) return format_time(value, {minutes=true, seconds=true, long=true}) end,
|
||||
tooltip_hours = function(value) return format_time(value, {hours=true, minutes=true, seconds=true, long=true}) end
|
||||
}
|
||||
|
||||
--- Check if a player is allowed to use certain interactions
|
||||
local function check_player_permissions(player, action)
|
||||
if not config.progress['allow_'..action] then
|
||||
return false
|
||||
end
|
||||
|
||||
if config.progress[action..'_admins_only'] and not player.admin then
|
||||
return false
|
||||
end
|
||||
|
||||
if config.progress[action..'_role_permission']
|
||||
and not Roles.player_allowed(player, config.progress[action..'_role_permission']) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- Button to toggle the auto launch on a rocket silo
|
||||
-- @element toggle_launch
|
||||
local toggle_launch =
|
||||
Gui.element{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/play',
|
||||
tooltip = {'rocket-info.toggle-rocket-tooltip'},
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style(Gui.sprite_style(16))
|
||||
:on_click(function(_, element, _)
|
||||
local rocket_silo_name = element.parent.name:sub(8)
|
||||
local rocket_silo = Rockets.get_silo_entity(rocket_silo_name)
|
||||
if rocket_silo.auto_launch then
|
||||
element.sprite = 'utility/play'
|
||||
element.tooltip = {'rocket-info.toggle-rocket-tooltip'}
|
||||
rocket_silo.auto_launch = false
|
||||
else
|
||||
element.sprite = 'utility/stop'
|
||||
element.tooltip = {'rocket-info.toggle-rocket-tooltip-disabled'}
|
||||
rocket_silo.auto_launch = true
|
||||
end
|
||||
end)
|
||||
|
||||
--- Button to remotely launch a rocket from a silo
|
||||
-- @element launch_rocket
|
||||
local launch_rocket =
|
||||
Gui.element{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/center',
|
||||
tooltip = {'rocket-info.launch-tooltip'},
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style(Gui.sprite_style(16, -1))
|
||||
:on_click(function(player, element, _)
|
||||
local rocket_silo_name = element.parent.name:sub(8)
|
||||
local silo_data = Rockets.get_silo_data_by_name(rocket_silo_name)
|
||||
if silo_data.entity.launch_rocket() then
|
||||
element.enabled = false
|
||||
else
|
||||
player.print({'rocket-info.launch-failed'}, Colors.orange_red)
|
||||
end
|
||||
end)
|
||||
|
||||
--- XY cords that allow zoom to map when pressed
|
||||
-- @element silo_cords
|
||||
local silo_cords =
|
||||
Gui.element(function(definition, parent, silo_data)
|
||||
local silo_name = silo_data.silo_name
|
||||
local pos = silo_data.position
|
||||
local tooltip = config.progress.allow_zoom_to_map and {'rocket-info.progress-label-tooltip'} or nil
|
||||
|
||||
-- Add the x cord flow
|
||||
local flow_x = parent.add{
|
||||
type ='flow',
|
||||
name = 'label-x-'..silo_name,
|
||||
caption = silo_name
|
||||
}
|
||||
flow_x.style.padding = {0, 2,0, 1}
|
||||
|
||||
-- Add the x cord label
|
||||
local label_x = flow_x.add{
|
||||
type = 'label',
|
||||
caption = {'rocket-info.progress-x-pos', pos.x},
|
||||
tooltip = tooltip
|
||||
}
|
||||
|
||||
-- Add the y cord flow
|
||||
local flow_y = parent.add{
|
||||
type ='flow',
|
||||
name = 'label-y-'..silo_name,
|
||||
caption = silo_name
|
||||
}
|
||||
flow_y.style.padding = {0, 2,0, 1}
|
||||
|
||||
-- Add the y cord label
|
||||
local label_y = flow_y.add{
|
||||
type = 'label',
|
||||
caption = {'rocket-info.progress-y-pos', pos.y},
|
||||
tooltip = tooltip
|
||||
}
|
||||
|
||||
if config.progress.allow_zoom_to_map then
|
||||
definition:triggers_events(label_x)
|
||||
definition:triggers_events(label_y)
|
||||
end
|
||||
end)
|
||||
:on_click(function(player, element, _)
|
||||
local rocket_silo_name = element.parent.caption
|
||||
local rocket_silo = Rockets.get_silo_entity(rocket_silo_name)
|
||||
player.zoom_to_world(rocket_silo.position, 2)
|
||||
end)
|
||||
|
||||
--- Base element for each rocket in the progress list
|
||||
-- @element rocket_entry
|
||||
local rocket_entry =
|
||||
Gui.element(function(_, parent, silo_data)
|
||||
local silo_name = silo_data.silo_name
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
|
||||
-- Add the toggle auto launch if the player is allowed it
|
||||
if check_player_permissions(player, 'toggle_active') then
|
||||
local flow = parent.add{ type = 'flow', name = 'toggle-'..silo_name}
|
||||
local button = toggle_launch(flow)
|
||||
button.tooltip = silo_data.toggle_tooltip
|
||||
button.sprite = silo_data.toggle_sprite
|
||||
end
|
||||
|
||||
-- Add the remote launch if the player is allowed it
|
||||
if check_player_permissions(player, 'remote_launch') then
|
||||
local flow = parent.add{ type = 'flow', name = 'launch-'..silo_name}
|
||||
local button = launch_rocket(flow)
|
||||
button.enabled = silo_data.allow_launch
|
||||
end
|
||||
|
||||
-- Draw the silo cords element
|
||||
silo_cords(parent, silo_data)
|
||||
|
||||
-- Add a progress label
|
||||
local alignment = Gui.alignment(parent, silo_name)
|
||||
local element =
|
||||
alignment.add{
|
||||
type = 'label',
|
||||
name = 'label',
|
||||
caption = silo_data.progress_caption,
|
||||
tooltip = silo_data.progress_tooltip
|
||||
}
|
||||
|
||||
-- Return the progress label
|
||||
return element
|
||||
end)
|
||||
|
||||
--- Data label which contains a name and a value label pair
|
||||
-- @element data_label
|
||||
local data_label =
|
||||
Gui.element(function(_, parent, label_data)
|
||||
local data_name = label_data.name
|
||||
local data_subname = label_data.subname
|
||||
local data_fullname = data_subname and data_name..data_subname or data_name
|
||||
|
||||
-- Add the name label
|
||||
local name_label = parent.add{
|
||||
type = 'label',
|
||||
name = data_fullname..'-label',
|
||||
caption = {'rocket-info.data-caption-'..data_name, data_subname},
|
||||
tooltip = {'rocket-info.data-tooltip-'..data_name, data_subname}
|
||||
}
|
||||
name_label.style.padding = {0, 2}
|
||||
|
||||
--- Right aligned label to store the data
|
||||
local alignment = Gui.alignment(parent, data_fullname)
|
||||
local element =
|
||||
alignment.add{
|
||||
type = 'label',
|
||||
name = 'label',
|
||||
caption = label_data.value,
|
||||
tooltip = label_data.tooltip
|
||||
}
|
||||
element.style.padding = {0, 2}
|
||||
|
||||
return element
|
||||
end)
|
||||
|
||||
-- Used to update the captions and tooltips on the data labels
|
||||
local function update_data_labels(parent, data_label_data)
|
||||
for _, label_data in ipairs(data_label_data) do
|
||||
local data_name = label_data.subname and label_data.name..label_data.subname or label_data.name
|
||||
if not parent[data_name] then
|
||||
data_label(parent, label_data)
|
||||
else
|
||||
local data_label_element = parent[data_name].label
|
||||
data_label_element.tooltip = label_data.tooltip
|
||||
data_label_element.caption = label_data.value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function get_progress_data(force_name)
|
||||
local force_silos = Rockets.get_silos(force_name)
|
||||
local progress_data = {}
|
||||
|
||||
for _, silo_data in pairs(force_silos) do
|
||||
local rocket_silo = silo_data.entity
|
||||
if not rocket_silo or not rocket_silo.valid then
|
||||
-- Remove from list if not valid
|
||||
force_silos[silo_data.name] = nil
|
||||
table.insert(progress_data, {
|
||||
silo_name = silo_data.name,
|
||||
remove = true
|
||||
})
|
||||
|
||||
else
|
||||
-- Get the progress caption and tooltip
|
||||
local progress_color = Colors.white
|
||||
local progress_caption = {'rocket-info.progress-caption', rocket_silo.rocket_parts}
|
||||
local progress_tooltip = {'rocket-info.progress-tooltip', silo_data.launched or 0}
|
||||
local status = rocket_silo.status == defines.entity_status.waiting_to_launch_rocket
|
||||
if status and silo_data.awaiting_reset then
|
||||
progress_caption = {'rocket-info.progress-launched'}
|
||||
progress_color = Colors.green
|
||||
elseif status then
|
||||
progress_caption = {'rocket-info.progress-caption', 100}
|
||||
progress_color = Colors.cyan
|
||||
else
|
||||
silo_data.awaiting_reset = false
|
||||
end
|
||||
|
||||
-- Get the toggle button data
|
||||
local toggle_tooltip = {'rocket-info.toggle-rocket-tooltip-disabled'}
|
||||
local toggle_sprite = 'utility/play'
|
||||
if rocket_silo.auto_launch then
|
||||
toggle_tooltip = {'rocket-info.toggle-rocket-tooltip'}
|
||||
toggle_sprite = 'utility/stop'
|
||||
end
|
||||
|
||||
-- Insert the gui data
|
||||
table.insert(progress_data, {
|
||||
silo_name = silo_data.name,
|
||||
position = rocket_silo.position,
|
||||
allow_launch = not silo_data.awaiting_reset and status or false,
|
||||
progress_color = progress_color,
|
||||
progress_caption = progress_caption,
|
||||
progress_tooltip = progress_tooltip,
|
||||
toggle_tooltip = toggle_tooltip,
|
||||
toggle_sprite = toggle_sprite
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
return progress_data
|
||||
end
|
||||
|
||||
--- Update the build progress section
|
||||
local function update_build_progress(parent, progress_data)
|
||||
local show_message = true
|
||||
for _, silo_data in ipairs(progress_data) do
|
||||
parent.parent.no_silos.visible = false
|
||||
parent.visible = true
|
||||
local silo_name = silo_data.silo_name
|
||||
local progress_label = parent[silo_name]
|
||||
if silo_data.remove then
|
||||
-- Remove the rocket from the list
|
||||
Gui.destroy_if_valid(parent['toggle-'..silo_name])
|
||||
Gui.destroy_if_valid(parent['launch-'..silo_name])
|
||||
Gui.destroy_if_valid(parent['label-x-'..silo_name])
|
||||
Gui.destroy_if_valid(parent['label-y-'..silo_name])
|
||||
Gui.destroy_if_valid(parent[silo_name])
|
||||
|
||||
elseif not progress_label then
|
||||
-- Add the rocket to the list
|
||||
show_message = false
|
||||
rocket_entry(parent, silo_data)
|
||||
|
||||
else
|
||||
show_message = false
|
||||
-- Update the existing labels
|
||||
progress_label = progress_label.label
|
||||
progress_label.caption = silo_data.progress_caption
|
||||
progress_label.tooltip = silo_data.progress_tooltip
|
||||
progress_label.style.font_color = silo_data.progress_color
|
||||
|
||||
-- Update the toggle button
|
||||
local toggle_button = parent['toggle-'..silo_name]
|
||||
if toggle_button then
|
||||
toggle_button = toggle_button[toggle_launch.name]
|
||||
toggle_button.tooltip = silo_data.toggle_tooltip
|
||||
toggle_button.sprite = silo_data.toggle_sprite
|
||||
end
|
||||
|
||||
-- Update the launch button
|
||||
local launch_button = parent['launch-'..silo_name]
|
||||
if launch_button then
|
||||
launch_button = launch_button[launch_rocket.name]
|
||||
launch_button.enabled = silo_data.allow_launch
|
||||
end
|
||||
end
|
||||
end
|
||||
if show_message then parent.parent.no_silos.visible = true parent.visible = false end
|
||||
end
|
||||
|
||||
--- Gets the label data for all the different stats
|
||||
local function get_stats_data(force_name)
|
||||
local force_rockets = Rockets.get_rocket_count(force_name)
|
||||
local stats = Rockets.get_stats(force_name)
|
||||
local stats_data = {}
|
||||
|
||||
-- Format the first launch data
|
||||
if config.stats.show_first_rocket then
|
||||
local value = stats.first_launch or 0
|
||||
table.insert(stats_data, {
|
||||
name = 'first-launch',
|
||||
value = time_formats.caption_hours(value),
|
||||
tooltip = time_formats.tooltip_hours(value)
|
||||
})
|
||||
end
|
||||
|
||||
-- Format the last launch data
|
||||
if config.stats.show_last_rocket then
|
||||
local value = stats.last_launch or 0
|
||||
table.insert(stats_data, {
|
||||
name = 'last-launch',
|
||||
value = time_formats.caption_hours(value),
|
||||
tooltip = time_formats.tooltip_hours(value)
|
||||
})
|
||||
end
|
||||
|
||||
-- Format fastest launch data
|
||||
if config.stats.show_fastest_rocket then
|
||||
local value = stats.fastest_launch or 0
|
||||
table.insert(stats_data, {
|
||||
name = 'fastest-launch',
|
||||
value = time_formats.caption_hours(value),
|
||||
tooltip = time_formats.tooltip_hours(value)
|
||||
})
|
||||
end
|
||||
|
||||
-- Format total rocket data
|
||||
if config.stats.show_total_rockets then
|
||||
local total_rockets = Rockets.get_game_rocket_count()
|
||||
total_rockets = total_rockets == 0 and 1 or total_rockets
|
||||
local percentage = math.round(force_rockets/total_rockets, 3)*100
|
||||
table.insert(stats_data, {
|
||||
name = 'total-rockets',
|
||||
value = force_rockets,
|
||||
tooltip = {'rocket-info.value-tooltip-total-rockets', percentage}
|
||||
})
|
||||
end
|
||||
|
||||
-- Format game avg data
|
||||
if config.stats.show_game_avg then
|
||||
local avg = force_rockets > 0 and math.floor(game.tick/force_rockets) or 0
|
||||
table.insert(stats_data, {
|
||||
name = 'avg-launch',
|
||||
value = time_formats.caption(avg),
|
||||
tooltip = time_formats.tooltip(avg)
|
||||
})
|
||||
end
|
||||
|
||||
-- Format rolling avg data
|
||||
for _, avg_over in pairs(config.stats.rolling_avg) do
|
||||
local avg = Rockets.get_rolling_average(force_name, avg_over)
|
||||
table.insert(stats_data, {
|
||||
name = 'avg-launch-n',
|
||||
subname = avg_over,
|
||||
value = time_formats.caption(avg),
|
||||
tooltip = time_formats.tooltip(avg)
|
||||
})
|
||||
end
|
||||
|
||||
-- Return formated data
|
||||
return stats_data
|
||||
end
|
||||
|
||||
--- Gets the label data for the milestones
|
||||
local function get_milestone_data(force_name)
|
||||
local force_rockets = Rockets.get_rocket_count(force_name)
|
||||
local milestone_data = {}
|
||||
|
||||
for _, milestone in ipairs(config.milestones) do
|
||||
if milestone <= force_rockets then
|
||||
local time = Rockets.get_rocket_time(force_name, milestone)
|
||||
table.insert(milestone_data, {
|
||||
name = 'milestone-n',
|
||||
subname = milestone,
|
||||
value = time_formats.caption_hours(time),
|
||||
tooltip = time_formats.tooltip_hours(time)
|
||||
})
|
||||
else
|
||||
table.insert(milestone_data, {
|
||||
name = 'milestone-n',
|
||||
subname = milestone,
|
||||
value = {'rocket-info.data-caption-milestone-next'},
|
||||
tooltip = {'rocket-info.data-tooltip-milestone-next'}
|
||||
})
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return milestone_data
|
||||
end
|
||||
|
||||
-- Button to toggle a section dropdown
|
||||
-- @element toggle_section
|
||||
local toggle_section =
|
||||
Gui.element{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/expand_dark',
|
||||
hovered_sprite = 'utility/expand',
|
||||
tooltip = {'rocket-info.toggle-section-tooltip'},
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style(Gui.sprite_style(20))
|
||||
:on_click(function(_, element, _)
|
||||
local header_flow = element.parent
|
||||
local flow_name = header_flow.caption
|
||||
local flow = header_flow.parent.parent[flow_name]
|
||||
if Gui.toggle_visible_state(flow) then
|
||||
element.sprite = 'utility/collapse_dark'
|
||||
element.hovered_sprite = 'utility/collapse'
|
||||
element.tooltip = {'rocket-info.toggle-section-collapse-tooltip'}
|
||||
else
|
||||
element.sprite = 'utility/expand_dark'
|
||||
element.hovered_sprite = 'utility/expand'
|
||||
element.tooltip = {'rocket-info.toggle-section-tooltip'}
|
||||
end
|
||||
end)
|
||||
|
||||
-- Draw a section header and main scroll
|
||||
-- @element rocket_list_container
|
||||
local section =
|
||||
Gui.element(function(definition, parent, section_name, table_size)
|
||||
-- Draw the header for the section
|
||||
local header = Gui.header(
|
||||
parent,
|
||||
{'rocket-info.section-caption-'..section_name},
|
||||
{'rocket-info.section-tooltip-'..section_name},
|
||||
true,
|
||||
section_name..'-header'
|
||||
)
|
||||
definition:triggers_events(header.parent.header_label)
|
||||
|
||||
-- Right aligned button to toggle the section
|
||||
header.caption = section_name
|
||||
toggle_section(header)
|
||||
|
||||
-- Table used to store the data
|
||||
local scroll_table = Gui.scroll_table(parent, 215, table_size, section_name)
|
||||
scroll_table.parent.visible = false
|
||||
|
||||
-- Return the flow table
|
||||
return definition:no_events(scroll_table)
|
||||
end)
|
||||
:on_click(function(_, element, event)
|
||||
event.element = element.parent.alignment[toggle_section.name]
|
||||
toggle_section:raise_event(event)
|
||||
end)
|
||||
|
||||
--- Main gui container for the left flow
|
||||
-- @element rocket_list_container
|
||||
local rocket_list_container =
|
||||
Gui.element(function(definition, parent)
|
||||
-- Draw the internal container
|
||||
local container = Gui.container(parent, definition.name, 200)
|
||||
|
||||
-- Set the container style
|
||||
local style = container.style
|
||||
style.padding = 0
|
||||
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
local force_name = player.force.name
|
||||
-- Draw stats section
|
||||
if config.stats.show_stats then
|
||||
update_data_labels(section(container, 'stats', 2), get_stats_data(force_name))
|
||||
end
|
||||
|
||||
-- Draw milestones section
|
||||
if config.milestones.show_milestones then
|
||||
update_data_labels(section(container, 'milestones', 2), get_milestone_data(force_name))
|
||||
end
|
||||
|
||||
-- Draw build progress list
|
||||
if config.progress.show_progress then
|
||||
local col_count = 3
|
||||
if check_player_permissions(player, 'remote_launch') then col_count = col_count+1 end
|
||||
if check_player_permissions(player, 'toggle_active') then col_count = col_count+1 end
|
||||
local progress = section(container, 'progress', col_count)
|
||||
-- Label used when there are no active silos
|
||||
local no_silos = progress.parent.add{
|
||||
type = 'label',
|
||||
name = 'no_silos',
|
||||
caption = {'rocket-info.progress-no-silos'}
|
||||
}
|
||||
no_silos.style.padding = {1, 2}
|
||||
update_build_progress(progress, get_progress_data(force_name))
|
||||
end
|
||||
|
||||
-- Return the exteral container
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow(function(player)
|
||||
return player.force.rockets_launched > 0 and Roles.player_allowed(player, 'gui/rocket-info')
|
||||
end)
|
||||
|
||||
--- Button on the top flow used to toggle the container
|
||||
-- @element toggle_rocket_info
|
||||
Gui.left_toolbar_button('item/satellite', {'rocket-info.main-tooltip'}, rocket_list_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/rocket-info')
|
||||
end)
|
||||
|
||||
--- Update the gui for all players on a force
|
||||
local function update_rocket_gui_all(force_name)
|
||||
local stats = get_stats_data(force_name)
|
||||
local milestones = get_milestone_data(force_name)
|
||||
local progress = get_progress_data(force_name)
|
||||
for _, player in pairs(game.forces[force_name].players) do
|
||||
local frame = Gui.get_left_element(player, rocket_list_container)
|
||||
local container = frame.container
|
||||
update_data_labels(container.stats.table, stats)
|
||||
update_data_labels(container.milestones.table, milestones)
|
||||
update_build_progress(container.progress.table, progress)
|
||||
end
|
||||
end
|
||||
|
||||
--- Event used to update the stats when a rocket is launched
|
||||
Event.add(defines.events.on_rocket_launched, function(event)
|
||||
local force = event.rocket_silo.force
|
||||
update_rocket_gui_all(force.name)
|
||||
end)
|
||||
|
||||
--- Update only the progress gui for a force
|
||||
local function update_rocket_gui_progress(force_name)
|
||||
local progress = get_progress_data(force_name)
|
||||
for _, player in pairs(game.forces[force_name].players) do
|
||||
local frame = Gui.get_left_element(player, rocket_list_container)
|
||||
local container = frame.container
|
||||
update_build_progress(container.progress.table, progress)
|
||||
end
|
||||
end
|
||||
|
||||
--- Event used to set a rocket silo to be awaiting reset
|
||||
Event.add(defines.events.on_rocket_launch_ordered, function(event)
|
||||
local silo = event.rocket_silo
|
||||
local silo_data = Rockets.get_silo_data(silo)
|
||||
silo_data.awaiting_reset = true
|
||||
update_rocket_gui_progress(silo.force.name)
|
||||
end)
|
||||
|
||||
Event.on_nth_tick(150, function()
|
||||
for _, force in pairs(game.forces) do
|
||||
if #Rockets.get_silos(force.name) > 0 then
|
||||
update_rocket_gui_progress(force.name)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--- Adds a silo to the list when it is built
|
||||
local function on_built(event)
|
||||
local entity = event.created_entity
|
||||
if entity.valid and entity.name == 'rocket-silo' then
|
||||
update_rocket_gui_progress(entity.force.name)
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_built_entity, on_built)
|
||||
Event.add(defines.events.on_robot_built_entity, on_built)
|
||||
|
||||
--- Redraw the progress section on role change
|
||||
local function role_update_event(event)
|
||||
if not config.progress.show_progress then return end
|
||||
local player = game.players[event.player_index]
|
||||
local container = Gui.get_left_element(player, rocket_list_container).container
|
||||
local progress_scroll = container.progress
|
||||
Gui.destroy_if_valid(progress_scroll.table)
|
||||
|
||||
local col_count = 3
|
||||
if check_player_permissions(player, 'remote_launch') then col_count = col_count+1 end
|
||||
if check_player_permissions(player, 'toggle_active') then col_count = col_count+1 end
|
||||
local progress = progress_scroll.add{
|
||||
type = 'table',
|
||||
name = 'table',
|
||||
column_count = col_count
|
||||
}
|
||||
|
||||
update_build_progress(progress, get_progress_data(player.force.name))
|
||||
end
|
||||
|
||||
Event.add(Roles.events.on_role_assigned, role_update_event)
|
||||
Event.add(Roles.events.on_role_unassigned, role_update_event)
|
||||
|
||||
return rocket_list_container
|
||||
374
exp_legacy/module/modules/gui/science-info.lua
Normal file
374
exp_legacy/module/modules/gui/science-info.lua
Normal file
@@ -0,0 +1,374 @@
|
||||
--[[-- Gui Module - Science Info
|
||||
- Adds a science info gui that shows production usage and net for the different science packs as well as an eta
|
||||
@gui Science-Info
|
||||
@alias science_info
|
||||
]]
|
||||
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.gui
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.gui.science' --- @dep config.gui.science
|
||||
local Production = require 'modules.control.production' --- @dep modules.control.production
|
||||
local format_time = _C.format_time --- @dep expcore.common
|
||||
|
||||
local null_time_short = {'science-info.eta-time', format_time(0, {hours=true, minutes=true, seconds=true, time=true, null=true})}
|
||||
local null_time_long = format_time(0, {hours=true, minutes=true, seconds=true, long=true, null=true})
|
||||
|
||||
--- Data label that contains the value and the surfix
|
||||
-- @element production_label
|
||||
local production_label =
|
||||
Gui.element(function(_, parent, production_label_data)
|
||||
local name = production_label_data.name
|
||||
local tooltip = production_label_data.tooltip
|
||||
local color = production_label_data.color
|
||||
|
||||
-- Add an alignment for the number
|
||||
local alignment = Gui.alignment(parent, name)
|
||||
|
||||
-- Add the main value label
|
||||
local element =
|
||||
alignment.add{
|
||||
name = 'label',
|
||||
type = 'label',
|
||||
caption = production_label_data.caption,
|
||||
tooltip = tooltip
|
||||
}
|
||||
|
||||
-- Change the style
|
||||
element.style.font_color = color
|
||||
|
||||
-- Add the surfix label
|
||||
local surfix_element =
|
||||
parent.add{
|
||||
name = 'surfix-'..name,
|
||||
type = 'label',
|
||||
caption = {'science-info.unit', production_label_data.surfix},
|
||||
tooltip = tooltip
|
||||
}
|
||||
|
||||
-- Change the style
|
||||
local surfix_element_style = surfix_element.style
|
||||
surfix_element_style.font_color = color
|
||||
surfix_element_style.right_margin = 1
|
||||
|
||||
-- Return the value label
|
||||
return element
|
||||
end)
|
||||
|
||||
-- Get the data that is used with the production label
|
||||
local function get_production_label_data(name, tooltip, value, cutout, secondary)
|
||||
local data_colour = Production.get_color(config.color_cutoff * cutout, value, secondary)
|
||||
local surfix, caption = Production.format_number(value)
|
||||
|
||||
return {
|
||||
name = name,
|
||||
caption = caption,
|
||||
surfix = surfix,
|
||||
tooltip = tooltip,
|
||||
color = data_colour
|
||||
}
|
||||
end
|
||||
|
||||
-- Updates a prodution label to match the current data
|
||||
local function update_production_label(parent, production_label_data)
|
||||
local name = production_label_data.name
|
||||
local tooltip = production_label_data.tooltip
|
||||
local color = production_label_data.color
|
||||
|
||||
-- Update the production label
|
||||
local production_label_element = parent[name] and parent[name].label or production_label(parent, production_label_data)
|
||||
production_label_element.caption = production_label_data.caption
|
||||
production_label_element.tooltip = production_label_data.tooltip
|
||||
production_label_element.style.font_color = color
|
||||
|
||||
-- Update the surfix label
|
||||
local surfix_element = parent['surfix-'..name]
|
||||
surfix_element.caption = {'science-info.unit', production_label_data.surfix}
|
||||
surfix_element.tooltip = tooltip
|
||||
surfix_element.style.font_color = color
|
||||
|
||||
end
|
||||
|
||||
--- Adds 4 elements that show the data for a science pack
|
||||
-- @element science_pack_base
|
||||
local science_pack_base =
|
||||
Gui.element(function(_, parent, science_pack_data)
|
||||
local science_pack = science_pack_data.science_pack
|
||||
|
||||
-- Draw the icon for the science pack
|
||||
local icon_style = science_pack_data.icon_style
|
||||
local pack_icon =
|
||||
parent.add{
|
||||
name = 'icon-'..science_pack,
|
||||
type = 'sprite-button',
|
||||
sprite = 'item/'..science_pack,
|
||||
tooltip = {'item-name.'..science_pack},
|
||||
style = icon_style
|
||||
}
|
||||
|
||||
-- Change the style of the icon
|
||||
local pack_icon_style = pack_icon.style
|
||||
pack_icon.ignored_by_interaction = true
|
||||
pack_icon_style.height = 55
|
||||
if icon_style == 'slot_button' then
|
||||
pack_icon_style.padding = {0, -2}
|
||||
pack_icon_style.width = 36
|
||||
end
|
||||
|
||||
-- Draw the delta flow
|
||||
local delta_flow =
|
||||
parent.add{
|
||||
name = 'delta-'..science_pack,
|
||||
type = 'frame',
|
||||
style = 'bordered_frame'
|
||||
}
|
||||
delta_flow.style.padding = {0, 3}
|
||||
|
||||
-- Draw the delta flow table
|
||||
local delta_table =
|
||||
delta_flow.add{
|
||||
name = 'table',
|
||||
type = 'table',
|
||||
column_count = 2
|
||||
}
|
||||
delta_table.style.padding = 0
|
||||
|
||||
-- Draw the production labels
|
||||
update_production_label(delta_table, science_pack_data.positive)
|
||||
update_production_label(delta_table, science_pack_data.negative)
|
||||
update_production_label(parent, science_pack_data.net)
|
||||
|
||||
-- Return the pack icon
|
||||
return pack_icon
|
||||
end)
|
||||
|
||||
local function get_science_pack_data(player, science_pack)
|
||||
local force = player.force
|
||||
|
||||
-- Check that some packs have been made
|
||||
local total = Production.get_production_total(force, science_pack)
|
||||
if total.made == 0 then return end
|
||||
local minute = Production.get_production(force, science_pack, defines.flow_precision_index.one_minute)
|
||||
local hour = Production.get_production(force, science_pack, defines.flow_precision_index.one_hour)
|
||||
|
||||
-- Get the icon style
|
||||
local icon_style = 'slot_button'
|
||||
local flux = Production.get_fluctuations(force, science_pack, defines.flow_precision_index.one_minute)
|
||||
if minute.net > 0 and flux.net > -config.color_flux/2 then
|
||||
icon_style = 'slot_sized_button_green'
|
||||
elseif flux.net < -config.color_flux then
|
||||
icon_style = 'slot_sized_button_red'
|
||||
elseif minute.made > 0 then
|
||||
icon_style = 'yellow_slot_button'
|
||||
end
|
||||
|
||||
-- Return the pack data
|
||||
return {
|
||||
science_pack = science_pack,
|
||||
icon_style = icon_style,
|
||||
positive = get_production_label_data(
|
||||
'pos-'..science_pack,
|
||||
{'science-info.pos-tooltip', total.made},
|
||||
minute.made, hour.made
|
||||
),
|
||||
negative = get_production_label_data(
|
||||
'neg-'..science_pack,
|
||||
{'science-info.neg-tooltip', total.used},
|
||||
-minute.used, hour.used
|
||||
),
|
||||
net = get_production_label_data(
|
||||
'net-'..science_pack,
|
||||
{'science-info.net-tooltip', total.net},
|
||||
minute.net, minute.net > 0 and hour.net or 0,
|
||||
minute.made+minute.used
|
||||
)
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
local function update_science_pack(pack_table, science_pack_data)
|
||||
if not science_pack_data then return end
|
||||
local science_pack = science_pack_data.science_pack
|
||||
pack_table.parent.non_made.visible = false
|
||||
|
||||
-- Update the icon
|
||||
local pack_icon = pack_table['icon-'..science_pack] or science_pack_base(pack_table, science_pack_data)
|
||||
local icon_style = science_pack_data.icon_style
|
||||
pack_icon.style = icon_style
|
||||
|
||||
local pack_icon_style = pack_icon.style
|
||||
pack_icon_style.height = 55
|
||||
if icon_style == 'slot_button' then
|
||||
pack_icon_style.padding = {0, -2}
|
||||
pack_icon_style.width = 36
|
||||
end
|
||||
|
||||
-- Update the production labels
|
||||
local delta_table = pack_table['delta-'..science_pack].table
|
||||
update_production_label(delta_table, science_pack_data.positive)
|
||||
update_production_label(delta_table, science_pack_data.negative)
|
||||
update_production_label(pack_table, science_pack_data.net)
|
||||
|
||||
end
|
||||
|
||||
--- Gets the data that is used with the eta label
|
||||
local function get_eta_label_data(player)
|
||||
local force = player.force
|
||||
|
||||
-- If there is no current research then return no research
|
||||
local research = force.current_research
|
||||
if not research then
|
||||
return { research = false }
|
||||
end
|
||||
|
||||
local limit
|
||||
local progress = force.research_progress
|
||||
local remaining = research.research_unit_count*(1-progress)
|
||||
|
||||
-- Check for the limiting science pack
|
||||
for _, ingredient in pairs(research.research_unit_ingredients) do
|
||||
local pack_name = ingredient.name
|
||||
local required = ingredient.amount * remaining
|
||||
local time = Production.get_consumsion_eta(force, pack_name, defines.flow_precision_index.one_minute, required)
|
||||
if not limit or limit < time then
|
||||
limit = time
|
||||
end
|
||||
end
|
||||
|
||||
-- Return the caption and tooltip
|
||||
return limit and limit > 0 and {
|
||||
research = true,
|
||||
caption = format_time(limit, {hours=true, minutes=true, seconds=true, time=true}),
|
||||
tooltip = format_time(limit, {hours=true, minutes=true, seconds=true, long=true})
|
||||
} or { research = false }
|
||||
|
||||
end
|
||||
|
||||
-- Updates the eta label
|
||||
local function update_eta_label(element, eta_label_data)
|
||||
-- If no research selected show null
|
||||
if not eta_label_data.research then
|
||||
element.caption = null_time_short
|
||||
element.tooltip = null_time_long
|
||||
return
|
||||
end
|
||||
|
||||
-- Update the element
|
||||
element.caption = {'science-info.eta-time', eta_label_data.caption}
|
||||
element.tooltip = eta_label_data.tooltip
|
||||
end
|
||||
|
||||
--- Main task list container for the left flow
|
||||
-- @element task_list_container
|
||||
local science_info_container =
|
||||
Gui.element(function(definition, parent)
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
|
||||
-- Draw the internal container
|
||||
local container = Gui.container(parent, definition.name, 200)
|
||||
|
||||
-- Draw the header
|
||||
Gui.header(container, {'science-info.main-caption'}, {'science-info.main-tooltip'})
|
||||
|
||||
-- Draw the scroll table for the tasks
|
||||
local scroll_table = Gui.scroll_table(container, 178, 4)
|
||||
|
||||
-- Draw the no packs label
|
||||
local no_packs_label =
|
||||
scroll_table.parent.add{
|
||||
name = 'non_made',
|
||||
type = 'label',
|
||||
caption = {'science-info.no-packs'}
|
||||
}
|
||||
|
||||
-- Change the style of the no packs label
|
||||
local no_packs_style = no_packs_label.style
|
||||
no_packs_style.padding = {2, 4}
|
||||
no_packs_style.single_line = false
|
||||
no_packs_style.width = 200
|
||||
|
||||
-- Add the footer and eta
|
||||
if config.show_eta then
|
||||
-- Draw the footer
|
||||
local footer = Gui.footer(container, {'science-info.eta-caption'}, {'science-info.eta-tooltip'}, true)
|
||||
|
||||
-- Draw the eta label
|
||||
local eta_label =
|
||||
footer.add{
|
||||
name = 'label',
|
||||
type = 'label',
|
||||
caption = null_time_short,
|
||||
tooltip = null_time_long,
|
||||
style = 'heading_1_label'
|
||||
}
|
||||
|
||||
-- Update the eta
|
||||
update_eta_label(eta_label, get_eta_label_data(player))
|
||||
|
||||
end
|
||||
|
||||
-- Add packs which have been made
|
||||
for _, science_pack in ipairs(config) do
|
||||
update_science_pack(scroll_table, get_science_pack_data(player, science_pack))
|
||||
end
|
||||
|
||||
-- Return the exteral container
|
||||
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_science_info
|
||||
Gui.left_toolbar_button('entity/lab', {'science-info.main-tooltip'}, science_info_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/science-info')
|
||||
end)
|
||||
|
||||
--- Updates the gui every 1 second
|
||||
Event.on_nth_tick(60, function()
|
||||
local force_pack_data = {}
|
||||
local force_eta_data = {}
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local force_name = player.force.name
|
||||
local frame = Gui.get_left_element(player, science_info_container)
|
||||
local container = frame.container
|
||||
|
||||
-- Update the science packs
|
||||
local scroll_table = container.scroll.table
|
||||
local pack_data = force_pack_data[force_name]
|
||||
if not pack_data then
|
||||
-- No data in cache so it needs to be generated
|
||||
pack_data = {}
|
||||
force_pack_data[force_name] = pack_data
|
||||
for _, science_pack in ipairs(config) do
|
||||
local next_data = get_science_pack_data(player, science_pack)
|
||||
pack_data[science_pack] = next_data
|
||||
update_science_pack(scroll_table, next_data)
|
||||
end
|
||||
|
||||
else
|
||||
-- Data found in cache is no need to generate it
|
||||
for _, next_data in pairs(pack_data) do
|
||||
update_science_pack(scroll_table, next_data)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Update the eta times
|
||||
if not config.show_eta then return end
|
||||
local eta_label = container.footer.alignment.label
|
||||
local eta_data = force_eta_data[force_name]
|
||||
if not eta_data then
|
||||
-- No data in chache so it needs to be generated
|
||||
eta_data = get_eta_label_data(player)
|
||||
force_eta_data[force_name] = eta_data
|
||||
update_eta_label(eta_label, eta_data)
|
||||
|
||||
else
|
||||
-- Data found in chache is no need to generate it
|
||||
update_eta_label(eta_label, eta_data)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end)
|
||||
88
exp_legacy/module/modules/gui/server-ups.lua
Normal file
88
exp_legacy/module/modules/gui/server-ups.lua
Normal file
@@ -0,0 +1,88 @@
|
||||
--[[-- Gui Module - Server UPS
|
||||
- Adds a server ups counter in the top right and a command to toggle is
|
||||
@gui server-ups
|
||||
@alias server_ups
|
||||
]]
|
||||
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local External = require 'expcore.external' --- @dep expcore.external
|
||||
|
||||
--- Stores the visible state of server ups
|
||||
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
local UsesServerUps = PlayerData.Settings:combine('UsesServerUps')
|
||||
UsesServerUps:set_default(false)
|
||||
UsesServerUps:set_metadata{
|
||||
permission = 'command/server-ups',
|
||||
stringify = function(value) return value and 'Visible' or 'Hidden' end
|
||||
}
|
||||
|
||||
--- Label to show the server ups
|
||||
-- @element server_ups
|
||||
local server_ups =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
caption = 'SUPS = 60.0',
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style{
|
||||
font = 'default-game'
|
||||
}
|
||||
|
||||
--- Change the visible state when your data loads
|
||||
UsesServerUps:on_load(function(player_name, visible)
|
||||
local player = game.players[player_name]
|
||||
local label = player.gui.screen[server_ups.name]
|
||||
if not External.valid() or not global.ext.var.server_ups then visible = false end
|
||||
label.visible = visible
|
||||
end)
|
||||
|
||||
--- Toggles if the server ups is visbile
|
||||
-- @command server-ups
|
||||
Commands.new_command('server-ups', 'Toggle the server UPS display')
|
||||
:add_alias('sups', 'ups')
|
||||
:register(function(player)
|
||||
local label = player.gui.screen[server_ups.name]
|
||||
if not External.valid() then
|
||||
label.visible = false
|
||||
return Commands.error{'expcom-server-ups.no-ext'}
|
||||
end
|
||||
label.visible = not label.visible
|
||||
UsesServerUps:set(player, label.visible)
|
||||
end)
|
||||
|
||||
-- Set the location of the label
|
||||
-- 1920x1080: x=1455, y=30 (ui scale 100%)
|
||||
local function set_location(event)
|
||||
local player = game.players[event.player_index]
|
||||
local label = player.gui.screen[server_ups.name]
|
||||
local res = player.display_resolution
|
||||
local uis = player.display_scale
|
||||
-- below ups and clock
|
||||
-- label.location = {x=res.width-423*uis, y=50*uis}
|
||||
label.location = {x=res.width-363*uis, y=31*uis}
|
||||
end
|
||||
|
||||
-- Draw the label when the player joins
|
||||
Event.add(defines.events.on_player_created, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
local label = server_ups(player.gui.screen)
|
||||
label.visible = false
|
||||
set_location(event)
|
||||
end)
|
||||
|
||||
-- Update the caption for all online players
|
||||
-- percentage of game speed
|
||||
Event.on_nth_tick(60, function()
|
||||
if External.valid() then
|
||||
local caption = External.get_server_ups() .. ' (' .. string.format('%.1f', External.get_server_ups() * 5 / 3) .. '%)'
|
||||
for _, player in pairs(game.connected_players) do
|
||||
player.gui.screen[server_ups.name].caption = caption
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Update when res or ui scale changes
|
||||
Event.add(defines.events.on_player_display_resolution_changed, set_location)
|
||||
Event.add(defines.events.on_player_display_scale_changed, set_location)
|
||||
199
exp_legacy/module/modules/gui/surveillance.lua
Normal file
199
exp_legacy/module/modules/gui/surveillance.lua
Normal file
@@ -0,0 +1,199 @@
|
||||
---- module surveillance
|
||||
-- @gui surveillance
|
||||
|
||||
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 cctv_player =
|
||||
Gui.element(function(definition, parent, player_list)
|
||||
return parent.add{
|
||||
name = definition.name,
|
||||
type = 'drop-down',
|
||||
items = player_list,
|
||||
selected_index = #player_list > 0 and 1
|
||||
}
|
||||
end)
|
||||
:style{
|
||||
horizontally_stretchable = true
|
||||
}
|
||||
:static_name(Gui.unique_static_name)
|
||||
|
||||
local cctv_status =
|
||||
Gui.element{
|
||||
type = 'drop-down',
|
||||
items = {{'surveillance.status-enable'}, {'surveillance.status-disable'}},
|
||||
selected_index = 2
|
||||
}:style{
|
||||
width = 96
|
||||
}:on_selection_changed(function(_, element, _)
|
||||
if element.selected_index == 1 then
|
||||
element.parent.parent.parent.cctv_display.visible = true
|
||||
else
|
||||
element.parent.parent.parent.cctv_display.visible = false
|
||||
end
|
||||
end)
|
||||
|
||||
local cctv_type =
|
||||
Gui.element{
|
||||
type = 'drop-down',
|
||||
name = Gui.unique_static_name,
|
||||
items = {{'surveillance.type-player'}, {'surveillance.type-static'}, {'surveillance.type-player-loop'}},
|
||||
selected_index = 1
|
||||
}:style{
|
||||
width = 96
|
||||
}
|
||||
|
||||
local cctv_location =
|
||||
Gui.element{
|
||||
type = 'button',
|
||||
name = Gui.unique_static_name,
|
||||
caption = {'surveillance.func-set'}
|
||||
}:style{
|
||||
width = 48
|
||||
}:on_click(function(player, element, _)
|
||||
element.parent.parent.parent.cctv_display.position = player.position
|
||||
end)
|
||||
|
||||
local zoom_in =
|
||||
Gui.element{
|
||||
type = 'button',
|
||||
name = Gui.unique_static_name,
|
||||
caption = '+'
|
||||
}:style{
|
||||
width = 32
|
||||
}:on_click(function(_, element, _)
|
||||
local display = element.parent.parent.parent.cctv_display
|
||||
if display.zoom < 2.0 then
|
||||
display.zoom = display.zoom + 0.05
|
||||
end
|
||||
end)
|
||||
|
||||
local zoom_out =
|
||||
Gui.element{
|
||||
type = 'button',
|
||||
name = Gui.unique_static_name,
|
||||
caption = '-'
|
||||
}:style{
|
||||
width = 32
|
||||
}:on_click(function(_, element, _)
|
||||
local display = element.parent.parent.parent.cctv_display
|
||||
if display.zoom > 0.2 then
|
||||
display.zoom = display.zoom - 0.05
|
||||
end
|
||||
end)
|
||||
|
||||
local camera_set =
|
||||
Gui.element(function(_, parent, name, player_list)
|
||||
local camera_set = parent.add{type='flow', direction='vertical', name=name}
|
||||
local buttons = Gui.scroll_table(camera_set, 480, 6, 'buttons')
|
||||
|
||||
cctv_player(buttons, player_list)
|
||||
cctv_status(buttons)
|
||||
cctv_type(buttons)
|
||||
cctv_location(buttons)
|
||||
zoom_out(buttons)
|
||||
zoom_in(buttons)
|
||||
|
||||
local camera = camera_set.add{
|
||||
type = 'camera',
|
||||
name = 'cctv_display',
|
||||
position = {x=0, y=0},
|
||||
surface_index = game.surfaces['nauvis'].index,
|
||||
zoom = 0.75,
|
||||
}
|
||||
|
||||
camera.visible = false
|
||||
camera.style.minimal_width = 480
|
||||
camera.style.minimal_height = 290
|
||||
return camera_set
|
||||
end)
|
||||
|
||||
local cctv_container =
|
||||
Gui.element(function(definition, parent)
|
||||
local container = Gui.container(parent, definition.name, 480)
|
||||
local scroll = container.add{name='scroll', type='scroll-pane', direction='vertical'}
|
||||
scroll.style.maximal_height = 704
|
||||
local player_list = {}
|
||||
|
||||
for _, player in pairs(game.connected_players) do
|
||||
table.insert(player_list, player.name)
|
||||
end
|
||||
|
||||
camera_set(scroll, 'cctv_st_1', player_list)
|
||||
camera_set(scroll, 'cctv_st_2', player_list)
|
||||
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow()
|
||||
|
||||
Gui.left_toolbar_button('entity/radar', {'surveillance.main-tooltip'}, cctv_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/surveillance')
|
||||
end)
|
||||
|
||||
local function gui_update()
|
||||
local player_list = {}
|
||||
|
||||
for _, player in pairs(game.connected_players) do
|
||||
table.insert(player_list, player.name)
|
||||
end
|
||||
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local frame = Gui.get_left_element(player, cctv_container)
|
||||
frame.container.scroll['cctv_st_1'].buttons.table[cctv_player.name].items = player_list
|
||||
frame.container.scroll['cctv_st_2'].buttons.table[cctv_player.name].items = player_list
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_player_joined_game, gui_update)
|
||||
Event.add(defines.events.on_player_left_game, gui_update)
|
||||
|
||||
Event.add(defines.events.on_tick, function(_)
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local frame = Gui.get_left_element(player, cctv_container)
|
||||
|
||||
for i=1, 2 do
|
||||
local scroll_table_name = 'cctv_st_' .. i
|
||||
local current_camera_set = frame.container.scroll[scroll_table_name]
|
||||
local switch_index = current_camera_set.buttons.table[cctv_type.name].selected_index
|
||||
|
||||
if (switch_index == 1) or (switch_index == 3) then
|
||||
local selected_index = current_camera_set.buttons.table[cctv_player.name].selected_index
|
||||
|
||||
if selected_index ~= 0 then
|
||||
selected_index = current_camera_set.buttons.table[cctv_player.name].items[selected_index]
|
||||
current_camera_set['cctv_display'].position = game.players[selected_index].position
|
||||
current_camera_set['cctv_display'].surface_index = game.players[selected_index].surface_index
|
||||
|
||||
else
|
||||
current_camera_set['cctv_display'].position = {x=0, y=0}
|
||||
current_camera_set['cctv_display'].surface_index = game.surfaces['nauvis'].index
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Event.on_nth_tick(600, function(_)
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local frame = Gui.get_left_element(player, cctv_container)
|
||||
|
||||
for i=1, 2 do
|
||||
local current_camera_set = frame.container.scroll['cctv_st_' .. i]
|
||||
|
||||
if current_camera_set.buttons.table[cctv_type.name].selected_index == 3 then
|
||||
local item_n = #current_camera_set.buttons.table[cctv_player.name].items
|
||||
|
||||
if item_n ~= 0 then
|
||||
if current_camera_set.buttons.table[cctv_player.name].selected_index < item_n then
|
||||
current_camera_set.buttons.table[cctv_player.name].selected_index = current_camera_set.buttons.table[cctv_player.name].selected_index + 1
|
||||
|
||||
else
|
||||
current_camera_set.buttons.table[cctv_player.name].selected_index = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
782
exp_legacy/module/modules/gui/task-list.lua
Normal file
782
exp_legacy/module/modules/gui/task-list.lua
Normal file
@@ -0,0 +1,782 @@
|
||||
--[[-- Gui Module - Task List
|
||||
- Adds a task list to the game which players can add, remove and edit items on
|
||||
@gui Task-List
|
||||
@alias task_list
|
||||
]]
|
||||
local Gui = require "expcore.gui" --- @dep expcore.gui
|
||||
local Event = require "utils.event" --- @dep utils.event
|
||||
local Roles = require "expcore.roles" --- @dep expcore.roles
|
||||
local Datastore = require "expcore.datastore" --- @dep expcore.datastore
|
||||
local config = require "config.gui.tasks" --- @dep config.gui.tasks
|
||||
local Tasks = require "modules.control.tasks" --- @dep modules.control.tasks
|
||||
local format_time = _C.format_time --- @dep expcore.common
|
||||
|
||||
--- Stores all data for the task gui by player
|
||||
local TaskGuiData = Datastore.connect("TaskGuiData")
|
||||
TaskGuiData:set_serializer(Datastore.name_serializer)
|
||||
local PlayerIsEditing = TaskGuiData:combine("PlayerIsEditing")
|
||||
PlayerIsEditing:set_default(false)
|
||||
local PlayerIsCreating = TaskGuiData:combine("PlayerIsCreating")
|
||||
PlayerIsCreating:set_default(false)
|
||||
local PlayerSelected = TaskGuiData:combine("PlayerSelected")
|
||||
PlayerSelected:set_default(nil)
|
||||
|
||||
-- Styles used for sprite buttons
|
||||
local Styles = {
|
||||
sprite22 = {
|
||||
height = 22,
|
||||
width = 22,
|
||||
padding = -2
|
||||
},
|
||||
footer_button = {
|
||||
height = 29,
|
||||
maximal_width = 268,
|
||||
horizontally_stretchable = true,
|
||||
padding = -2
|
||||
}
|
||||
}
|
||||
|
||||
--- If a player is allowed to use the edit buttons
|
||||
local function check_player_permissions(player, task)
|
||||
if task then
|
||||
-- When a task is given check if the player can edit it
|
||||
local allow_edit_task = config.allow_edit_task
|
||||
|
||||
-- Check if the player being the last to edit will override existing permisisons
|
||||
if config.user_can_edit_own_tasks and task.last_edit_name == player.name then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Check player has permisison based on value in the config
|
||||
if allow_edit_task == "all" then
|
||||
return true
|
||||
elseif allow_edit_task == "admin" then
|
||||
return player.admin
|
||||
elseif allow_edit_task == "expcore.roles" then
|
||||
return Roles.player_allowed(player, config.expcore_roles_allow_edit_task)
|
||||
end
|
||||
|
||||
-- Return false as all other condidtions have not been met
|
||||
return false
|
||||
else
|
||||
-- When a task is not given check if the player can add a new task
|
||||
local allow_add_task = config.allow_add_task
|
||||
|
||||
-- Check player has permisison based on value in the config
|
||||
if allow_add_task == "all" then
|
||||
return true
|
||||
elseif allow_add_task == "admin" then
|
||||
return player.admin
|
||||
elseif allow_add_task == "expcore.roles" then
|
||||
return Roles.player_allowed(player, config.expcore_roles_allow_add_task)
|
||||
end
|
||||
|
||||
-- Return false as all other condidtions have not been met
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--- Elements
|
||||
|
||||
--- Button displayed in the header bar, used to add a new task
|
||||
-- @element add_new_task
|
||||
local add_new_task =
|
||||
Gui.element {
|
||||
type = "sprite-button",
|
||||
sprite = "utility/add",
|
||||
tooltip = {"task-list.add-tooltip"},
|
||||
style = "tool_button",
|
||||
name = Gui.unique_static_name
|
||||
}:style(Styles.sprite22):on_click(
|
||||
function(player, _, _)
|
||||
-- Disable editing
|
||||
PlayerIsEditing:set(player, false)
|
||||
-- Clear selected
|
||||
PlayerSelected:set(player, nil)
|
||||
-- Open task create footer
|
||||
PlayerIsCreating:set(player, true)
|
||||
end
|
||||
)
|
||||
|
||||
--- Header displayed when no tasks are in the task list
|
||||
-- @element no_tasks_found
|
||||
local no_tasks_found =
|
||||
Gui.element(
|
||||
function(_, parent)
|
||||
local header =
|
||||
parent.add {
|
||||
name = "no_tasks_found_element",
|
||||
type = "frame",
|
||||
style = "negative_subheader_frame"
|
||||
}
|
||||
header.style.horizontally_stretchable = true
|
||||
-- Flow used for centering the content in the subheader
|
||||
local center =
|
||||
header.add {
|
||||
type = "flow",
|
||||
style = "centering_horizontal_flow"
|
||||
}
|
||||
center.style.horizontally_stretchable = true
|
||||
center.add {
|
||||
name = "header_label",
|
||||
type = "label",
|
||||
style = "bold_label",
|
||||
caption = {"", "[img=utility/warning_white] ", {"task-list.no-tasks"}},
|
||||
tooltip = {"task-list.no-tasks-tooltip"}
|
||||
}
|
||||
return header
|
||||
end
|
||||
)
|
||||
|
||||
--- Frame element with the right styling
|
||||
-- @element subfooter_frame
|
||||
local subfooter_frame =
|
||||
Gui.element(
|
||||
function(_, parent, name)
|
||||
return parent.add {
|
||||
type = "frame",
|
||||
name = name,
|
||||
direction = "vertical",
|
||||
style = "subfooter_frame"
|
||||
}
|
||||
end
|
||||
):style(
|
||||
{
|
||||
padding = 5,
|
||||
use_header_filler = false,
|
||||
horizontally_stretchable = true
|
||||
}
|
||||
)
|
||||
|
||||
--- Label element preset
|
||||
-- @element subfooter_label
|
||||
local subfooter_label =
|
||||
Gui.element(
|
||||
function(_, parent, caption)
|
||||
return parent.add {
|
||||
name = "footer_label",
|
||||
type = "label",
|
||||
style = "heading_1_label",
|
||||
caption = caption
|
||||
}
|
||||
end
|
||||
)
|
||||
|
||||
--- Action flow that contains action buttons
|
||||
-- @element subfooter_actions
|
||||
local subfooter_actions =
|
||||
Gui.element {
|
||||
type = "flow",
|
||||
name = "actions"
|
||||
}
|
||||
|
||||
--- Button element with a flow around it to fix duplicate name inside of the scroll flow
|
||||
-- @element task_list_item
|
||||
local task_list_item =
|
||||
Gui.element(
|
||||
function(definition, parent, task)
|
||||
local flow = parent.add {
|
||||
type = "flow",
|
||||
name = "task-" .. task.task_id,
|
||||
caption = task.task_id
|
||||
}
|
||||
|
||||
flow.style.horizontally_stretchable = true
|
||||
|
||||
local button = flow.add {
|
||||
name = definition.name,
|
||||
type = "button",
|
||||
style = "list_box_item",
|
||||
caption = task.title,
|
||||
tooltip = { "task-list.last-edit", task.last_edit_name, format_time(task.last_edit_time) }
|
||||
}
|
||||
|
||||
button.style.horizontally_stretchable = true
|
||||
button.style.horizontally_squashable = true
|
||||
|
||||
return button
|
||||
end
|
||||
):on_click(
|
||||
function(player, element, _)
|
||||
local task_id = element.parent.caption
|
||||
PlayerSelected:set(player, task_id)
|
||||
end
|
||||
):static_name(Gui.unique_static_name)
|
||||
|
||||
--- Scrollable list of all tasks
|
||||
-- @element task_list
|
||||
local task_list =
|
||||
Gui.element(
|
||||
function(_, parent)
|
||||
local scroll_pane =
|
||||
parent.add {
|
||||
name = "scroll",
|
||||
type = "scroll-pane",
|
||||
direction = "vertical",
|
||||
horizontal_scroll_policy = "never",
|
||||
vertical_scroll_policy = "auto",
|
||||
style = "scroll_pane_under_subheader"
|
||||
}
|
||||
scroll_pane.style.horizontally_stretchable = true
|
||||
scroll_pane.style.padding = 0
|
||||
scroll_pane.style.maximal_height = 224
|
||||
|
||||
local flow =
|
||||
scroll_pane.add {
|
||||
name = "task_list",
|
||||
type = "flow",
|
||||
direction = "vertical"
|
||||
}
|
||||
flow.style.vertical_spacing = 0
|
||||
flow.style.horizontally_stretchable = true
|
||||
|
||||
return flow
|
||||
end
|
||||
)
|
||||
|
||||
--- Button element inside the task view footer to start editing a task
|
||||
-- @element task_view_edit_button
|
||||
local task_view_edit_button =
|
||||
Gui.element {
|
||||
type = "button",
|
||||
name = Gui.unique_static_name,
|
||||
caption = {"", "[img=utility/rename_icon_normal] ", {"task-list.edit"}},
|
||||
tooltip = {"task-list.edit-tooltip"},
|
||||
style = "shortcut_bar_button"
|
||||
}:style(Styles.footer_button):on_click(
|
||||
function(player, _, _)
|
||||
local selected = PlayerSelected:get(player)
|
||||
PlayerIsEditing:set(player, true)
|
||||
|
||||
Tasks.set_editing(selected, player.name, true)
|
||||
end
|
||||
)
|
||||
|
||||
--- Button to close the task view footer
|
||||
-- @element task_view_close_button
|
||||
local task_view_close_button =
|
||||
Gui.element{
|
||||
type = "sprite-button",
|
||||
sprite = "utility/collapse_dark",
|
||||
hovered_sprite = "utility/collapse",
|
||||
tooltip = {"task-list.close-tooltip"}
|
||||
}
|
||||
:style(Styles.sprite22):on_click(
|
||||
function(player, _, _)
|
||||
PlayerSelected:set(player, nil)
|
||||
end
|
||||
)
|
||||
|
||||
--- Button to delete the task inside the task view footer
|
||||
-- @element task_view_delete_button
|
||||
local task_view_delete_button =
|
||||
Gui.element {
|
||||
type = "button",
|
||||
name = Gui.unique_static_name,
|
||||
caption = {"", "[img=utility/trash] ", {"task-list.delete"}},
|
||||
tooltip = {"task-list.delete-tooltip"},
|
||||
style = "shortcut_bar_button_red"
|
||||
}:style(Styles.footer_button):on_click(
|
||||
function(player, _, _)
|
||||
local selected = PlayerSelected:get(player)
|
||||
PlayerSelected:set(player, nil)
|
||||
Tasks.remove_task(selected)
|
||||
end
|
||||
)
|
||||
|
||||
--- Subfooter inside the tasklist container that holds all the elements for viewing a task
|
||||
-- @element task_view_footer
|
||||
local task_view_footer =
|
||||
Gui.element(
|
||||
function(_, parent)
|
||||
local footer = subfooter_frame(parent, "view")
|
||||
local flow = footer.add{ type = "flow" }
|
||||
subfooter_label(flow, {"task-list.view-footer-header"})
|
||||
local alignment = Gui.alignment(flow)
|
||||
task_view_close_button(alignment)
|
||||
local title_label =
|
||||
footer.add {
|
||||
type = "label",
|
||||
name = "title"
|
||||
}
|
||||
title_label.style.padding = 4
|
||||
title_label.style.font = "default-bold"
|
||||
title_label.style.single_line = false
|
||||
local body_label =
|
||||
footer.add {
|
||||
type = "label",
|
||||
name = "body"
|
||||
}
|
||||
body_label.style.padding = 4
|
||||
body_label.style.single_line = false
|
||||
|
||||
local action_flow = subfooter_actions(footer)
|
||||
task_view_delete_button(action_flow)
|
||||
task_view_edit_button(action_flow)
|
||||
return footer
|
||||
end
|
||||
)
|
||||
|
||||
local message_pattern = "(.-)\n(.*)"
|
||||
|
||||
--- Parse a string into a message object with title and body
|
||||
-- @tparam string str message data
|
||||
local function parse_message(str)
|
||||
-- Trim the spaces of the string
|
||||
local trimmed = string.gsub(str, "^%s*(.-)%s*$", "%1")
|
||||
local message = { title = "", body = "" }
|
||||
local title, body = string.match(trimmed, message_pattern)
|
||||
if not title then
|
||||
-- If it doesn't match the pattern return the str as a title
|
||||
message.title = trimmed
|
||||
else
|
||||
message.title = title
|
||||
message.body = body
|
||||
end
|
||||
return message
|
||||
end
|
||||
|
||||
-- Button variable initialisation because it is used inside the textfield element events
|
||||
local task_edit_confirm_button
|
||||
local task_create_confirm_button
|
||||
|
||||
--- Textfield element used in both the task create and edit footers
|
||||
-- @element task_message_textfield
|
||||
local task_message_textfield =
|
||||
Gui.element {
|
||||
name = Gui.unique_static_name,
|
||||
type = "text-box",
|
||||
text = ""
|
||||
}:style(
|
||||
{
|
||||
maximal_width = 268,
|
||||
minimal_height = 100,
|
||||
horizontally_stretchable = true
|
||||
}
|
||||
):on_text_changed(
|
||||
function(player, element, _)
|
||||
local isEditing = PlayerIsEditing:get(player)
|
||||
local isCreating = PlayerIsCreating:get(player)
|
||||
|
||||
local valid = string.len(element.text) > 5
|
||||
|
||||
if isCreating then
|
||||
element.parent.actions[task_create_confirm_button.name].enabled = valid
|
||||
elseif isEditing then
|
||||
element.parent.actions[task_edit_confirm_button.name].enabled = valid
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
--- Button to confirm the changes inside the task edit footer
|
||||
-- @element task_edit_confirm_button
|
||||
task_edit_confirm_button =
|
||||
Gui.element {
|
||||
type = "button",
|
||||
name = Gui.unique_static_name,
|
||||
caption = {"", "[img=utility/check_mark] ", {"task-list.confirm"}},
|
||||
tooltip = {"task-list.confirm-tooltip"},
|
||||
style = "shortcut_bar_button_green"
|
||||
}:style(Styles.footer_button):on_click(
|
||||
function(player, element, _)
|
||||
local selected = PlayerSelected:get(player)
|
||||
PlayerIsEditing:set(player, false)
|
||||
local new_message = element.parent.parent[task_message_textfield.name].text
|
||||
local parsed = parse_message(new_message)
|
||||
Tasks.update_task(selected, player.name, parsed.title, parsed.body)
|
||||
Tasks.set_editing(selected, player.name, nil)
|
||||
end
|
||||
)
|
||||
|
||||
--- Button to discard the changes inside the task edit footer
|
||||
-- @element edit_task_discard_button
|
||||
local edit_task_discard_button =
|
||||
Gui.element {
|
||||
type = "button",
|
||||
caption = {"", "[img=utility/close_black] ", {"task-list.discard"}},
|
||||
tooltip = {"task-list.discard-tooltip"},
|
||||
style = "shortcut_bar_button_red"
|
||||
}:style(Styles.footer_button):on_click(
|
||||
function(player, _, _)
|
||||
local selected = PlayerSelected:get(player)
|
||||
Tasks.set_editing(selected, player.name, nil)
|
||||
PlayerIsEditing:set(player, false)
|
||||
end
|
||||
)
|
||||
|
||||
--- Subfooter inside the tasklist container that holds all the elements for editing a task
|
||||
-- @element task_edit_footer
|
||||
local task_edit_footer =
|
||||
Gui.element(
|
||||
function(_, parent)
|
||||
local footer = subfooter_frame(parent, "edit")
|
||||
subfooter_label(footer, {"task-list.edit-footer-header"})
|
||||
|
||||
task_message_textfield(footer)
|
||||
|
||||
local action_flow = subfooter_actions(footer)
|
||||
|
||||
edit_task_discard_button(action_flow)
|
||||
task_edit_confirm_button(action_flow)
|
||||
|
||||
return footer
|
||||
end
|
||||
)
|
||||
|
||||
--- Button to confirm the changes inside the task create footer
|
||||
-- @element task_create_confirm_button
|
||||
task_create_confirm_button =
|
||||
Gui.element {
|
||||
type = "button",
|
||||
name = Gui.unique_static_name,
|
||||
caption = {"", "[img=utility/check_mark] ", {"task-list.confirm"}},
|
||||
tooltip = {"task-list.confirm-tooltip"},
|
||||
style = "shortcut_bar_button_green",
|
||||
enabled = false
|
||||
}:style(Styles.footer_button):on_click(
|
||||
function(player, element, _)
|
||||
local message = element.parent.parent[task_message_textfield.name].text
|
||||
PlayerIsCreating:set(player, false)
|
||||
local parsed = parse_message(message)
|
||||
local task_id = Tasks.add_task(player.force.name, player.name, parsed.title, parsed.body)
|
||||
PlayerSelected:set(player, task_id)
|
||||
end
|
||||
)
|
||||
|
||||
--- Button to discard the changes inside the task create footer
|
||||
-- @element task_create_discard_button
|
||||
local task_create_discard_button =
|
||||
Gui.element {
|
||||
type = "button",
|
||||
caption = {"", "[img=utility/close_black] ", {"task-list.discard"}},
|
||||
tooltip = {"task-list.discard-tooltip"},
|
||||
style = "shortcut_bar_button_red"
|
||||
}:style(Styles.footer_button):on_click(
|
||||
function(player, _, _)
|
||||
PlayerIsCreating:set(player, false)
|
||||
end
|
||||
)
|
||||
|
||||
--- Subfooter inside the tasklist container that holds all the elements to create a new task
|
||||
-- @element task_create_footer
|
||||
local task_create_footer =
|
||||
Gui.element(
|
||||
function(_, parent)
|
||||
local footer = subfooter_frame(parent, "create")
|
||||
subfooter_label(footer, {"task-list.create-footer-header"})
|
||||
|
||||
task_message_textfield(footer)
|
||||
|
||||
local action_flow = subfooter_actions(footer)
|
||||
|
||||
task_create_discard_button(action_flow)
|
||||
task_create_confirm_button(action_flow)
|
||||
|
||||
return footer
|
||||
end
|
||||
)
|
||||
|
||||
--- Clear and repopulate the task list with all current tasks
|
||||
local repopulate_task_list = function(task_list_element)
|
||||
local force = Gui.get_player_from_element(task_list_element).force
|
||||
local task_ids = Tasks.get_force_task_ids(force.name)
|
||||
task_list_element.clear()
|
||||
|
||||
-- Set visibility of the no_tasks_found element depending on the amount of tasks still in the task manager
|
||||
task_list_element.parent.parent.no_tasks_found_element.visible = #task_ids == 0
|
||||
|
||||
-- Add each task to the flow
|
||||
for _, task_id in ipairs(task_ids) do
|
||||
-- Add the task
|
||||
local task = Tasks.get_task(task_id)
|
||||
task_list_item(task_list_element, task)
|
||||
end
|
||||
end
|
||||
|
||||
--- Main task list container for the left flow
|
||||
-- @element task_list_container
|
||||
local task_list_container =
|
||||
Gui.element(
|
||||
function(definition, parent)
|
||||
-- Draw the internal container
|
||||
local container = Gui.container(parent, definition.name, 268)
|
||||
container.style.maximal_width = 268
|
||||
container.style.minimal_width = 268
|
||||
|
||||
-- Draw the header
|
||||
local header = Gui.header(container, {"task-list.main-caption"}, {"task-list.sub-tooltip"}, true)
|
||||
|
||||
-- Draw the new task button
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
local add_new_task_element = add_new_task(header)
|
||||
add_new_task_element.visible = check_player_permissions(player)
|
||||
|
||||
-- Draw no task found element
|
||||
no_tasks_found(container)
|
||||
|
||||
-- Draw task list element
|
||||
local task_list_element = task_list(container)
|
||||
repopulate_task_list(task_list_element)
|
||||
|
||||
local task_view_footer_element = task_view_footer(container)
|
||||
local task_edit_footer_element = task_edit_footer(container)
|
||||
local task_create_footer_element = task_create_footer(container)
|
||||
task_view_footer_element.visible = false
|
||||
task_edit_footer_element.visible = false
|
||||
task_create_footer_element.visible = false
|
||||
-- Return the external container
|
||||
return container.parent
|
||||
end
|
||||
):static_name(Gui.unique_static_name):add_to_left_flow(
|
||||
function(player)
|
||||
local task_ids = Tasks.get_force_task_ids(player.force.name)
|
||||
return #task_ids > 0
|
||||
end
|
||||
)
|
||||
|
||||
--- Button on the top flow used to toggle the task list container
|
||||
-- @element toggle_left_element
|
||||
Gui.left_toolbar_button(
|
||||
"utility/not_enough_repair_packs_icon",
|
||||
{"task-list.main-tooltip"},
|
||||
task_list_container,
|
||||
function(player)
|
||||
return Roles.player_allowed(player, "gui/task-list")
|
||||
end
|
||||
)
|
||||
|
||||
-- Function to update a single task and some of the elements inside the container
|
||||
local update_task = function(player, task_list_element, task_id)
|
||||
local task = Tasks.get_task(task_id)
|
||||
local task_ids = Tasks.get_force_task_ids(player.force.name)
|
||||
-- Set visibility of the no_tasks_found element depending on the amount of tasks still in the task manager
|
||||
task_list_element.parent.parent.no_tasks_found_element.visible = #task_ids == 0
|
||||
|
||||
-- Task no longer exists so should be removed from the list
|
||||
if not task then
|
||||
task_list_element["task-" .. task_id].destroy()
|
||||
return
|
||||
end
|
||||
|
||||
local flow = task_list_element["task-" .. task_id]
|
||||
if not flow then
|
||||
-- If task does not exist yet add it to the list
|
||||
task_list_item(task_list_element, task)
|
||||
else
|
||||
-- If the task exists update the caption and tooltip
|
||||
local button = flow[task_list_item.name]
|
||||
button.caption = task.title
|
||||
button.tooltip = {"task-list.last-edit", task.last_edit_name, format_time(task.last_edit_time)}
|
||||
end
|
||||
end
|
||||
|
||||
-- Update the footer task edit view
|
||||
local update_task_edit_footer = function(player, task_id)
|
||||
local task = Tasks.get_task(task_id)
|
||||
local frame = Gui.get_left_element(player, task_list_container)
|
||||
local edit_flow = frame.container.edit
|
||||
|
||||
local message_element = edit_flow[task_message_textfield.name]
|
||||
|
||||
message_element.focus()
|
||||
message_element.text = task.title .. "\n" .. task.body
|
||||
end
|
||||
|
||||
-- Update the footer task view
|
||||
local update_task_view_footer = function(player, task_id)
|
||||
local task = Tasks.get_task(task_id)
|
||||
local frame = Gui.get_left_element(player, task_list_container)
|
||||
local view_flow = frame.container.view
|
||||
local has_permission = check_player_permissions(player, task)
|
||||
|
||||
local title_element = view_flow.title
|
||||
local body_element = view_flow.body
|
||||
local edit_button_element = view_flow.actions[task_view_edit_button.name]
|
||||
local delete_button_element = view_flow.actions[task_view_delete_button.name]
|
||||
|
||||
edit_button_element.visible = has_permission
|
||||
delete_button_element.visible = has_permission
|
||||
title_element.caption = task.title
|
||||
body_element.caption = task.body
|
||||
|
||||
local players_editing = table.get_keys(task.currently_editing)
|
||||
if #players_editing > 0 then
|
||||
edit_button_element.tooltip = {"task-list.edit-tooltip", table.concat(players_editing, ", ")}
|
||||
else
|
||||
edit_button_element.tooltip = {"task-list.edit-tooltip-none"}
|
||||
end
|
||||
end
|
||||
|
||||
--- When a new task is added it will update the task list for everyone on that force
|
||||
--- Or when a task is updated it will update the specific task elements
|
||||
Tasks.on_update(
|
||||
function(task_id, curr_state, prev_state)
|
||||
-- Get the force to update, task is nil when removed
|
||||
local force
|
||||
if curr_state then
|
||||
force = game.forces[curr_state.force_name]
|
||||
else
|
||||
force = game.forces[prev_state.force_name]
|
||||
end
|
||||
|
||||
-- Update the task for all the players on the force
|
||||
for _, player in pairs(force.connected_players) do
|
||||
-- Update the task view elements if the player currently being looped over has this specific task selected
|
||||
local selected = PlayerSelected:get(player)
|
||||
if selected == task_id then
|
||||
if curr_state then
|
||||
update_task_view_footer(player, selected)
|
||||
else
|
||||
PlayerSelected:set(player, nil)
|
||||
end
|
||||
end
|
||||
|
||||
local frame = Gui.get_left_element(player, task_list_container)
|
||||
local task_list_element = frame.container.scroll.task_list
|
||||
|
||||
-- Update the task that was changed
|
||||
update_task(player, task_list_element, task_id)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
-- When a player is creating a new task.
|
||||
PlayerIsCreating:on_update(
|
||||
function(player_name, curr_state, _)
|
||||
local player = game.players[player_name]
|
||||
|
||||
local frame = Gui.get_left_element(player, task_list_container)
|
||||
local create = frame.container.create
|
||||
|
||||
-- Clear the textfield
|
||||
local message_element = frame.container.create[task_message_textfield.name]
|
||||
local confirm_button_element = frame.container.create.actions[task_create_confirm_button.name]
|
||||
message_element.focus()
|
||||
message_element.text = ""
|
||||
confirm_button_element.enabled = false
|
||||
|
||||
if curr_state then
|
||||
create.visible = true
|
||||
else
|
||||
create.visible = false
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
-- When a player selects a different warp from the list
|
||||
PlayerSelected:on_update(
|
||||
function(player_name, curr_state, prev_state)
|
||||
local player = game.players[player_name]
|
||||
|
||||
local frame = Gui.get_left_element(player, task_list_container)
|
||||
local task_list_element = frame.container.scroll.task_list
|
||||
local view_flow = frame.container.view
|
||||
local edit_flow = frame.container.edit
|
||||
local isEditing = PlayerIsEditing:get(player)
|
||||
local isCreating = PlayerIsCreating:get(player)
|
||||
|
||||
-- If the selection has an previous state re-enable the button list element
|
||||
if prev_state then
|
||||
task_list_element["task-" .. prev_state][task_list_item.name].enabled = true
|
||||
end
|
||||
|
||||
if curr_state then
|
||||
-- Disable the selected element
|
||||
task_list_element["task-" .. curr_state][task_list_item.name].enabled = false
|
||||
|
||||
-- Update the view footer
|
||||
update_task_view_footer(player, curr_state)
|
||||
|
||||
-- If a player is creating then remove the creation dialogue
|
||||
if isCreating then
|
||||
PlayerIsCreating:set(player, false)
|
||||
end
|
||||
|
||||
-- Depending on if the player is currently editing change the current task edit footer to the current task
|
||||
if isEditing then
|
||||
update_task_edit_footer(player, curr_state)
|
||||
Tasks.set_editing(prev_state, player.name, nil)
|
||||
Tasks.set_editing(curr_state, player.name, true)
|
||||
view_flow.visible = false
|
||||
edit_flow.visible = true
|
||||
else
|
||||
view_flow.visible = true
|
||||
edit_flow.visible = false
|
||||
end
|
||||
else
|
||||
-- If curr_state nil then hide footer elements and set editing to nil for prev_state
|
||||
if prev_state and Tasks.get_task(prev_state) then
|
||||
Tasks.set_editing(prev_state, player.name, nil)
|
||||
end
|
||||
view_flow.visible = false
|
||||
edit_flow.visible = false
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
-- When the edit view opens or closes
|
||||
PlayerIsEditing:on_update(
|
||||
function(player_name, curr_state, _)
|
||||
local player = game.players[player_name]
|
||||
|
||||
local frame = Gui.get_left_element(player, task_list_container)
|
||||
local view_flow = frame.container.view
|
||||
local edit_flow = frame.container.edit
|
||||
|
||||
local selected = PlayerSelected:get(player)
|
||||
if curr_state then
|
||||
update_task_edit_footer(player, selected)
|
||||
view_flow.visible = false
|
||||
edit_flow.visible = true
|
||||
else
|
||||
view_flow.visible = true
|
||||
edit_flow.visible = false
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
--- Makes sure the right buttons are present when roles change
|
||||
local function role_update_event(event)
|
||||
local player = game.players[event.player_index]
|
||||
local container = Gui.get_left_element(player, task_list_container).container
|
||||
-- Update the view task
|
||||
local selected = PlayerSelected:get(player)
|
||||
if selected then
|
||||
update_task_view_footer(player, selected)
|
||||
PlayerSelected:set(player, selected)
|
||||
-- button to edit the task.
|
||||
-- Resetting the players selected task to make sure the player does not see an
|
||||
end
|
||||
|
||||
-- Update the new task button and create footer in case the user can now add them
|
||||
local has_permission = check_player_permissions(player)
|
||||
local add_new_task_element = container.header.alignment[add_new_task.name]
|
||||
add_new_task_element.visible = has_permission
|
||||
local isCreating = PlayerIsCreating:get(player)
|
||||
if isCreating and not has_permission then
|
||||
PlayerIsCreating:set(player, false)
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(Roles.events.on_role_assigned, role_update_event)
|
||||
Event.add(Roles.events.on_role_unassigned, role_update_event)
|
||||
|
||||
--- Redraw all tasks and clear editing/creating after joining or changing force
|
||||
local function reset_task_list(event)
|
||||
-- Repopulate the task list
|
||||
local player = game.players[event.player_index]
|
||||
local frame = Gui.get_left_element(player, task_list_container)
|
||||
local task_list_element = frame.container.scroll.task_list
|
||||
repopulate_task_list(task_list_element)
|
||||
|
||||
-- Check if the selected task is still valid
|
||||
local selected = PlayerSelected:get(player)
|
||||
if selected and Tasks.get_task(selected) ~= nil then
|
||||
PlayerIsCreating:set(player, false)
|
||||
PlayerIsEditing:set(player, false)
|
||||
PlayerSelected:set(player, nil)
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_player_joined_game, reset_task_list)
|
||||
Event.add(defines.events.on_player_changed_force, reset_task_list)
|
||||
523
exp_legacy/module/modules/gui/toolbar.lua
Normal file
523
exp_legacy/module/modules/gui/toolbar.lua
Normal file
@@ -0,0 +1,523 @@
|
||||
local Gui = require "expcore.gui" --- @dep expcore.gui
|
||||
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
|
||||
|
||||
-- Used to store the state of the toolbar when a player leaves
|
||||
local ToolbarState = PlayerData.Settings:combine('ToolbarState')
|
||||
ToolbarState:set_metadata{
|
||||
stringify = function(value)
|
||||
local buttons, favourites = 0, 0
|
||||
for _, state in ipairs(value) do
|
||||
buttons = buttons + 1
|
||||
if state.favourite then
|
||||
favourites = favourites + 1
|
||||
end
|
||||
end
|
||||
return string.format("Buttons: %d, Favourites: %d", buttons, favourites)
|
||||
end
|
||||
}
|
||||
|
||||
-- Styles used for sprite buttons
|
||||
local button_size = 20
|
||||
local Styles = {
|
||||
header = Gui.sprite_style(22),
|
||||
item = Gui.sprite_style(button_size)
|
||||
}
|
||||
|
||||
--- Set the style of the fake toolbar element
|
||||
local function copy_style(src, dst)
|
||||
dst.style = src.style.name
|
||||
dst.style.height = button_size
|
||||
dst.style.width = button_size
|
||||
dst.style.padding = -2
|
||||
end
|
||||
|
||||
local toolbar_container, move_up, move_down, toggle_toolbar
|
||||
|
||||
--- Reorder the buttons relative to each other, this will update the datastore
|
||||
local function move_toolbar_button(player, item, offset)
|
||||
local old_index = item.get_index_in_parent()
|
||||
local new_index = old_index + offset
|
||||
|
||||
-- Ideally the following would all happen in on_update but this had too much latency
|
||||
-- Swap the position in the list
|
||||
local list = item.parent
|
||||
local other_item = list.children[new_index]
|
||||
list.swap_children(old_index, new_index)
|
||||
|
||||
-- Swap the position in the top flow, offset by 1 because of settings button
|
||||
local top_flow = Gui.get_top_flow(player)
|
||||
top_flow.swap_children(old_index+1, new_index+1)
|
||||
|
||||
-- Check if the element has a left element to move
|
||||
local element_define = Gui.defines[item.tags.top_element_uid]
|
||||
local other_define = Gui.defines[other_item.tags.top_element_uid]
|
||||
if element_define.left_flow_element and other_define.left_flow_element then
|
||||
local left_element = Gui.get_left_element(player, element_define.left_flow_element)
|
||||
local other_left_element = Gui.get_left_element(player, other_define.left_flow_element)
|
||||
local left_index = left_element.get_index_in_parent()
|
||||
local other_index = other_left_element.get_index_in_parent()
|
||||
left_element.parent.swap_children(left_index, other_index)
|
||||
end
|
||||
|
||||
-- If we are moving in/out of first/last place we need to update the move buttons
|
||||
local last_index = #list.children
|
||||
if old_index == 1 then -- Moving out of index 1
|
||||
other_item.move[move_up.name].enabled = false
|
||||
item.move[move_up.name].enabled = true
|
||||
elseif new_index == 1 then -- Moving into index 1
|
||||
other_item.move[move_up.name].enabled = true
|
||||
item.move[move_up.name].enabled = false
|
||||
elseif old_index == last_index then -- Moving out of the last index
|
||||
other_item.move[move_down.name].enabled = false
|
||||
item.move[move_down.name].enabled = true
|
||||
elseif new_index == last_index then -- Moving into the last index
|
||||
other_item.move[move_down.name].enabled = true
|
||||
item.move[move_down.name].enabled = false
|
||||
end
|
||||
|
||||
-- Update the datastore state
|
||||
ToolbarState:update(player, function(_, order)
|
||||
local tmp = order[old_index]
|
||||
order[old_index] = order[new_index]
|
||||
order[new_index] = tmp
|
||||
end)
|
||||
end
|
||||
|
||||
--- Reorder the toolbar buttons
|
||||
local function reorder_toolbar_menu(player)
|
||||
local frame = Gui.get_left_element(player, toolbar_container)
|
||||
local list = frame.container.scroll.list
|
||||
local order = ToolbarState:get(player)
|
||||
local last_index = #order
|
||||
|
||||
-- Reorder the buttons
|
||||
for index, state in ipairs(order) do
|
||||
local element_define = Gui.defines[state.element_uid]
|
||||
|
||||
-- Switch item order
|
||||
local item = list[element_define.name]
|
||||
list.swap_children(index, item.get_index_in_parent())
|
||||
|
||||
-- Check if the player is allowed to see the button
|
||||
local allowed = element_define.authenticator
|
||||
if type(allowed) == 'function' then allowed = allowed(player) end
|
||||
|
||||
-- Update the checkbox state and item visibility
|
||||
local toolbar_button = Gui.get_top_element(player, element_define)
|
||||
toolbar_button.visible = allowed and state.favourite or false
|
||||
item.checkbox.state = state.favourite
|
||||
|
||||
-- Update the state if the move buttons
|
||||
item.move[move_up.name].enabled = index ~= 1
|
||||
item.move[move_down.name].enabled = index ~= last_index
|
||||
end
|
||||
|
||||
-- Update the state of the toggle button
|
||||
local button = frame.container.header.alignment[toggle_toolbar.name]
|
||||
button.enabled = Gui.top_flow_has_visible_elements(player)
|
||||
button.toggled = Gui.get_top_flow(player).parent.visible
|
||||
end
|
||||
|
||||
--- Resets the toolbar to its default state when pressed
|
||||
-- @element reset_toolbar
|
||||
local reset_toolbar =
|
||||
Gui.element {
|
||||
type = "sprite-button",
|
||||
sprite = "utility/reset",
|
||||
style = "shortcut_bar_button_red",
|
||||
tooltip = {"toolbar.reset"},
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style(Gui.sprite_style(Styles.header.width, -1))
|
||||
:on_click(function(player)
|
||||
ToolbarState:set(player, nil)
|
||||
Gui.toggle_top_flow(player, true)
|
||||
reorder_toolbar_menu(player)
|
||||
end)
|
||||
|
||||
--- Replaces the default method for opening and closing the toolbar
|
||||
-- @element toggle_toolbar
|
||||
toggle_toolbar =
|
||||
Gui.element {
|
||||
type = "sprite-button",
|
||||
sprite = "utility/bookmark",
|
||||
tooltip = {"toolbar.toggle"},
|
||||
style = "tool_button",
|
||||
auto_toggle = true,
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style(Styles.header)
|
||||
:on_click(function(player, element)
|
||||
Gui.toggle_top_flow(player, element.toggled)
|
||||
end)
|
||||
|
||||
--- Move an element up the list
|
||||
-- @element move_up
|
||||
move_up =
|
||||
Gui.element {
|
||||
type = "sprite-button",
|
||||
sprite = "utility/speed_up",
|
||||
tooltip = {"toolbar.move-up"},
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style(Styles.item)
|
||||
:on_click(function(player, element)
|
||||
local item = element.parent.parent
|
||||
move_toolbar_button(player, item, -1)
|
||||
end)
|
||||
|
||||
--- Move an element down the list
|
||||
-- @element move_down
|
||||
move_down =
|
||||
Gui.element {
|
||||
type = "sprite-button",
|
||||
sprite = "utility/speed_down",
|
||||
tooltip = {"toolbar.move-down"},
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
:style(Styles.item)
|
||||
:on_click(function(player, element)
|
||||
local item = element.parent.parent
|
||||
move_toolbar_button(player, item, 1)
|
||||
end)
|
||||
|
||||
--- A flow which represents one item in the toolbar list
|
||||
-- @element toolbar_list_item
|
||||
local toolbar_list_item =
|
||||
Gui.element(function(definition, parent, element_define)
|
||||
local flow = parent.add {
|
||||
type = "frame",
|
||||
style = "shortcut_selection_row",
|
||||
name = element_define.name,
|
||||
tags = {
|
||||
top_element_uid = element_define.uid
|
||||
}
|
||||
}
|
||||
flow.style.horizontally_stretchable = true
|
||||
flow.style.vertical_align = "center"
|
||||
|
||||
-- Add the button and the icon edit button
|
||||
local element = element_define(flow)
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
local top_element = Gui.get_top_element(player, element_define)
|
||||
copy_style(top_element, element)
|
||||
|
||||
-- Add the checkbox that can toggle the visibility
|
||||
local checkbox = flow.add{
|
||||
type = "checkbox",
|
||||
name = "checkbox",
|
||||
caption = element_define.tooltip or element_define.caption or "None",
|
||||
state = top_element.visible or false,
|
||||
tags = {
|
||||
top_element_name = element_define.name
|
||||
}
|
||||
}
|
||||
definition:triggers_events(checkbox)
|
||||
checkbox.style.width = 180
|
||||
|
||||
-- Add the buttons used to move the flow up and down
|
||||
local move_flow = flow.add{ type = "flow", name = "move" }
|
||||
move_flow.style.horizontal_spacing = 0
|
||||
move_up(move_flow)
|
||||
move_down(move_flow)
|
||||
|
||||
return definition:no_events(flow)
|
||||
end)
|
||||
:on_checked_changed(function(player, element)
|
||||
local top_flow = Gui.get_top_flow(player)
|
||||
local top_element = top_flow[element.tags.top_element_name]
|
||||
local had_visible = Gui.top_flow_has_visible_elements(player)
|
||||
top_element.visible = element.state
|
||||
|
||||
-- Check if we are on the edge case between 0 and 1 visible elements
|
||||
if element.state and not had_visible then
|
||||
Gui.toggle_top_flow(player, true)
|
||||
local container = element.parent.parent.parent.parent
|
||||
local button = container.header.alignment[toggle_toolbar.name]
|
||||
button.toggled = true
|
||||
button.enabled = true
|
||||
elseif not element.state and not Gui.top_flow_has_visible_elements(player) then
|
||||
Gui.toggle_top_flow(player, false)
|
||||
local container = element.parent.parent.parent.parent
|
||||
local button = container.header.alignment[toggle_toolbar.name]
|
||||
button.toggled = false
|
||||
button.enabled = false
|
||||
end
|
||||
|
||||
-- Update the datastore state
|
||||
ToolbarState:update(player, function(_, order)
|
||||
local index = element.parent.get_index_in_parent()
|
||||
order[index].favourite = element.state
|
||||
end)
|
||||
end)
|
||||
|
||||
--- Scrollable list of all toolbar buttons
|
||||
-- @element toolbar_list
|
||||
local toolbar_list =
|
||||
Gui.element(function(_, parent)
|
||||
-- This is a scroll pane for the list
|
||||
local scroll_pane = parent.add {
|
||||
name = "scroll",
|
||||
type = "scroll-pane",
|
||||
direction = "vertical",
|
||||
horizontal_scroll_policy = "never",
|
||||
vertical_scroll_policy = "auto",
|
||||
style = "scroll_pane_under_subheader"
|
||||
}
|
||||
scroll_pane.style.horizontally_stretchable = true
|
||||
scroll_pane.style.padding = 0
|
||||
scroll_pane.style.maximal_height = 224
|
||||
|
||||
-- This flow is the list, we need a linear list because of get_index_in_parent
|
||||
local flow = scroll_pane.add {
|
||||
name = "list",
|
||||
type = "flow",
|
||||
direction = "vertical"
|
||||
}
|
||||
flow.style.vertical_spacing = 0
|
||||
flow.style.horizontally_stretchable = true
|
||||
|
||||
return flow
|
||||
end)
|
||||
|
||||
--- Main toolbar container for the left flow
|
||||
-- @element toolbar_container
|
||||
toolbar_container =
|
||||
Gui.element(function(definition, parent)
|
||||
-- Draw the internal container
|
||||
local container = Gui.container(parent, definition.name, 268)
|
||||
container.style.maximal_width = 268
|
||||
container.style.minimal_width = 268
|
||||
|
||||
-- Draw the header
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
local header = Gui.header(container, {"toolbar.main-caption"}, {"toolbar.main-tooltip"}, true)
|
||||
|
||||
-- Draw the toolbar control buttons
|
||||
local toggle_element = toggle_toolbar(header)
|
||||
toggle_element.toggled = Gui.get_top_flow(player).visible
|
||||
reset_toolbar(header)
|
||||
|
||||
-- Draw toolbar list element
|
||||
local list_element = toolbar_list(container)
|
||||
local flow_order = Gui.get_top_flow_order(player)
|
||||
|
||||
for _, element_define in ipairs(flow_order) do
|
||||
-- Ensure the element exists
|
||||
local element = list_element[element_define.name]
|
||||
if not element then
|
||||
element = toolbar_list_item(list_element, element_define)
|
||||
end
|
||||
|
||||
-- Set the visible state
|
||||
local allowed = element_define.authenticator
|
||||
if type(allowed) == 'function' then allowed = allowed(player) end
|
||||
element.visible = allowed or false
|
||||
end
|
||||
|
||||
-- Set the state of the move buttons for the first and last element
|
||||
local children = list_element.children
|
||||
children[1].move[move_up.name].enabled = false
|
||||
children[#children].move[move_down.name].enabled = false
|
||||
|
||||
-- Return the external container
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow(false)
|
||||
|
||||
--- Set the default value for the datastore
|
||||
local datastore_id_map = {}
|
||||
local toolbar_default_state = {}
|
||||
ToolbarState:set_default(toolbar_default_state)
|
||||
|
||||
--- Get the datastore id for this element define, to best of ability it should be unique between versions
|
||||
local function to_datastore_id(element_define)
|
||||
-- First try to use the tooltip locale string
|
||||
local tooltip = element_define.tooltip
|
||||
if type(tooltip) == "table" then
|
||||
return tooltip[1]:gsub("%.(.+)", "")
|
||||
end
|
||||
|
||||
-- Then try to use the caption or sprite
|
||||
return element_define.caption or element_define.sprite
|
||||
end
|
||||
|
||||
--- For all top element, register an on click which will copy their style
|
||||
for index, element_define in ipairs(Gui.top_elements) do
|
||||
-- This is a bit hacky, the gui system cant have multiple handlers registered
|
||||
local prev_handler = element_define[Gui.events.on_toolbar_button_toggled]
|
||||
|
||||
-- Add the handler for when the button is toggled
|
||||
element_define:on_event(Gui.events.on_toolbar_button_toggled, function(player, element, event)
|
||||
if prev_handler then prev_handler(player, element, event) end -- Kind of hacky but works
|
||||
local frame = Gui.get_left_element(player, toolbar_container)
|
||||
if not frame then return end -- Gui might not be loaded yet
|
||||
local button = frame.container.scroll.list[element_define.name][element_define.name]
|
||||
local toolbar_button = Gui.get_top_element(player, element_define)
|
||||
copy_style(toolbar_button, button)
|
||||
end)
|
||||
|
||||
-- Insert the element into the id map
|
||||
local id = to_datastore_id(element_define)
|
||||
if datastore_id_map[id] then
|
||||
error(string.format("All toolbar elements need a unique id to be saved correctly, %d (%s) and %d (%s) share the id %s",
|
||||
datastore_id_map[id].uid, datastore_id_map[id].defined_at, element_define.uid, element_define.defined_at, id
|
||||
))
|
||||
end
|
||||
datastore_id_map[id] = element_define
|
||||
|
||||
-- Add the element to the default state
|
||||
table.insert(toolbar_default_state, {
|
||||
element_uid = element_define.uid,
|
||||
favourite = true,
|
||||
})
|
||||
end
|
||||
|
||||
--- Get the top order based on the players settings
|
||||
Gui.inject_top_flow_order(function(player)
|
||||
local order = ToolbarState:get(player)
|
||||
|
||||
local elements = {}
|
||||
for index, state in ipairs(order) do
|
||||
elements[index] = Gui.defines[state.element_uid]
|
||||
end
|
||||
|
||||
return elements
|
||||
end)
|
||||
|
||||
--- Get the left order based on the player settings, with toolbar menu first, and all remaining after
|
||||
Gui.inject_left_flow_order(function(player)
|
||||
local order = Gui.get_top_flow_order(player)
|
||||
local elements, element_map = { toolbar_container }, { [toolbar_container] = true }
|
||||
|
||||
-- Add the flows that have a top element
|
||||
for _, element_define in ipairs(order) do
|
||||
if element_define.left_flow_element then
|
||||
table.insert(elements, element_define.left_flow_element)
|
||||
element_map[element_define.left_flow_element] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Add the flows that dont have a top element
|
||||
for _, element_define in ipairs(Gui.left_elements) do
|
||||
if not element_map[element_define] then
|
||||
table.insert(elements, element_define)
|
||||
end
|
||||
end
|
||||
|
||||
return elements
|
||||
end)
|
||||
|
||||
--- Overwrite the default toggle behaviour and instead toggle this menu
|
||||
Gui.core_defines.hide_top_flow:on_click(function(player, _, _)
|
||||
Gui.toggle_left_element(player, toolbar_container)
|
||||
end)
|
||||
|
||||
--- Overwrite the default toggle behaviour and instead toggle this menu
|
||||
Gui.core_defines.show_top_flow:on_click(function(player, _, _)
|
||||
Gui.toggle_left_element(player, toolbar_container)
|
||||
end)
|
||||
|
||||
--- Overwrite the default update top flow
|
||||
local _update_top_flow = Gui.update_top_flow
|
||||
function Gui.update_top_flow(player)
|
||||
_update_top_flow(player) -- Call the original
|
||||
|
||||
local order = ToolbarState:get(player)
|
||||
for index, state in ipairs(order) do
|
||||
local element_define = Gui.defines[state.element_uid]
|
||||
local top_element = Gui.get_top_element(player, element_define)
|
||||
top_element.visible = top_element.visible and state.favourite or false
|
||||
end
|
||||
end
|
||||
|
||||
--- Uncompress the data to be more useable
|
||||
ToolbarState:on_load(function(player_name, value)
|
||||
-- If there is no value, do nothing
|
||||
if value == nil then return end
|
||||
|
||||
-- Create a hash map of the favourites
|
||||
local favourites = {}
|
||||
for _, id in ipairs(value[2]) do
|
||||
favourites[id] = true
|
||||
end
|
||||
|
||||
-- Read the order from the value
|
||||
local elements = {}
|
||||
local element_hash = {}
|
||||
for index, id in ipairs(value[1]) do
|
||||
local element = datastore_id_map[id]
|
||||
if element and not element_hash[element.uid] then
|
||||
element_hash[element.uid] = true
|
||||
elements[index] = {
|
||||
element_uid = element.uid,
|
||||
favourite = favourites[id] or false,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Add any in the default state that are missing
|
||||
for _, state in ipairs(toolbar_default_state) do
|
||||
if not element_hash[state.element_uid] then
|
||||
table.insert(elements, table.deep_copy(state))
|
||||
end
|
||||
end
|
||||
|
||||
-- Create a hash map of the open left flows
|
||||
local left_flows = {}
|
||||
for _, id in ipairs(value[3]) do
|
||||
local element = datastore_id_map[id]
|
||||
if element.left_flow_element then
|
||||
left_flows[element.left_flow_element] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Set the visible state of all left flows
|
||||
local player = game.get_player(player_name)
|
||||
for _, left_element in ipairs(Gui.left_elements) do
|
||||
Gui.toggle_left_element(player, left_element, left_flows[left_element] or false)
|
||||
end
|
||||
|
||||
-- Set the toolbar visible state
|
||||
Gui.toggle_top_flow(player, value[4])
|
||||
|
||||
-- Set the data now and update now, ideally this would be on_update but that had too large of a latency
|
||||
ToolbarState:raw_set(player_name, elements)
|
||||
Gui.reorder_top_flow(player)
|
||||
Gui.reorder_left_flow(player)
|
||||
reorder_toolbar_menu(player)
|
||||
|
||||
return elements
|
||||
end)
|
||||
|
||||
--- Save the current state of the players toolbar menu
|
||||
ToolbarState:on_save(function(player_name, value)
|
||||
if value == nil then return nil end -- Don't save default
|
||||
local order, favourites, left_flows = {}, {}, {}
|
||||
|
||||
local player = game.get_player(player_name)
|
||||
local top_flow_open = Gui.get_top_flow(player).parent.visible
|
||||
|
||||
for index, state in ipairs(value) do
|
||||
-- Add the element to the order array
|
||||
local element_define = Gui.defines[state.element_uid]
|
||||
local id = to_datastore_id(element_define)
|
||||
order[index] = id
|
||||
|
||||
-- If its a favourite then insert it
|
||||
if state.favourite then
|
||||
table.insert(favourites, id)
|
||||
end
|
||||
|
||||
-- If it has a left flow and its open then insert it
|
||||
if element_define.left_flow_element then
|
||||
local left_element = Gui.get_left_element(player, element_define.left_flow_element)
|
||||
if left_element.visible then
|
||||
table.insert(left_flows, id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return { order, favourites, left_flows, top_flow_open }
|
||||
end)
|
||||
528
exp_legacy/module/modules/gui/vlayer.lua
Normal file
528
exp_legacy/module/modules/gui/vlayer.lua
Normal file
@@ -0,0 +1,528 @@
|
||||
--[[-- 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 Selection = require 'modules.control.selection' --- @dep modules.control.selection
|
||||
local SelectionConvertArea = 'VlayerConvertChest'
|
||||
|
||||
--- 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)
|
||||
return '[gps=' .. string.format('%.1f', pos.x) .. ',' .. string.format('%.1f', pos.y) .. ']'
|
||||
end
|
||||
|
||||
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 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
|
||||
|
||||
--- When an area is selected to add protection to the area
|
||||
Selection.on_selection(SelectionConvertArea, function(event)
|
||||
local area = aabb_align_expand(event.area)
|
||||
local player = game.get_player(event.player_index)
|
||||
|
||||
if not player then
|
||||
return nil
|
||||
end
|
||||
|
||||
local entities = player.surface.find_entities_filtered{area=area, name='steel-chest', force=player.force}
|
||||
local frame = Gui.get_left_element(player, vlayer_container)
|
||||
local disp = frame.container['vlayer_st_2'].disp.table
|
||||
local target = vlayer_control_type_list[disp[vlayer_gui_control_type.name].selected_index]
|
||||
|
||||
if #entities == 0 then
|
||||
player.print{'vlayer.steel-chest-detect'}
|
||||
return nil
|
||||
|
||||
elseif #entities > 1 then
|
||||
player.print{'vlayer.result-unable', {'vlayer.control-type-' .. target:gsub('_', '-')}, {'vlayer.result-multiple'}}
|
||||
return nil
|
||||
end
|
||||
|
||||
if not entities[1] then
|
||||
return nil
|
||||
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 = e.circuit_connected_entities
|
||||
|
||||
if not e.get_inventory(defines.inventory.chest).is_empty() then
|
||||
player.print{'vlayer.steel-chest-empty'}
|
||||
return nil
|
||||
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 nil
|
||||
end
|
||||
|
||||
e.destroy()
|
||||
|
||||
if target == 'energy' then
|
||||
if not vlayer.create_energy_interface(player.surface, e_pos, player) then
|
||||
player.print{'vlayer.result-unable', {'vlayer.control-type-energy'}, {'vlayer.result-space'}}
|
||||
return nil
|
||||
end
|
||||
|
||||
elseif target == 'circuit' then
|
||||
vlayer.create_circuit_interface(player.surface, e_pos, e_circ, player)
|
||||
|
||||
elseif target == 'storage_input' then
|
||||
vlayer.create_input_interface(player.surface, e_pos, e_circ, player)
|
||||
|
||||
elseif target == 'storage_output' then
|
||||
vlayer.create_output_interface(player.surface, e_pos, e_circ, player)
|
||||
end
|
||||
|
||||
game.print{'vlayer.interface-result', player.name, pos_to_gps_string(e_pos), {'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.element{
|
||||
type = 'label',
|
||||
name = 'vlayer_display_item_solar_name',
|
||||
caption = {'vlayer.display-item-solar'},
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width = 200
|
||||
}
|
||||
|
||||
local vlayer_gui_display_item_solar_count =
|
||||
Gui.element{
|
||||
type = 'progressbar',
|
||||
name = 'vlayer_display_item_solar_count',
|
||||
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.element{
|
||||
type = 'label',
|
||||
name = 'vlayer_display_item_accumulator_name',
|
||||
caption = {'vlayer.display-item-accumulator'},
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width = 200
|
||||
}
|
||||
|
||||
local vlayer_gui_display_item_accumulator_count =
|
||||
Gui.element{
|
||||
type = 'progressbar',
|
||||
name = 'vlayer_display_item_accumulator_count',
|
||||
caption = '',
|
||||
value = 0,
|
||||
style = 'electric_satisfaction_statistics_progressbar'
|
||||
}:style{
|
||||
width = 200,
|
||||
font = 'heading-2'
|
||||
}
|
||||
|
||||
--- Display label for the remaining surface area
|
||||
-- @element vlayer_gui_display_signal_remaining_surface_area_name
|
||||
local vlayer_gui_display_signal_remaining_surface_area_name =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
name = 'vlayer_display_signal_remaining_surface_area_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_remaining_surface_area_count =
|
||||
Gui.element{
|
||||
type = 'label',
|
||||
name = 'vlayer_display_signal_remaining_surface_area_count',
|
||||
caption = '0',
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width = 200,
|
||||
height = 28,
|
||||
horizontal_align = 'right'
|
||||
}
|
||||
|
||||
--- 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 = {'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.element{
|
||||
type = 'label',
|
||||
name = 'vlayer_display_signal_sustained_count',
|
||||
caption = '0',
|
||||
style = 'heading_2_label'
|
||||
}:style{
|
||||
width = 200,
|
||||
height = 28,
|
||||
horizontal_align = 'right'
|
||||
}
|
||||
|
||||
--- 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_production_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.element{
|
||||
type = 'progressbar',
|
||||
name = 'vlayer_display_signal_production_count',
|
||||
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.element{
|
||||
type = 'label',
|
||||
name = 'vlayer_display_signal_capacity_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.element{
|
||||
type = 'progressbar',
|
||||
name = 'vlayer_display_signal_capacity_count',
|
||||
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.element(function(_, parent, name)
|
||||
local vlayer_set = parent.add{type='flow', direction='vertical', name=name}
|
||||
local disp = Gui.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_remaining_surface_area_name(disp)
|
||||
vlayer_gui_display_signal_remaining_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 frame = Gui.get_left_element(player, vlayer_container)
|
||||
local disp = frame.container['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.element{
|
||||
type = 'drop-down',
|
||||
name = Gui.unique_static_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_changed(function(player, _, _)
|
||||
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.element{
|
||||
type = 'drop-down',
|
||||
name = Gui.unique_static_name
|
||||
}:style{
|
||||
width = 200
|
||||
}
|
||||
|
||||
--- A button to refresh the remove list
|
||||
-- @element vlayer_gui_control_refresh
|
||||
local vlayer_gui_control_refresh =
|
||||
Gui.element{
|
||||
type = 'button',
|
||||
name = Gui.unique_static_name,
|
||||
caption = {'vlayer.control-refresh'}
|
||||
}:style{
|
||||
width = 200
|
||||
}:on_click(function(player, _, _)
|
||||
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.element{
|
||||
type = 'button',
|
||||
name = Gui.unique_static_name,
|
||||
caption = {'vlayer.control-see'}
|
||||
}:style{
|
||||
width = 200
|
||||
}:on_click(function(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]] and i[vlayer_control_type_list[target]][n] then
|
||||
local pos = i[vlayer_control_type_list[target]][n].position
|
||||
|
||||
if pos then
|
||||
player.zoom_to_world(pos, 2)
|
||||
player.print{'vlayer.result-interface-location', {'vlayer.control-type-' .. vlayer_control_type_list[target]:gsub('_', '-')}, pos_to_gps_string(pos)}
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--- A button used to build the vlayer interface
|
||||
-- @element vlayer_gui_control_build
|
||||
local vlayer_gui_control_build =
|
||||
Gui.element{
|
||||
type = 'button',
|
||||
name = Gui.unique_static_name,
|
||||
caption = {'vlayer.control-build'}
|
||||
}:style{
|
||||
width = 200
|
||||
}:on_click(function(player, _, _)
|
||||
if Selection.is_selecting(player, SelectionConvertArea) then
|
||||
Selection.stop(player)
|
||||
else
|
||||
Selection.start(player, SelectionConvertArea)
|
||||
player.print{'expcom-waterfill.entered-area-selection'}
|
||||
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.element{
|
||||
type = 'button',
|
||||
name = Gui.unique_static_name,
|
||||
caption = {'vlayer.control-remove'}
|
||||
}:style{
|
||||
width = 200
|
||||
}:on_click(function(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_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), {'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.element(function(_, parent, name)
|
||||
local vlayer_set = parent.add{type='flow', direction='vertical', name=name}
|
||||
local disp = Gui.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)
|
||||
vlayer_gui_control_build(disp)
|
||||
vlayer_gui_control_remove(disp)
|
||||
|
||||
return vlayer_set
|
||||
end)
|
||||
|
||||
--- The main container for the vlayer gui
|
||||
-- @element vlayer_container
|
||||
vlayer_container =
|
||||
Gui.element(function(definition, parent)
|
||||
local player = Gui.get_player_from_element(parent)
|
||||
local container = Gui.container(parent, definition.name, 400)
|
||||
|
||||
vlayer_display_set(container, 'vlayer_st_1')
|
||||
local control_set = vlayer_control_set(container, 'vlayer_st_2')
|
||||
control_set.visible = Roles.player_allowed(player, 'gui/vlayer-edit')
|
||||
|
||||
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 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']) .. ' / ' .. format_number(items['solar-panel'])
|
||||
},
|
||||
[vlayer_gui_display_item_accumulator_count.name] = {
|
||||
val = (items_alloc['accumulator'] / math.max(items['accumulator'], 1)),
|
||||
cap = format_number(items_alloc['accumulator']) .. ' / ' .. format_number(items['accumulator'])
|
||||
},
|
||||
[vlayer_gui_display_signal_remaining_surface_area_count.name] = {
|
||||
cap = format_number(stats.remaining_surface_area)
|
||||
},
|
||||
[vlayer_gui_display_signal_sustained_count.name] = {
|
||||
cap = format_energy(stats.energy_sustained, '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 frame = Gui.get_left_element(player, vlayer_container)
|
||||
local disp = frame.container['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)
|
||||
902
exp_legacy/module/modules/gui/warp-list.lua
Normal file
902
exp_legacy/module/modules/gui/warp-list.lua
Normal file
@@ -0,0 +1,902 @@
|
||||
--[[-- Gui Module - Warp List
|
||||
- Adds a warp list gui which allows players to add and remove warp points
|
||||
@gui Warps-List
|
||||
@alias warp_list
|
||||
]]
|
||||
|
||||
local Gui = require 'expcore.gui' --- @dep expcore.gui
|
||||
local Datastore = require 'expcore.datastore' --- @dep expcore.datastore
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
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, player_return = _C.format_time, _C.player_return --- @dep expcore.common
|
||||
|
||||
--- Stores all data for the warp gui
|
||||
local WrapGuiData = Datastore.connect('WrapGuiData')
|
||||
WrapGuiData:set_serializer(Datastore.name_serializer)
|
||||
local PlayerInRange = WrapGuiData:combine('PlayerInRange')
|
||||
PlayerInRange:set_default(false)
|
||||
local PlayerCooldown = WrapGuiData:combine('PlayerCooldown')
|
||||
PlayerCooldown:set_default(0)
|
||||
|
||||
--- Table that stores a boolean value of weather to keep the warp gui open
|
||||
local keep_gui_open = {}
|
||||
Global.register(keep_gui_open, function(tbl)
|
||||
keep_gui_open = tbl
|
||||
end)
|
||||
|
||||
--- Styles used for sprite buttons
|
||||
local Styles = {
|
||||
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
|
||||
local function check_player_permissions(player, action, warp)
|
||||
-- Check if the action is allow edit and then check bypass settings
|
||||
if action == 'allow_edit_warp' then
|
||||
-- Check if the warp is the spawn then it cant be edited
|
||||
local spawn_id = Warps.get_spawn_warp_id(player.force.name)
|
||||
if spawn_id == warp.warp_id then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Check if the player being the last to edit will override existing permisisons
|
||||
if config.user_can_edit_own_warps and warp.last_edit_name == player.name then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- Check player has permission based on value in the config
|
||||
local action_config = config[action]
|
||||
if action_config == 'all' then
|
||||
return true
|
||||
elseif action_config == 'admin' then
|
||||
return player.admin
|
||||
elseif action_config == 'expcore.roles' then
|
||||
return Roles.player_allowed(player, config['expcore_roles_'..action])
|
||||
end
|
||||
|
||||
-- Return false as all other conditions have not been met
|
||||
return false
|
||||
end
|
||||
|
||||
--- Will add a new warp to the list, checks if the player is too close to an existing one
|
||||
-- @element add_new_warp
|
||||
local add_new_warp =
|
||||
Gui.element{
|
||||
type = 'sprite-button',
|
||||
sprite = 'utility/add',
|
||||
tooltip = {'warp-list.add-tooltip'},
|
||||
style = 'shortcut_bar_button',
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
: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 + 2.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 + 2.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)
|
||||
|
||||
|
||||
--- Warp icon button, this will trigger a warp when the player is able to
|
||||
-- @element warp_icon_button
|
||||
local warp_icon_button =
|
||||
Gui.element(function(definition, parent, warp)
|
||||
local warp_position = warp.position
|
||||
|
||||
-- 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
|
||||
end
|
||||
|
||||
-- Draw the element
|
||||
return parent.add{
|
||||
type = 'sprite-button',
|
||||
sprite = sprite,
|
||||
name = definition.name,
|
||||
tooltip = {'warp-list.goto-tooltip', warp_position.x, warp_position.y},
|
||||
style = 'slot_button'
|
||||
}
|
||||
end)
|
||||
:style(Styles.sprite32)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:on_click(function(player, element, _)
|
||||
if element.type == 'choose-elem-button' then return end
|
||||
local warp_id = element.parent.caption
|
||||
Warps.teleport_player(warp_id, player)
|
||||
|
||||
-- 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.update_smoothing*config.cooldown_duration)
|
||||
end
|
||||
|
||||
PlayerInRange:set(player, warp_id)
|
||||
end)
|
||||
|
||||
--- The button that is visible when the warp is in edit state
|
||||
-- @element warp_icon_editing
|
||||
local warp_icon_editing =
|
||||
Gui.element(function(definition, parent, warp)
|
||||
return parent.add{
|
||||
name = definition.name,
|
||||
type = 'choose-elem-button',
|
||||
elem_type = 'signal',
|
||||
signal = {type = warp.icon.type, name = warp.icon.name},
|
||||
tooltip = {'warp-list.goto-edit'}
|
||||
}
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:style(Styles.sprite32)
|
||||
|
||||
--- Warp label, visible if the player is not in edit state
|
||||
-- @element warp_label
|
||||
local warp_label =
|
||||
Gui.element(function(definition, parent, warp)
|
||||
local last_edit_name = warp.last_edit_name
|
||||
local last_edit_time = warp.last_edit_time
|
||||
-- Draw the element
|
||||
return parent.add{
|
||||
type = 'label',
|
||||
caption = warp.name,
|
||||
tooltip = {'warp-list.last-edit', last_edit_name, format_time(last_edit_time)},
|
||||
name = definition.name
|
||||
}
|
||||
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)
|
||||
:static_name(Gui.unique_static_name)
|
||||
|
||||
--- 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{
|
||||
type = 'label',
|
||||
caption = '[img=utility/electricity_icon_unplugged]', -- Temporary icon
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
: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(definition, parent, warp)
|
||||
-- Draw the element
|
||||
return parent.add{
|
||||
type = 'textfield',
|
||||
text = warp.name,
|
||||
clear_and_focus_on_right_click = true,
|
||||
name = definition.name
|
||||
}
|
||||
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)
|
||||
:static_name(Gui.unique_static_name)
|
||||
|
||||
|
||||
--- 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',
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
: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',
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
: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',
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
: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',
|
||||
name = Gui.unique_static_name
|
||||
}
|
||||
: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',
|
||||
name = Gui.unique_static_name,
|
||||
tooltip = {'warp-list.timer-tooltip-zero', config.cooldown_duration},
|
||||
minimum_value = 0,
|
||||
maximum_value = config.cooldown_duration*config.update_smoothing
|
||||
}
|
||||
:style{
|
||||
horizontally_stretchable = true,
|
||||
color = Colors.light_blue
|
||||
}
|
||||
|
||||
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_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 player is currenty on cooldown
|
||||
timer = timer or PlayerCooldown:get(player)
|
||||
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 _, 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
|
||||
|
||||
--- Updates a warp for a player
|
||||
local function update_warp(player, warp_table, warp_id)
|
||||
local warp = Warps.get_warp(warp_id)
|
||||
|
||||
-- If the warp no longer exists then remove the warp elements from the warp table
|
||||
if not warp then
|
||||
remove_warp_elements(warp_table, warp_id)
|
||||
return
|
||||
end
|
||||
|
||||
-- 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]
|
||||
|
||||
-- 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)
|
||||
edit_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, ', ')}
|
||||
else
|
||||
edit_warp_element.hovered_sprite = edit_warp_element.sprite
|
||||
edit_warp_element.tooltip = {'warp-list.edit-tooltip-none'}
|
||||
end
|
||||
|
||||
-- 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]
|
||||
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
|
||||
end
|
||||
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
|
||||
local function update_all_warps(player, warp_table)
|
||||
local warp_ids = Warps.get_force_warp_ids(player.force.name)
|
||||
warp_table.clear()
|
||||
for _, warp_id in ipairs(warp_ids) do
|
||||
update_warp(player, warp_table, warp_id)
|
||||
end
|
||||
end
|
||||
|
||||
-- Update all warps for all players on a 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() -- Needed to re-sort the warps
|
||||
for _, warp_id in ipairs(warp_ids) do
|
||||
update_warp(player, warp_table, warp_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Main warp list container for the left flow
|
||||
-- @element warp_list_container
|
||||
warp_list_container =
|
||||
Gui.element(function(definition, 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, definition.name, 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-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 add_new_warp_element = add_new_warp(header)
|
||||
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
|
||||
scroll_table_style.top_cell_padding = 3
|
||||
scroll_table_style.bottom_cell_padding = 3
|
||||
|
||||
-- Draw the warp cooldown progress bar
|
||||
local warp_timer_element = warp_timer(container)
|
||||
|
||||
-- Change the progress of the warp timer
|
||||
local timer = PlayerCooldown:get(player)
|
||||
if timer > 0 then
|
||||
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
|
||||
|
||||
-- Add any existing warps
|
||||
update_all_warps(player, scroll_table)
|
||||
|
||||
-- Return the external container
|
||||
return container.parent
|
||||
end)
|
||||
:static_name(Gui.unique_static_name)
|
||||
:add_to_left_flow()
|
||||
|
||||
--- Button on the top flow used to toggle the warp list container
|
||||
-- @element toggle_warp_list
|
||||
Gui.left_toolbar_button(config.default_icon.type ..'/'..config.default_icon.name, {'warp-list.main-tooltip'}, warp_list_container, function(player)
|
||||
return Roles.player_allowed(player, 'gui/warp-list')
|
||||
end)
|
||||
:on_event(Gui.events.on_visibility_changed_by_click, function(player, _,event)
|
||||
-- Set gui keep open state for player that clicked the button: true if visible, false if invisible
|
||||
keep_gui_open[player.name] = event.state
|
||||
end)
|
||||
|
||||
--- When the name of a warp is updated this is triggered
|
||||
Warps.on_update(function(_, warp, old_warp)
|
||||
-- Get the force to update, warp is nil when removed
|
||||
if warp then
|
||||
update_all_warp_force(game.forces[warp.force_name])
|
||||
else
|
||||
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, 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, warp_id ~= nil)
|
||||
end
|
||||
|
||||
update_all_warp_elements(player, nil, warp_id)
|
||||
end)
|
||||
|
||||
--- Update the warp cooldown progress bars to match the current cooldown
|
||||
PlayerCooldown:on_update(function(player_name, player_cooldown)
|
||||
-- Get the progress bar element
|
||||
local player = game.players[player_name]
|
||||
local frame = Gui.get_left_element(player, warp_list_container)
|
||||
local warp_timer_element = frame.container[warp_timer.name]
|
||||
|
||||
-- Set the progress
|
||||
if player_cooldown and player_cooldown > 0 then
|
||||
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
|
||||
|
||||
-- Trigger update of buttons if cooldown is now 0
|
||||
if player_cooldown == 0 then
|
||||
update_all_warp_elements(player, player_cooldown, nil)
|
||||
end
|
||||
end)
|
||||
|
||||
--- Handles updating the timer and checking distance from a warp
|
||||
local r2 = config.standard_proximity_radius^2
|
||||
local rs2 = config.spawn_proximity_radius^2
|
||||
local mr2 = config.minimum_distance^2
|
||||
Event.on_nth_tick(math.floor(60/config.update_smoothing), function()
|
||||
PlayerCooldown:update_all(function(_, player_cooldown)
|
||||
if player_cooldown > 0 then return player_cooldown - 1 end
|
||||
end)
|
||||
|
||||
local force_warps = {}
|
||||
local warps = {}
|
||||
for _, player in pairs(game.connected_players) do
|
||||
local was_in_range = PlayerInRange:get(player)
|
||||
|
||||
-- Get the ids of all the warps on the players force
|
||||
local force_name = player.force.name
|
||||
local warp_ids = force_warps[force_name]
|
||||
if not warp_ids then
|
||||
warp_ids = Warps.get_force_warp_ids(force_name)
|
||||
force_warps[force_name] = warp_ids
|
||||
end
|
||||
|
||||
-- Check if the force has any warps
|
||||
local closest_warp = nil
|
||||
local closest_distance = nil
|
||||
if #warp_ids > 0 then
|
||||
local surface = player.surface
|
||||
local pos = player.position
|
||||
local px, py = pos.x, pos.y
|
||||
|
||||
-- Loop over each warp
|
||||
for _, warp_id in ipairs(warp_ids) do
|
||||
-- Check if warp id is cached
|
||||
local warp = warps[warp_id]
|
||||
if not warp then
|
||||
warp = Warps.get_warp(warp_id)
|
||||
warps[warp_id] = warp
|
||||
end
|
||||
|
||||
-- Check if the player is within range
|
||||
local warp_pos = warp.position
|
||||
if warp.surface == surface then
|
||||
local dx, dy = px-warp_pos.x, py-warp_pos.y
|
||||
local dist = (dx*dx)+(dy*dy)
|
||||
if closest_distance == nil or dist < closest_distance then
|
||||
closest_warp = warp
|
||||
closest_distance = dist
|
||||
if dist < r2 then break end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check the dist to the closest warp
|
||||
local in_range = closest_warp ~= nil and (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, nil)
|
||||
elseif not was_in_range and in_range then
|
||||
---@cast closest_warp -nil
|
||||
PlayerInRange:set(player, closest_warp.warp_id)
|
||||
end
|
||||
|
||||
-- Change the enabled state of the add warp button
|
||||
local frame = Gui.get_left_element(player, warp_list_container)
|
||||
local add_warp_element = frame.container.header.alignment[add_new_warp.name]
|
||||
local old_closest_warp_name = add_warp_element.tooltip[2] or closest_warp and closest_warp.name
|
||||
local was_able_to_make_warp = add_warp_element.enabled
|
||||
local can_make_warp = closest_distance == nil or closest_distance > mr2
|
||||
if can_make_warp and not was_able_to_make_warp then
|
||||
add_warp_element.enabled = true
|
||||
add_warp_element.tooltip = {'warp-list.add-tooltip'}
|
||||
elseif not can_make_warp and was_able_to_make_warp or closest_warp and (old_closest_warp_name ~= closest_warp.name) then
|
||||
---@cast closest_warp -nil
|
||||
add_warp_element.enabled = false
|
||||
add_warp_element.tooltip = {'warp-list.too-close', closest_warp.name}
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
--- When a player is created make sure that there is a spawn warp created
|
||||
Event.add(defines.events.on_player_created, function(event)
|
||||
-- If the force has no spawn then make a spawn warp
|
||||
local player = game.players[event.player_index]
|
||||
local force = player.force
|
||||
local spawn_id = Warps.get_spawn_warp_id(force.name)
|
||||
if not spawn_id then
|
||||
local spawn_position = force.get_spawn_position(player.surface)
|
||||
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)
|
||||
|
||||
--- Update the warps when the player joins
|
||||
Event.add(defines.events.on_player_joined_game, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
local frame = Gui.get_left_element(player, warp_list_container)
|
||||
local scroll_table = frame.container.scroll.table
|
||||
update_all_warps(player, scroll_table)
|
||||
end)
|
||||
|
||||
--- Makes sure the right buttons are present when roles change
|
||||
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 = allow_add_warp
|
||||
end
|
||||
|
||||
Event.add(Roles.events.on_role_assigned, role_update_event)
|
||||
Event.add(Roles.events.on_role_unassigned, role_update_event)
|
||||
|
||||
--- When a chart tag is removed or edited make sure it is not one that belongs to a warp
|
||||
local function maintain_tag(event)
|
||||
if not event.player_index then return end
|
||||
local tag = event.tag
|
||||
local force_name = event.force.name
|
||||
local warp_ids = Warps.get_force_warp_ids(force_name)
|
||||
for _, warp_id in pairs(warp_ids) do
|
||||
local warp = Warps.get_warp(warp_id)
|
||||
local warp_tag = warp.tag
|
||||
if not warp_tag or not warp_tag.valid or warp_tag == tag then
|
||||
if event.name == defines.events.on_chart_tag_removed then
|
||||
warp.tag = nil
|
||||
end
|
||||
Warps.make_warp_tag(warp_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_chart_tag_modified, maintain_tag)
|
||||
Event.add(defines.events.on_chart_tag_removed, maintain_tag)
|
||||
Reference in New Issue
Block a user