You've already forked ComfyFactorio
mirror of
https://github.com/ComfyFactory/ComfyFactorio.git
synced 2025-11-25 22:32:18 +02:00
715 lines
22 KiB
Lua
715 lines
22 KiB
Lua
local Event = require 'utils.event'
|
|
local Global = require 'utils.global'
|
|
local Gui = require 'utils.gui'
|
|
local Task = require 'utils.task_token'
|
|
local Config = require 'utils.gui.config'
|
|
local CustomEvents = require 'utils.created_events'
|
|
|
|
local this =
|
|
{
|
|
players = {},
|
|
storage = {},
|
|
activate_custom_buttons = false,
|
|
bottom_quickbar_button = {}
|
|
}
|
|
|
|
Global.register(
|
|
this,
|
|
function (t)
|
|
this = t
|
|
end
|
|
)
|
|
|
|
--- Events generated by the bottom frame module.
|
|
-- @table events
|
|
-- @field bottom_quickbar_respawn_raise The event triggered when the bottom quickbar is respawned or raised.
|
|
-- @field bottom_quickbar_location_changed The event triggered when the location of the bottom quickbar is changed.
|
|
local Public = {}
|
|
|
|
local set_location
|
|
local destroy_frame
|
|
local remove_player
|
|
local get_player_data
|
|
|
|
local main_frame_name = Gui.uid_name()
|
|
|
|
local sections =
|
|
{
|
|
[1] = 1,
|
|
[2] = 1,
|
|
[3] = 2,
|
|
[4] = 2,
|
|
[5] = 3,
|
|
[6] = 3,
|
|
[7] = 4,
|
|
[8] = 4,
|
|
[9] = 5,
|
|
[10] = 5,
|
|
[11] = 6,
|
|
[12] = 6
|
|
}
|
|
|
|
Config.register_scenario_module(
|
|
{
|
|
id = "bottom_frame",
|
|
admin_only = false,
|
|
gui_rows = Config.register_token(
|
|
function (player, frame)
|
|
local switch_state
|
|
|
|
local autostash = is_loaded('modules.autostash')
|
|
if autostash then
|
|
switch_state = 'right'
|
|
local bottom_frame = Public.get_player_data(player)
|
|
if bottom_frame and bottom_frame.top then
|
|
switch_state = 'left'
|
|
end
|
|
Config.add_switch(frame, switch_state, 'top_location', 'Position - top', 'Toggle to select if you want the bottom buttons at the top or the bottom.')
|
|
frame.add({ type = 'line' })
|
|
end
|
|
|
|
switch_state = 'right'
|
|
local bottom_frame = Public.get_player_data(player)
|
|
if bottom_frame and bottom_frame.bottom_state == 'bottom_left' then
|
|
switch_state = 'left'
|
|
end
|
|
Config.add_switch(frame, switch_state, 'bottom_location', 'Position - bottom', 'Toggle to select if you want the bottom button on the left side or the right side.')
|
|
|
|
frame.add({ type = 'line' })
|
|
|
|
switch_state = 'right'
|
|
if bottom_frame and bottom_frame.above then
|
|
switch_state = 'left'
|
|
end
|
|
Config.add_switch(frame, switch_state, 'middle_location', 'Position - middle', 'Toggle to select if you want the bottom button above the quickbar or the side of the quickbar.')
|
|
|
|
frame.add({ type = 'line' })
|
|
|
|
switch_state = 'right'
|
|
if bottom_frame and bottom_frame.portable then
|
|
switch_state = 'left'
|
|
end
|
|
Config.add_switch(frame, switch_state, 'portable_button', 'Position - portable', 'Toggle to select if you want the bottom button to be portable or not.')
|
|
frame.add({ type = 'line' })
|
|
end),
|
|
handlers =
|
|
{
|
|
['top_location'] = Config.register_token(
|
|
function (player, _)
|
|
local data = Public.get_player_data(player)
|
|
if data and data.state and not data.top then
|
|
Public.set_top(player, true)
|
|
else
|
|
Public.set_top(player, false)
|
|
end
|
|
end),
|
|
['bottom_location'] = Config.register_token(
|
|
function (player, event)
|
|
if event.element.switch_state == 'left' then
|
|
Public.set_location(player, 'bottom_left')
|
|
else
|
|
Public.set_location(player, 'bottom_right')
|
|
end
|
|
end),
|
|
['middle_location'] = Config.register_token(
|
|
function (player, event)
|
|
local data = Public.get_player_data(player)
|
|
if event.element.switch_state == 'left' then
|
|
data.above = true
|
|
data.portable = false
|
|
else
|
|
data.above = false
|
|
data.portable = false
|
|
end
|
|
if not data.bottom_state then
|
|
data.bottom_state = 'bottom_right'
|
|
end
|
|
|
|
Public.set_location(player, data.bottom_state)
|
|
end),
|
|
['portable_button'] = Config.register_token(
|
|
function (player, event)
|
|
local data = Public.get_player_data(player)
|
|
if event.element.switch_state == 'left' then
|
|
data.above = false
|
|
data.portable = true
|
|
else
|
|
data.portable = false
|
|
data.above = false
|
|
end
|
|
|
|
if not data.bottom_state then
|
|
data.bottom_state = 'bottom_right'
|
|
end
|
|
|
|
Public.set_location(player, data.bottom_state)
|
|
end),
|
|
}
|
|
})
|
|
|
|
local check_bottom_buttons_token =
|
|
Task.register(
|
|
function (event)
|
|
local player_index = event.player_index
|
|
local player = game.get_player(player_index)
|
|
if not player or not player.valid then
|
|
return
|
|
end
|
|
|
|
local player_data, storage_data = get_player_data(player)
|
|
if not player_data or not storage_data or not next(storage_data) then
|
|
destroy_frame(player)
|
|
remove_player(player.index)
|
|
return
|
|
end
|
|
end
|
|
)
|
|
|
|
remove_player = function (index)
|
|
this.players[index] = nil
|
|
this.storage[index] = nil
|
|
this.bottom_quickbar_button[index] = nil
|
|
end
|
|
|
|
get_player_data = function (player, remove_user_data)
|
|
if remove_user_data then
|
|
this.players[player.index] = nil
|
|
this.storage[player.index] = nil
|
|
return
|
|
end
|
|
if not this.players[player.index] then
|
|
this.players[player.index] =
|
|
{
|
|
state = 'bottom_right',
|
|
section = {},
|
|
direction = 'vertical',
|
|
row_index = 1,
|
|
row_selection = 1,
|
|
row_selection_added = 1
|
|
}
|
|
this.storage[player.index] = {}
|
|
end
|
|
return this.players[player.index], this.storage[player.index]
|
|
end
|
|
|
|
--- Refreshes all inner frames for a given player
|
|
local function refresh_inner_frames(player)
|
|
if not player or not player.valid then
|
|
return
|
|
end
|
|
local player_data, storage_data = get_player_data(player)
|
|
if not player_data or not storage_data or not player_data.frame or not player_data.frame.valid then
|
|
return
|
|
end
|
|
|
|
local main_frame = player_data.frame
|
|
|
|
local horizontal_flow = main_frame.add { type = 'flow', direction = 'horizontal' }
|
|
horizontal_flow.style.horizontal_spacing = 0
|
|
|
|
for row_index, row_index_data in pairs(storage_data) do
|
|
if row_index_data and type(row_index_data) == 'table' then
|
|
local section_row_index = player_data.section[row_index]
|
|
local vertical_flow = horizontal_flow.add { type = 'flow', direction = 'vertical' }
|
|
vertical_flow.style.vertical_spacing = 0
|
|
|
|
if not section_row_index then
|
|
player_data.section[row_index] = {}
|
|
section_row_index = player_data.section[row_index]
|
|
end
|
|
|
|
if not section_row_index.inside_frame or not section_row_index.inside_frame.valid then
|
|
section_row_index.inner_frame = vertical_flow
|
|
end
|
|
|
|
for row_selection, row_selection_data in pairs(row_index_data) do
|
|
if section_row_index[row_selection] and section_row_index[row_selection].valid then
|
|
section_row_index[row_selection].destroy()
|
|
end
|
|
|
|
section_row_index[row_selection] =
|
|
section_row_index.inner_frame.add
|
|
{
|
|
type = 'sprite-button',
|
|
sprite = row_selection_data.sprite,
|
|
name = row_selection_data.name,
|
|
tooltip = row_selection_data.tooltip or '',
|
|
style = 'quick_bar_page_button'
|
|
}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local refresh_inner_frames_token =
|
|
Task.register(
|
|
function (event)
|
|
local player_index = event.player_index
|
|
local player = game.get_player(player_index)
|
|
if not player or not player.valid then
|
|
return
|
|
end
|
|
|
|
refresh_inner_frames(player)
|
|
end
|
|
)
|
|
|
|
---Adds a new inner frame to the bottom frame
|
|
-- local BottomFrame = require 'utils.gui.bottom_frame'
|
|
-- BottomFrame.add_inner_frame({player = player, element_name = Gui.uid_name(), tooltip = 'Some tooltip', sprite = 'item/raw-fish' })
|
|
---@param data any
|
|
local function add_inner_frame(data)
|
|
if not data then
|
|
return
|
|
end
|
|
local player = data.player
|
|
local element_name = data.element_name
|
|
local tooltip = data.tooltip
|
|
local sprite = data.sprite
|
|
if not player or not player.valid then
|
|
return error('Given player was not valid', 2)
|
|
end
|
|
if not element_name then -- the element_name to pick from the row_selection
|
|
return error('Element name is missing', 2)
|
|
end
|
|
if not sprite then
|
|
return error('Sprite is missing', 2)
|
|
end
|
|
|
|
local player_data, storage_data = get_player_data(player)
|
|
if not player_data or not storage_data or not player_data.frame or not player_data.frame.valid then
|
|
return
|
|
end
|
|
|
|
if player_data.row_index > 6 then
|
|
return error('Having more than 6 rows is currently not supported.', 2)
|
|
end
|
|
|
|
local found = false
|
|
for _, row_index_data in pairs(storage_data) do
|
|
if row_index_data and type(row_index_data) == 'table' then
|
|
for _, row_selection_data in pairs(row_index_data) do
|
|
if row_selection_data and row_selection_data.name == element_name then
|
|
found = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if found then
|
|
return
|
|
end
|
|
|
|
player_data.row_index = sections[player_data.row_selection_added]
|
|
|
|
if not storage_data[player_data.row_index] then
|
|
storage_data[player_data.row_index] = {}
|
|
end
|
|
|
|
local storage_data_section = storage_data[player_data.row_index]
|
|
storage_data_section[player_data.row_selection] =
|
|
{
|
|
name = element_name,
|
|
sprite = sprite,
|
|
tooltip = tooltip
|
|
}
|
|
|
|
player_data.row_selection = player_data.row_selection + 1
|
|
player_data.row_selection_added = player_data.row_selection_added + 1
|
|
player_data.row_selection = player_data.row_selection > 2 and 1 or player_data.row_selection
|
|
Task.priority_delay(2, refresh_inner_frames_token, { player_index = player.index })
|
|
end
|
|
|
|
local function get_frame_by_element_name(player, element_name)
|
|
local player_data, storage_data = get_player_data(player)
|
|
if not player_data or not storage_data or not player_data.frame or not player_data.frame.valid then
|
|
return
|
|
end
|
|
|
|
for _, row_index_data in pairs(storage_data) do
|
|
if row_index_data and type(row_index_data) == 'table' then
|
|
for _, row_selection_data in pairs(row_index_data) do
|
|
if row_selection_data and row_selection_data.name == element_name then
|
|
return row_selection_data
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
destroy_frame = function (player)
|
|
local gui = player.gui
|
|
local frame = gui.screen[main_frame_name]
|
|
if frame and frame.valid then
|
|
frame.destroy()
|
|
end
|
|
end
|
|
|
|
--- Creates a new frame
|
|
---@param player LuaPlayer
|
|
---@param alignment string
|
|
---@param location table
|
|
---@param data any
|
|
---@return unknown
|
|
local function create_frame(player, alignment, location, data)
|
|
local gui = player.gui
|
|
local frame = gui.screen[main_frame_name]
|
|
if frame and frame.valid then
|
|
destroy_frame(player)
|
|
end
|
|
|
|
alignment = alignment or 'vertical'
|
|
|
|
frame =
|
|
player.gui.screen.add
|
|
{
|
|
type = 'frame',
|
|
name = main_frame_name,
|
|
direction = alignment
|
|
}
|
|
|
|
if data.visible ~= nil then
|
|
if data.visible then
|
|
frame.visible = true
|
|
else
|
|
frame.visible = false
|
|
end
|
|
end
|
|
|
|
frame.style.padding = 3
|
|
frame.style.top_padding = 4
|
|
if alignment == 'vertical' then
|
|
frame.style.minimal_height = 96
|
|
end
|
|
|
|
local inner_frame =
|
|
frame.add
|
|
{
|
|
type = 'frame',
|
|
direction = alignment
|
|
}
|
|
inner_frame.style = 'quick_bar_inner_panel'
|
|
|
|
frame.location = location
|
|
if data.portable then
|
|
frame.caption = '•'
|
|
end
|
|
|
|
if data.top then
|
|
frame.visible = false
|
|
else
|
|
frame.visible = true
|
|
end
|
|
|
|
data.frame = inner_frame
|
|
data.parent = frame
|
|
data.section = data.section or {}
|
|
data.section_data = data.section_data or {}
|
|
data.alignment = alignment
|
|
|
|
Task.priority_delay(5, check_bottom_buttons_token, { player_index = player.index })
|
|
|
|
return frame
|
|
end
|
|
|
|
set_location = function (player, state)
|
|
local data = get_player_data(player)
|
|
local alignment = 'vertical'
|
|
|
|
local location
|
|
local resolution = player.display_resolution
|
|
local scale = player.display_scale
|
|
|
|
state = state or data.state
|
|
|
|
if state == 'bottom_left' then
|
|
if data.above then
|
|
location =
|
|
{
|
|
x = (resolution.width / 2) - ((259) * scale),
|
|
y = (resolution.height - (-12 + (40 * 5) * scale))
|
|
}
|
|
alignment = 'horizontal'
|
|
else
|
|
location =
|
|
{
|
|
-- x = (resolution.width / 2) - ((54 + 528 - 44) * scale),
|
|
x = (resolution.width / 2) - ((455 + (data.row_index * 40)) * scale),
|
|
y = (resolution.height - (96 * scale))
|
|
}
|
|
end
|
|
data.bottom_state = 'bottom_left'
|
|
elseif state == 'bottom_right' then
|
|
if data.above then
|
|
location =
|
|
{
|
|
-- x = (resolution.width / 2) - ((-262 - (40 * t[data.row_index])) * scale),
|
|
x = (resolution.width / 2) - ((-460 + (data.row_index * 40)) * scale),
|
|
y = (resolution.height - (-12 + (40 * 5) * scale))
|
|
}
|
|
alignment = 'horizontal'
|
|
else
|
|
location =
|
|
{
|
|
x = (resolution.width / 2) - ((54 + -689) * scale),
|
|
y = (resolution.height - (96 * scale))
|
|
}
|
|
end
|
|
data.bottom_state = 'bottom_right'
|
|
else
|
|
location =
|
|
{
|
|
x = (resolution.width / 2) - ((54 + -528) * scale),
|
|
y = (resolution.height - (96 * scale))
|
|
}
|
|
end
|
|
|
|
Event.raise(CustomEvents.events.bottom_quickbar_location_changed, { player_index = player.index, data = data })
|
|
|
|
data.state = state
|
|
create_frame(player, alignment, location, data)
|
|
refresh_inner_frames(player)
|
|
end
|
|
|
|
--- Sets then frame location of the given player
|
|
---@param player LuaPlayer?
|
|
---@param value boolean
|
|
local function set_top(player, value)
|
|
local data = get_player_data(player)
|
|
data.top = value or false
|
|
Public.set_location(player, 'bottom_right')
|
|
end
|
|
|
|
--- Returns the current frame location of the given player
|
|
---@param player LuaPlayer
|
|
---@return table|nil
|
|
local function get_location(player)
|
|
local data = get_player_data(player)
|
|
return data and data.state or nil
|
|
end
|
|
|
|
--- Activates the custom buttons
|
|
---@param value boolean
|
|
function Public.activate_custom_buttons(value)
|
|
this.activate_custom_buttons = value or false
|
|
end
|
|
|
|
--- Checks if custom buttons are enabled.
|
|
--- @return boolean: True if custom buttons are enabled, false otherwise.
|
|
function Public.is_custom_buttons_enabled()
|
|
return this.activate_custom_buttons
|
|
end
|
|
|
|
--- Toggles the player frame.
|
|
--- @param player LuaPlayer: The player entity.
|
|
--- @param state boolean: The state to set for the player frame.
|
|
function Public.toggle_player_frame(player, state)
|
|
local gui = player.gui
|
|
local frame = gui.screen[main_frame_name]
|
|
if frame and frame.valid then
|
|
local data = get_player_data(player)
|
|
if state then
|
|
data.visible = true
|
|
frame.visible = true
|
|
else
|
|
data.visible = false
|
|
frame.visible = false
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Returns the current frame of the given player
|
|
---@param player LuaPlayer
|
|
---@param section_name string
|
|
---@return table|boolean|nil
|
|
function Public.get_section(player, section_name)
|
|
local data = get_player_data(player)
|
|
local section = data.section
|
|
if not section then
|
|
return false
|
|
end
|
|
|
|
for _, section_tbl in pairs(section) do
|
|
if not section_tbl or not next(section_tbl) then
|
|
break
|
|
end
|
|
|
|
for _, section_data in pairs(section_tbl) do
|
|
if section_data and section_data.valid and section_data.name == section_name then
|
|
return section_data
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Retrieves the value associated with the specified key.
|
|
--- @param key any The key to retrieve the value for.
|
|
--- @return any The value associated with the key.
|
|
function Public.get(key)
|
|
if key then
|
|
return this[key]
|
|
else
|
|
return this
|
|
end
|
|
end
|
|
|
|
--- Sets the value of a given key.
|
|
--- @param key any The key to set.
|
|
--- @param value any The value to set for the key.
|
|
function Public.set(key, value)
|
|
if key and (value or value == false) then
|
|
this[key] = value
|
|
return this[key]
|
|
elseif key then
|
|
return this[key]
|
|
else
|
|
return this
|
|
end
|
|
end
|
|
|
|
--- Resets the bottom frame.
|
|
function Public.reset()
|
|
local players = game.players
|
|
for i = 1, #players do
|
|
local player = players[i]
|
|
if player and player.valid then
|
|
if not player.connected then
|
|
this.players[player.index] = nil
|
|
this.storage[player.index] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
Event.add(
|
|
defines.events.on_player_joined_game,
|
|
function (event)
|
|
if this.activate_custom_buttons then
|
|
local player = game.get_player(event.player_index)
|
|
local data = get_player_data(player)
|
|
set_location(player, data.state)
|
|
end
|
|
end
|
|
)
|
|
|
|
Event.add(
|
|
defines.events.on_player_display_resolution_changed,
|
|
function (event)
|
|
if this.activate_custom_buttons then
|
|
local player = game.get_player(event.player_index)
|
|
local data = get_player_data(player)
|
|
set_location(player, data.state)
|
|
end
|
|
end
|
|
)
|
|
|
|
Event.add(
|
|
defines.events.on_player_display_scale_changed,
|
|
function (event)
|
|
local player = game.get_player(event.player_index)
|
|
if this.activate_custom_buttons then
|
|
local data = get_player_data(player)
|
|
set_location(player, data.state)
|
|
end
|
|
end
|
|
)
|
|
|
|
Event.add(
|
|
defines.events.on_pre_player_left_game,
|
|
function (event)
|
|
local player = game.get_player(event.player_index)
|
|
destroy_frame(player)
|
|
if this.activate_custom_buttons then
|
|
get_player_data(player, true)
|
|
end
|
|
end
|
|
)
|
|
|
|
Event.add(
|
|
defines.events.on_player_left_game,
|
|
function (event)
|
|
local player = game.get_player(event.player_index)
|
|
destroy_frame(player)
|
|
if this.activate_custom_buttons then
|
|
get_player_data(player, true)
|
|
end
|
|
end
|
|
)
|
|
|
|
Event.add(
|
|
defines.events.on_pre_player_died,
|
|
function (event)
|
|
if this.activate_custom_buttons then
|
|
local player = game.get_player(event.player_index)
|
|
destroy_frame(player)
|
|
end
|
|
end
|
|
)
|
|
|
|
Event.add(
|
|
defines.events.on_player_respawned,
|
|
function (event)
|
|
if this.activate_custom_buttons then
|
|
local player = game.get_player(event.player_index)
|
|
local data = get_player_data(player)
|
|
set_location(player, data.state)
|
|
end
|
|
end
|
|
)
|
|
|
|
Event.add(
|
|
defines.events.on_player_removed,
|
|
function (event)
|
|
remove_player(event.player_index)
|
|
end
|
|
)
|
|
|
|
Event.add(
|
|
CustomEvents.events.bottom_quickbar_respawn_raise,
|
|
function (event)
|
|
if not event or not event.player_index then
|
|
return
|
|
end
|
|
|
|
if this.activate_custom_buttons then
|
|
local player = game.get_player(event.player_index)
|
|
local data = get_player_data(player)
|
|
set_location(player, data.state)
|
|
end
|
|
end
|
|
)
|
|
|
|
Event.add(
|
|
CustomEvents.events.bottom_quickbar_location_changed,
|
|
function (event)
|
|
if not event or not event.player_index then
|
|
return
|
|
end
|
|
|
|
if this.activate_custom_buttons then
|
|
local player = game.get_player(event.player_index)
|
|
local data = get_player_data(player)
|
|
if data.frame and data.frame.valid then
|
|
if data.top then
|
|
data.frame.visible = false
|
|
else
|
|
data.frame.visible = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
)
|
|
|
|
Public.main_frame_name = main_frame_name
|
|
Public.refresh_inner_frames = refresh_inner_frames
|
|
Public.get_player_data = get_player_data
|
|
Public.remove_player = remove_player
|
|
Public.set_location = set_location
|
|
Public.get_location = get_location
|
|
Public.set_top = set_top
|
|
Public.add_inner_frame = add_inner_frame
|
|
Public.get_frame_by_element_name = get_frame_by_element_name
|
|
Gui.screen_to_bypass(main_frame_name)
|
|
|
|
return Public
|