This commit is contained in:
2026-05-20 15:36:04 +09:00
23 changed files with 2964 additions and 3256 deletions
+3 -1
View File
@@ -1,5 +1,5 @@
import * as lib from "@clusterio/lib";
import * as Messages from "./messages";
// import * as Messages from "./messages";
lib.definePermission({
name: "exp_scenario.config.view",
@@ -24,6 +24,7 @@ export const plugin: lib.PluginDeclaration = {
controllerEntrypoint: "./dist/node/controller",
instanceEntrypoint: "./dist/node/instance",
/*
messages: [
],
@@ -31,4 +32,5 @@ export const plugin: lib.PluginDeclaration = {
routes: [
"/exp_scenario",
],
*/
};
+2
View File
@@ -76,6 +76,8 @@ add(require("modules/exp_scenario/gui/player_bonus"))
add(require("modules/exp_scenario/gui/player_stats"))
add(require("modules/exp_scenario/gui/production_stats"))
add(require("modules/exp_scenario/gui/quick_actions"))
add(require("modules/exp_scenario/gui/readme"))
add(require("modules/exp_scenario/gui/research_milestones"))
add(require("modules/exp_scenario/gui/science_production"))
add(require("modules/exp_scenario/gui/surveillance"))
add(require("modules/exp_scenario/gui/task_list"))
@@ -37,8 +37,11 @@ local function on_pre_player_died(event)
local player = assert(game.get_player(event.player_index))
local cause = event.cause
if cause then
local by_player = event.cause.player
add_log_line("[DEATH]", player.name, "died because of", by_player and by_player.name or event.cause.name)
if cause.type == "character" then
add_log_line("[DEATH]", player.name, "died because of", cause.player.name)
else
add_log_line("[DEATH]", player.name, "died because of", cause.name)
end
else
add_log_line("[DEATH]", player.name, "died because of unknown reason")
end
@@ -52,9 +55,9 @@ local function on_research_finished(event)
local inf_research_level = config_res.inf_res[config_res.mod_set][event.research.name]
if inf_research_level and event.research.level >= inf_research_level then
add_log_line_locale{ "", "[RES] ", event.research.prototype.localised_name, " at level ", event.research.level - 1, "has been researched\n" }
add_log_line_locale{ "", "[RES]", event.research.prototype.localised_name, " at level ", event.research.level - 1, " has been researched\n" }
else
add_log_line_locale{ "", "[RES] ", event.research.prototype.localised_name, "has been researched\n" }
add_log_line_locale{ "", "[RES]", event.research.prototype.localised_name, " has been researched\n" }
end
end
@@ -67,7 +70,7 @@ end
--- @param event EventData.on_player_left_game
local function on_player_left_game(event)
local player = assert(game.get_player(event.player_index))
add_log_line("[LEAVE]", game.players[event.player_index].name, config.disconnect_reason[event.reason])
add_log_line("[LEAVE]", player.name, config.disconnect_reason[event.reason])
end
local e = defines.events
@@ -1,5 +1,8 @@
--[[-- Control - Inventory Clear
Will move players items to spawn when they are banned or kicked, option to clear on leave
on_player_banned - player_name
on_player_kicked - player_index
]]
local ExpUtil = require("modules/exp_util")
@@ -7,6 +10,7 @@ local events = require("modules.exp_legacy.config.inventory_clear")
--- @param event { player_index: number }
local function clear_items(event)
if not event.player_index then return end
local player = assert(game.get_player(event.player_index))
local inventory = assert(player.get_main_inventory())
ExpUtil.transfer_inventory_to_surface{
+701
View File
@@ -0,0 +1,701 @@
--[[-- Gui Module - Readme
Adds a main gui that contains important information about the server.
]]
local ExpUtil = require("modules/exp_util")
local Gui = require("modules/exp_gui")
local Roles = require("modules.exp_legacy.expcore.roles")
local Commands = require("modules/exp_commands")
local PlayerData = require("modules.exp_legacy.expcore.player_data")
local External = require("modules.exp_legacy.expcore.external")
local format_number = require("util").format_number
local format_time = ExpUtil.format_time_factory_locale{ format = "long", days = true, hours = true, minutes = true }
local frame_width = 595
local title_width = 270
local scroll_height = 275
--- @class ExpGui_Readme.elements
local Elements = {}
--- @type table<number, { caption: LocalisedString, tooltip: LocalisedString, element: ExpElement }>
local tabs = {}
--- Register a readme tab
--- @param caption LocalisedString
--- @param tooltip LocalisedString
--- @param element ExpElement
local function define_tab(caption, tooltip, element)
tabs[#tabs + 1] = { caption = caption, tooltip = tooltip, element = element }
end
--- Create a title section table
--- @class ExpGui_Readme.elements.title_table: ExpElement
--- @overload fun(parent: LuaGuiElement, bar_size: number, caption: LocalisedString, column_count: number): LuaGuiElement
Elements.title_table = Gui.define("readme/title_table")
:draw(function(_, parent, bar_size, caption, column_count)
Gui.elements.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,
} --[[ @as any ]]
--- Scroll pane used for title tables
--- @class ExpGui_Readme.elements.title_table_scroll: ExpElement
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
Elements.title_table_scroll = Gui.define("readme/title_table_scroll")
:draw{
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,
} --[[ @as any ]]
--- Sub content frame
--- @class ExpGui_Readme.elements.sub_content: ExpElement
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
Elements.sub_content = Gui.define("readme/sub_content")
:draw{
type = "frame",
direction = "vertical",
style = "inside_deep_frame",
}
:style{
horizontally_stretchable = true,
horizontal_align = "center",
padding = { 2, 2 },
top_margin = 2,
} --[[ @as any ]]
--- @class ExpGui_Readme.elements.join_server.elements
--- @field server_id string
--- Join server button
--- @class ExpGui_Readme.elements.join_server: ExpElement
--- @field data table<LuaGuiElement, ExpGui_Readme.elements.join_server.elements>
--- @overload fun(parent: LuaGuiElement, server_id: string, wrong_version: string?): LuaGuiElement
Elements.join_server = Gui.define("readme/join_server")
:track_all_elements()
:draw(function(def, parent, server_id, wrong_version)
--- @cast def ExpGui_Readme.elements.join_server
local flow = parent.add{
type = "flow",
}
local button = flow.add{
type = "sprite-button",
sprite = "utility/circuit_network_panel",
hovered_sprite = "utility/circuit_network_panel",
style = "frame_action_button",
}
def.data[button] = {
server_id = server_id,
}
Elements.join_server.refresh(button, wrong_version)
return button
end)
:style{
size = 20,
padding = -1,
}
:on_click(function(def, player, button)
--- @cast def ExpGui_Readme.elements.join_server
local server_id = def.data[button].server_id
External.request_connection(player, server_id, true)
end) --[[ @as any ]]
--- Refresh join server button
--- @param button LuaGuiElement
--- @param wrong_version string?
function Elements.join_server.refresh(button, wrong_version)
local server_id = Elements.join_server.data[button].server_id
local status = External.get_server_status(server_id) or "Offline"
if wrong_version then
status = "Version"
end
button.tooltip = { "exp-gui_readme.servers-connect-" .. status, wrong_version }
if status == "Offline" or status == "Current" then
button.enabled = false
button.sprite = "utility/circuit_network_panel"
button.hovered_sprite = "utility/circuit_network_panel"
elseif status == "Version" then
button.enabled = false
button.sprite = "utility/shuffle"
button.hovered_sprite = "utility/shuffle"
elseif status == "Password" then
button.enabled = true
button.sprite = "utility/warning_white"
button.hovered_sprite = "utility/warning"
elseif status == "Modded" then
button.enabled = true
button.sprite = "utility/downloading_white"
button.hovered_sprite = "utility/downloading"
else
button.enabled = true
button.sprite = "utility/circuit_network_panel"
button.hovered_sprite = "utility/circuit_network_panel"
end
end
--- Refresh all online join buttons
function Elements.join_server.refresh_all()
if not External.valid() then
return
end
local current_version = External.get_current_server().version
for _, button in Elements.join_server:tracked_elements() do
local server_id = Elements.join_server.data[button].server_id
local server = External.get_servers()[server_id]
if server then
Elements.join_server.refresh(button, current_version ~= server.version and server.version or nil)
end
end
end
--- Welcome tab
define_tab(
{ "exp-gui_readme.welcome-tab" },
{ "exp-gui_readme.welcome-tooltip" },
Gui.define("readme/welcome")
:draw(function(_, parent)
local player = Gui.get_player(parent)
local server_details = {
name = "ExpGaming S0 - Local",
welcome = "Failed to load description: disconnected from external api.",
reset_time = "Not Set",
}
if External.valid() then
server_details = External.get_current_server()
end
local container = parent.add{ type = "flow", direction = "vertical" }
local top_flow = container.add{ type = "flow" }
top_flow.add{ type = "sprite", sprite = "file/modules/exp_legacy/modules/gui/logo.png" }
local center_flow = top_flow.add{ type = "flow", direction = "vertical" }
center_flow.style.horizontal_align = "center"
Gui.elements.title_label(center_flow, 62, { "exp-gui_readme.welcome-title", server_details.name })
Gui.elements.centered_label(center_flow, 380, server_details.welcome)
top_flow.add{ type = "sprite", sprite = "file/modules/exp_legacy/modules/gui/logo.png" }
Gui.elements.bar(container)
container.add{ type = "flow" }.style.height = 4
local role_names = {}
for i, role in ipairs(Roles.get_player_roles(player)) do
role_names[i] = role.name
end
Gui.elements.centered_label(
Elements.sub_content(container),
frame_width,
{
"exp-gui_readme.welcome-general",
server_details.reset_time,
format_time(game.tick),
}
)
Gui.elements.centered_label(
Elements.sub_content(container),
frame_width,
{
"exp-gui_readme.welcome-roles",
table.concat(role_names, ", "),
}
)
Gui.elements.centered_label(
Elements.sub_content(container),
frame_width,
{ "exp-gui_readme.welcome-chat" }
)
return container
end) --[[ @as any ]]
)
--- Rules tab
define_tab(
{ "exp-gui_readme.rules-tab" },
{ "exp-gui_readme.rules-tooltip" },
Gui.define("readme/rules")
:draw(function(_, parent)
local container = parent.add{ type = "flow", direction = "vertical" }
Gui.elements.title_label(container, title_width - 3, { "exp-gui_readme.rules-tab" })
Gui.elements.centered_label(container, frame_width, { "exp-gui_readme.rules-general" })
Gui.elements.bar(container)
container.add{ type = "flow" }
local rules = Gui.elements.scroll_table(container, scroll_height, 1)
rules.style = "bordered_table"
rules.style.cell_padding = 4
for i = 1, 15 do
Gui.elements.centered_label(rules, frame_width - 30, { "exp-gui_readme.rules-" .. i })
end
return container
end) --[[ @as any ]]
)
--- Commands tab
define_tab(
{ "exp-gui_readme.commands-tab" },
{ "exp-gui_readme.commands-tooltip" },
Gui.define("readme/commands")
:draw(function(_, parent)
local player = Gui.get_player(parent)
local container = parent.add{ type = "flow", direction = "vertical" }
Gui.elements.title_label(container, title_width - 20, { "exp-gui_readme.commands-tab" })
Gui.elements.centered_label(container, frame_width, { "exp-gui_readme.commands-general" })
Gui.elements.bar(container)
container.add{ type = "flow" }
local commands = Gui.elements.scroll_table(container, scroll_height, 2)
commands.style = "bordered_table"
commands.style.cell_padding = 0
for name, command in pairs(Commands.list_for_player(player)) do
Gui.elements.centered_label(commands, 120, name)
Gui.elements.centered_label(commands, 450, command.description)
end
return container
end) --[[ @as any ]]
)
--- Servers tab
define_tab(
{ "exp-gui_readme.servers-tab" },
{ "exp-gui_readme.servers-tooltip" },
Gui.define("readme/servers")
:draw(function(_, parent)
local container = parent.add{ type = "flow", direction = "vertical" }
Gui.elements.title_label(container, title_width - 10, { "exp-gui_readme.servers-tab" })
Gui.elements.centered_label(container, frame_width, { "exp-gui_readme.servers-general" })
Gui.elements.bar(container)
container.add{ type = "flow" }
local scroll_pane = Elements.title_table_scroll(container)
scroll_pane.style.maximal_height = scroll_height + 20
if External.valid() then
local current_version = External.get_current_server().version
local factorio_servers = Elements.title_table(scroll_pane, 225, { "exp-gui_readme.servers-factorio" }, 3)
for server_id, server in pairs(External.get_servers()) do
Gui.elements.centered_label(factorio_servers, 110, server.short_name)
Gui.elements.centered_label(factorio_servers, 436, server.description)
Elements.join_server(factorio_servers, server_id, current_version ~= server.version and server.version or nil)
end
else
local factorio_servers = Elements.title_table(scroll_pane, 225, { "exp-gui_readme.servers-factorio" }, 2)
for i = 1, 8 do
Gui.elements.centered_label(factorio_servers, 110, { "exp-gui_readme.servers-" .. i })
Gui.elements.centered_label(factorio_servers, 460, { "exp-gui_readme.servers-d" .. i })
end
end
local external_links = Elements.title_table(scroll_pane, 235, { "exp-gui_readme.servers-external" }, 2)
for _, key in ipairs{ "discord", "website", "patreon", "status", "github" } do
Gui.elements.centered_label(external_links, 110, key:gsub("^%l", string.upper))
Gui.elements.centered_label(external_links, 460, { "links." .. key }, { "exp-gui_readme.servers-open-in-browser" })
end
return container
end) --[[ @as any ]]
)
--- Backers tab
--- Content area for the backers tab
define_tab(
{ "exp-gui_readme.backers-tab" },
{ "exp-gui_readme.backers-tooltip" },
Gui.define("readme/backers")
:draw(function(_, parent)
local container = parent.add{ type = "flow", direction = "vertical" }
Gui.elements.title_label(container, title_width - 10, { "exp-gui_readme.backers-tab" })
Gui.elements.centered_label(container, frame_width, { "exp-gui_readme.backers-general" })
Gui.elements.bar(container)
container.add{ type = "flow" }
local groups = {
{
roles = { "Senior Administrator", "Administrator" },
title = { "exp-gui_readme.backers-management" },
width = 230,
players = {},
},
{
roles = { "Senior Moderator", "Moderator", "Trainee Moderator" },
title = { "exp-gui_readme.backers-staff" },
width = 230,
players = {},
},
{
roles = { "Board Member", "Supporter", "Partner" },
title = { "exp-gui_readme.backers-backers" },
width = 230,
players = {},
},
{
roles = {},
time = 1 * 3600 * 60,
title = { "exp-gui_readme.backers-active" },
width = 230,
players = {},
},
}
local done = {}
-- Fill groups from configured roles
for player_name, player_roles in pairs(Roles.config.players) do
for _, group in ipairs(groups) do
for _, role_name in ipairs(group.roles) do
if table.contains(player_roles, role_name) then
done[player_name] = true
group.players[#group.players + 1] = player_name
break
end
end
end
end
-- Fill active player group
for _, player in pairs(game.players) do
if not done[player.name] then
for _, group in ipairs(groups) do
if group.time and player.online_time > group.time then
group.players[#group.players + 1] = player.name
end
end
end
end
local scroll_pane = Elements.title_table_scroll(container)
for _, group in ipairs(groups) do
if #group.players > 0 then
local backers_table = Elements.title_table(scroll_pane, group.width, group.title, 4)
for _, player_name in ipairs(group.players) do
Gui.elements.centered_label(backers_table, 140, player_name)
end
if #group.players < 4 then
for i = 1, 4 - #group.players do
Gui.elements.centered_label(backers_table, 140)
end
end
end
end
return container
end) --[[ @as any ]]
)
--- @class (exact) ExpGui_Readme.elements.readme_data.param table
--- @field scroll_pane LuaGuiElement
--- @field player LuaPlayer
--- @field player_name string
--- @field children table
--- @field title LocalisedString
--- @field locale_prefix string
--- @field default_stringify fun(value: any): string
--- @field columns number
--- @field title_width number
--- @field column_width number
--- @field extra_rows fun(data_table: LuaGuiElement)?
--- Render a player data category
--- @param opts ExpGui_Readme.elements.readme_data.param
local function render_data_category(opts)
local data_table = Elements.title_table(opts.scroll_pane, opts.title_width, opts.title, opts.columns)
if opts.extra_rows then
opts.extra_rows(data_table)
end
for name, child in pairs(opts.children) do
local metadata = child.metadata
if not metadata.permission or Roles.player_allowed(opts.player, metadata.permission) then
local value = child:get(opts.player_name)
if value ~= nil or metadata.show_always then
if metadata.stringify_short then
value = metadata.stringify_short(value)
elseif metadata.stringify then
value = metadata.stringify(value)
else
value = opts.default_stringify(value)
end
local tooltip = metadata.tooltip or { opts.locale_prefix .. name .. "-tooltip" }
Gui.elements.centered_label(
data_table, 150,
metadata.name or { opts.locale_prefix .. name },
tooltip
)
Gui.elements.centered_label(
data_table, opts.column_width,
{ "exp-gui_readme.data-format", value, metadata.unit or "" },
metadata.value_tooltip or { "?", { opts.locale_prefix .. name .. "-value-tooltip" }, tooltip }
)
end
end
end
end
--- Content area for the player data tab
define_tab(
{ "exp-gui_readme.data-tab" },
{ "exp-gui_readme.data-tooltip" },
Gui.define("readme/data")
:draw(function(_, parent)
local container = parent.add{ type = "flow", direction = "vertical" }
local player = Gui.get_player(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]
Gui.elements.title_label(container, title_width, { "exp-gui_readme.data-tab" })
Gui.elements.centered_label(container, frame_width, { "exp-gui_readme.data-general" })
Gui.elements.bar(container)
container.add{ type = "flow" }
local scroll_pane = Elements.title_table_scroll(container)
render_data_category{
scroll_pane = scroll_pane,
player = player,
player_name = player_name,
children = PlayerData.Required.children,
title = { "exp-gui_readme.data-required" },
locale_prefix = "exp-required.",
columns = 2,
title_width = 250,
column_width = 420,
default_stringify = tostring,
extra_rows = function(required)
Gui.elements.centered_label(required, 150, preference_meta.name, preference_meta.tooltip)
Gui.elements.centered_label(required, 420, { "expcore-data.preference-" .. enum[preference] }, preference_meta.value_tooltip)
end,
}
if preference <= enum.Settings then
render_data_category{
scroll_pane = scroll_pane,
player = player,
player_name = player_name,
children = PlayerData.Settings.children,
title = { "exp-gui_readme.data-settings" },
locale_prefix = "exp-settings.",
columns = 2,
title_width = 255,
column_width = 420,
default_stringify = function(value)
return tostring(value or "None set")
end,
}
end
if preference <= enum.Statistics then
render_data_category{
scroll_pane = scroll_pane,
player = player,
player_name = player_name,
children = PlayerData.Statistics.children,
title = { "exp-gui_readme.data-statistics" },
locale_prefix = "exp-statistics.",
columns = 4,
title_width = 250,
column_width = 130,
default_stringify = function(value)
return format_number(value or 0, false)
end,
}
end
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 = {}
for name, child in pairs(PlayerData.All.children) do
if not skip[name] then
misc[name] = child
end
end
render_data_category{
scroll_pane = scroll_pane,
player = player,
player_name = player_name,
children = misc,
title = { "exp-gui_readme.data-misc" },
locale_prefix = "",
columns = 2,
title_width = 232,
column_width = 420,
default_stringify = tostring,
}
end
return container
end) --[[ @as any ]]
)
--- @class ExpGui_Readme.elements.container.elements
--- @field pane LuaGuiElement
--- Main readme container
--- @class ExpGui_Readme.elements.container: ExpElement
--- @field data table<LuaGuiElement, ExpGui_Readme.elements.container.elements>
Elements.container = Gui.define("readme/container")
:track_all_elements()
:draw(function(def, parent)
--- @cast def ExpGui_Readme.elements.container
local container = parent.add{ name = def.name, type = "frame", style = "invisible_frame" }
local left_alignment = Gui.elements.aligned_flow(container, { vertical_align = "bottom" })
left_alignment.style.padding = { 32, 0, 0, 0 }
local left_side = left_alignment.add{ type = "frame", style = "character_gui_left_side" }
left_side.style.vertically_stretchable = true
left_side.style.padding = 0
left_side.style.width = 5
local pane = container.add{
name = "pane",
type = "tabbed-pane",
style = "frame_tabbed_pane",
}
for _, tab in ipairs(tabs) do
local gui_tab = pane.add{
type = "tab",
style = "frame_tab",
caption = tab.caption,
tooltip = tab.tooltip,
}
pane.add_tab(gui_tab, tab.element(pane))
end
def.data[container] = {
pane = pane,
}
return container
end)
:on_opened(function(def, player)
Gui.toolbar.set_button_toggled_state(Elements.toggle_button, player, true)
end)
:on_closed(function(def, player, element)
Gui.toolbar.set_button_toggled_state(Elements.toggle_button, player, false)
Gui.destroy_if_valid(element)
end)
--- Toggle button
Elements.toggle_button = Gui.toolbar.create_button{
name = "readme_toggle",
auto_toggle = true,
sprite = "virtual-signal/signal-info",
tooltip = { "exp-gui_readme.main-tooltip" },
visible = function(player)
return Roles.player_allowed(player, "gui/readme")
end,
}
:on_click(function(_, player)
local center = player.gui.center
local readme = center[Elements.container.name]
if readme then
player.opened = nil
else
player.opened = Elements.container(center)
end
end)
--- Open readme for new players
--- @param event EventData.on_player_created
local function open_readme(event)
local player = assert(game.get_player(event.player_index))
local element = Elements.container(player.gui.center)
element.pane.selected_tab_index = 1
player.opened = element
end
--- Clear stale readme
--- @param event EventData.on_player_joined_game | EventData.on_player_respawned
local function clear_readme(event)
local player = game.players[event.player_index]
if not player.opened then
Gui.destroy_if_valid(player.gui.center[Elements.container.name])
end
end
local e = defines.events
return {
elements = Elements,
events = {
[e.on_player_created] = open_readme,
[e.on_player_joined_game] = clear_readme,
[e.on_player_respawned] = clear_readme,
},
on_nth_tick = {
[60 * 60] = Elements.join_server.refresh_all,
}
}
+951
View File
@@ -0,0 +1,951 @@
--[[-- Gui - Task List
Adds a task list to the game which players can add, remove and edit items on
]]
local Gui = require("modules/exp_gui")
local Roles = require("modules.exp_legacy.expcore.roles")
local config = require("modules.exp_legacy.config.gui.tasks")
local ExpUtil = require("modules/exp_util")
local format_time = ExpUtil.format_time_factory_locale{ format = "short", hours = true, minutes = true }
--- @class ExpGui_TaskList.elements
local Elements = {}
local Styles = {
sprite22 = Gui.styles.sprite{
size = 22
},
footer_button = Gui.styles.sprite{
height = 29,
maximal_width = 268,
horizontally_stretchable = true,
},
}
--- @class ExpGui_TaskList.Task
--- @field id number
--- @field last_user LuaPlayer
--- @field last_edit_tick number
--- @field editing table<number, string>
--- @field title string
--- @field body string
--- @field new boolean?
--- @field deleted boolean?
--- Check if a player can create a new task
--- @param player LuaPlayer
--- @return boolean
local function has_permission_create_task(player)
local allow_add_task = config.allow_add_task
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
end
--- Check if a player can edit an existing task
--- @param player LuaPlayer
--- @param task ExpGui_TaskList.Task
--- @return boolean
local function has_permission_edit_task(player, task)
local allow_edit_task = config.allow_edit_task
-- Check if editing your own task allows bypassing other permissions
if config.user_can_edit_own_tasks and task.last_user.index == player.index then
return true
end
-- Check player has permission 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
end
--- @class ExpGui_TaskList.element.new_task_button.elements
--- @field container LuaGuiElement
--- Button displayed in the header bar, used to add a new task
--- @class ExpGui_TaskList.element.new_task_button: ExpElement
--- @field data table<LuaGuiElement, ExpGui_TaskList.element.new_task_button.elements>
--- @overload fun(parent: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
Elements.new_task_button = Gui.define("task_list/new_task_button")
:track_all_elements()
:draw{
type = "sprite-button",
sprite = "utility/add",
tooltip = { "exp-gui_task-list.tooltip-new" },
style = "tool_button",
}
:style(Styles.sprite22)
:element_data{
container = Gui.from_argument(1)
}
:on_click(function(def, player, new_task_button)
--- @cast def ExpGui_TaskList.element.new_task_button
local container = def.data[new_task_button].container
Elements.container.open_edit_task(container, {
id = Elements.container.next_task_id(),
last_edit_tick = game.tick,
last_user = player,
editing = {},
title = "",
body = "",
new = true,
})
end) --[[ @as any ]]
--- Refresh the visibility based on player permissions
--- @param new_task_button LuaGuiElement
function Elements.new_task_button.refresh(new_task_button)
local player = Gui.get_player(new_task_button)
new_task_button.visible = has_permission_create_task(player)
end
--- Refresh the visibility based on player permissions
--- @param player LuaPlayer
function Elements.new_task_button.refresh_player(player)
local visible = has_permission_create_task(player)
for _, new_task_button in Elements.new_task_button:tracked_elements(player) do
new_task_button.visible = visible
end
end
--- @class ExpGui_TaskList.elements.view_task_button.elements
--- @field task ExpGui_TaskList.Task
--- @field container LuaGuiElement
--- Button used to view a task details, the caption is the task title
--- @class ExpGui_TaskList.element.view_task_button: ExpElement
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.view_task_button.elements>
--- @overload fun(parent: LuaGuiElement, task: ExpGui_TaskList.Task, container: LuaGuiElement): LuaGuiElement
Elements.view_task_button = Gui.define("task_list/view_task_button")
:draw(function(def, parent, task)
--- @cast def ExpGui_TaskList.element.view_task_button
--- @cast task ExpGui_TaskList.Task
return parent.add{
type = "button",
style = "list_box_item",
caption = task.title,
tooltip = { "exp-gui_task-list.tooltip-last-edit", task.last_user.name, format_time(task.last_edit_tick) },
}
end)
:style{
width = 268,
horizontally_stretchable = true,
}
:element_data{
task = Gui.from_argument(1),
container = Gui.from_argument(2),
}
:on_click(function(def, player, view_task_button)
--- @cast def ExpGui_TaskList.element.view_task_button
local elements = def.data[view_task_button]
Elements.container.open_view_task(elements.container, elements.task)
end) --[[ @as any ]]
--- Refresh the title and tooltip of the view task button
--- @param view_task_button LuaGuiElement
--- @param task ExpGui_TaskList.Task
function Elements.view_task_button.refresh(view_task_button, task)
view_task_button.caption = task.title
view_task_button.tooltip = { "exp-gui_task-list.tooltip-last-edit", task.last_user.name, format_time(task.last_edit_tick) }
end
--- Label used to signal that no tasks are open
--- @class ExpGui_TaskList.elements.no_tasks_header: ExpElement
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
Elements.no_tasks_header = Gui.define("task_list/no_tasks_header")
:track_all_elements()
:draw(function(def, parent, ...)
local subheader = Gui.elements.subframe_base(parent, "negative_subheader_frame")
local label = subheader.add{
type = "label",
style = "bold_label",
caption = { "exp-gui_task-list.caption-no-tasks" },
tooltip = { "exp-gui_task-list.tooltip-no-tasks" },
}
label.style.width = 268
label.style.horizontal_align = "center"
return subheader
end)
:style{
padding = { 2, 0 },
bottom_margin = 0,
} --[[ @as any ]]
--- Refresh the visibility of the no tasks label
--- @param no_tasks_header LuaGuiElement
function Elements.no_tasks_header.refresh(no_tasks_header)
local force = Gui.get_player(no_tasks_header).force --[[ @as LuaForce ]]
no_tasks_header.visible = not Elements.container.has_tasks(force)
end
--- Refresh the visibility of the no tasks label
--- @param player LuaPlayer
function Elements.no_tasks_header.refresh_player(player)
local visible = not Elements.container.has_tasks(player.force --[[ @as LuaForce ]])
for _, no_tasks_header in Elements.no_tasks_header:tracked_elements(player) do
no_tasks_header.visible = visible
end
end
--- Refresh the visibility of the no tasks label
--- @param force LuaForce
function Elements.no_tasks_header.refresh_force_online(force)
local visible = not Elements.container.has_tasks(force)
for _, no_tasks_header in Elements.no_tasks_header:online_elements(force) do
no_tasks_header.visible = visible
end
end
--- A table containing all of the current tasks
--- @class ExpGui_TaskList.elements.task_table: ExpElement
--- @field data table<LuaGuiElement, LuaGuiElement[]>
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
Elements.task_list = Gui.define("task_list/task_list")
:draw(function(_, parent)
local task_list = parent.add{
type = "scroll-pane",
vertical_scroll_policy = "auto",
horizontal_scroll_policy = "never",
style = "scroll_pane_under_subheader",
}
local task_list_style = task_list.style
task_list_style.horizontally_stretchable = true
task_list_style.maximal_height = 224
task_list_style.padding = 0
-- Cant modify vertical spacing on scroll pane style so need a sub flow
task_list = task_list.add{ type = "flow", direction = "vertical" }
task_list_style = task_list.style
task_list_style.horizontally_stretchable = true
task_list_style.vertical_spacing = 0
task_list_style.padding = 0
-- Add the no tasks header
local no_tasks_header = Elements.no_tasks_header(task_list)
Elements.no_tasks_header.refresh(no_tasks_header)
return task_list
end)
:element_data{} --[[ @as any ]]
--- Adds a task to the task list
--- @param task_list LuaGuiElement
--- @param task ExpGui_TaskList.Task
function Elements.task_list.add_task(task_list, task)
local container = assert(task_list.parent.parent.parent)
local view_task_buttons = Elements.task_list.data[task_list]
view_task_buttons[task.id] = Elements.view_task_button(task_list, task, container)
end
--- Remove a task from the task list
--- @param task_list LuaGuiElement
--- @param task ExpGui_TaskList.Task
function Elements.task_list.remove_task(task_list, task)
local view_task_buttons = Elements.task_list.data[task_list]
Gui.destroy_if_valid(view_task_buttons[task.id])
view_task_buttons[task.id] = nil
end
--- Refresh a task on the list
--- @param task_list LuaGuiElement
--- @param task ExpGui_TaskList.Task
function Elements.task_list.refresh_task(task_list, task)
local view_task_buttons = Elements.task_list.data[task_list]
Elements.view_task_button.refresh(view_task_buttons[task.id], task)
end
--- Refresh all tasks on the list
--- @param task_list LuaGuiElement
--- @param tasks ExpGui_TaskList.Task[]
function Elements.task_list.refresh_tasks(task_list, tasks)
local view_task_buttons = Elements.task_list.data[task_list]
local done = {}
-- Refresh all valid tasks
for _, task in ipairs(tasks) do
done[task.id] = true
local view_task_button = view_task_buttons[task.id]
if view_task_button then
Elements.view_task_button.refresh(view_task_button, task)
else
Elements.task_list.add_task(task_list, task)
end
end
-- Remove tasks buttons that are no longer required
for id, view_task_button in pairs(view_task_buttons) do
if not done[id] then
view_task_button.destroy()
end
end
end
--- Select a task button, if nil then all buttons are reset
--- @param task_list LuaGuiElement
--- @param task ExpGui_TaskList.Task?
function Elements.task_list.select_task_button(task_list, task)
local view_task_buttons = Elements.task_list.data[task_list]
local task_button = task and view_task_buttons[task.id]
if task_button and not task_button.enabled then
return
end
for _, view_task_button in pairs(view_task_buttons) do
view_task_button.enabled = true
end
if task_button then
task_button.enabled = false
end
end
--- The base footer used for view and edit
--- @class ExpGui_TaskList.elements.task_footer: ExpElement
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
Elements.task_footer = Gui.define("task_list/task_footer")
:draw{
type = "frame",
direction = "vertical",
style = "subfooter_frame",
}
:style{
height = 0,
padding = 5,
use_header_filler = false,
} --[[ @as any ]]
--- @class ExpGui_TaskList.elements.close_task_button.elements
--- @field container LuaGuiElement
--- Button used to close an open task details
--- @class ExpGui_TaskList.element.close_task_button: ExpElement
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.close_task_button.elements>
--- @overload fun(parent: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
Elements.close_task_button = Gui.define("task_list/close_task_button")
:draw{
type = "sprite-button",
sprite = "utility/collapse",
style = "frame_action_button",
tooltip = { "exp-gui_task-list.tooltip-close" },
}
:style(Styles.sprite22)
:element_data{
container = Gui.from_argument(1),
}
:on_click(function(def, player, close_task_button)
--- @cast def ExpGui_TaskList.element.close_task_button
local elements = def.data[close_task_button]
Elements.container.close_footers(elements.container)
end) --[[ @as any ]]
--- @class ExpGui_TaskList.elements.task_button.elements
--- @field task ExpGui_TaskList.Task?
--- @field container LuaGuiElement
--- Button used to edit a task
--- @class ExpGui_TaskList.element.edit_task_button: ExpElement
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.task_button.elements>
--- @overload fun(parent: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
Elements.edit_task_button = Gui.define("task_list/edit_task_button")
:draw{
type = "button",
caption = { "exp-gui_task-list.caption-edit" },
tooltip = { "exp-gui_task-list.tooltip-edit" },
style = "shortcut_bar_button",
}
:style(Styles.footer_button)
:element_data{
container = Gui.from_argument(1),
}
:on_click(function(def, player, edit_task_button)
--- @cast def ExpGui_TaskList.element.edit_task_button
local elements = def.data[edit_task_button]
Elements.container.open_edit_task(elements.container, assert(elements.task))
end) --[[ @as any ]]
--- Refresh the edit button
--- @param edit_task_button LuaGuiElement
--- @param task ExpGui_TaskList.Task
function Elements.edit_task_button.refresh(edit_task_button, task)
local player = Gui.get_player(edit_task_button)
Elements.edit_task_button.data[edit_task_button].task = task
edit_task_button.visible = has_permission_edit_task(player, task)
if next(task.editing) then
local player_names = table.get_values(task.editing)
edit_task_button.tooltip = { "exp-gui_task-list.tooltip-edit", table.concat(player_names, ", ") }
else
edit_task_button.tooltip = { "exp-gui_task-list.tooltip-edit-none" }
end
end
--- Button used to delete a task
--- @class ExpGui_TaskList.element.delete_task_button: ExpElement
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.task_button.elements>
--- @overload fun(parent: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
Elements.delete_task_button = Gui.define("task_list/delete_task_button")
:draw{
type = "button",
caption = { "exp-gui_task-list.caption-delete" },
tooltip = { "exp-gui_task-list.tooltip-delete" },
style = "shortcut_bar_button_red",
}
:style(Styles.footer_button)
:element_data{
container = Gui.from_argument(1),
}
:on_click(function(def, player, delete_task_button)
--- @cast def ExpGui_TaskList.element.delete_task_button
local elements = def.data[delete_task_button]
Elements.container.remove_task(player.force --[[ @as LuaForce ]], elements.task)
Elements.container.close_footers(elements.container)
end) --[[ @as any ]]
--- Refresh the delete button
--- @param delete_task_button LuaGuiElement
--- @param task ExpGui_TaskList.Task
function Elements.delete_task_button.refresh(delete_task_button, task)
local player = Gui.get_player(delete_task_button)
Elements.delete_task_button.data[delete_task_button].task = task
delete_task_button.visible = has_permission_edit_task(player, task)
end
--- @class ExpGui_TaskList.elements.view_task_footer.elements
--- @field task ExpGui_TaskList.Task?
--- @field body_label LuaGuiElement
--- @field title_label LuaGuiElement
--- @field delete_task_button LuaGuiElement
--- @field edit_task_button LuaGuiElement
--- The view footer used for view task details
--- @class ExpGui_TaskList.elements.view_task_footer: ExpElement
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.view_task_footer.elements>
--- @overload fun(parent: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
Elements.view_task_footer = Gui.define("task_list/view_task_footer")
:draw(function(def, parent, container)
--- @cast def ExpGui_TaskList.elements.view_task_footer
local view_task_footer = Elements.task_footer(parent)
local header_flow = view_task_footer.add{ type = "flow" }
header_flow.add{
type = "label",
style = "frame_title",
caption = { "exp-gui_task-list.caption-view-footer" },
}
header_flow.style.right_padding = 1
header_flow.add{ type = "empty-widget" }.style.horizontally_stretchable = true
Elements.close_task_button(header_flow, container)
local title_label = view_task_footer.add{ type = "label" }
local title_label_style = title_label.style
title_label_style.font = "default-bold"
title_label_style.single_line = false
title_label_style.padding = 4
local body_label = view_task_footer.add{ type = "label" }
body_label.style.single_line = false
body_label.style.padding = 4
local action_flow = view_task_footer.add{ type = "flow" }
local delete_task_button = Elements.delete_task_button(action_flow, container)
local edit_task_button = Elements.edit_task_button(action_flow, container)
def.data[view_task_footer] = {
task = nil,
body_label = body_label,
title_label = title_label,
delete_task_button = delete_task_button,
edit_task_button = edit_task_button,
}
return view_task_footer
end) --[[ @as any ]]
--- Refresh the view task with new task details
--- @param view_task_footer LuaGuiElement
--- @param task ExpGui_TaskList.Task
function Elements.view_task_footer.refresh(view_task_footer, task)
local elements = Elements.view_task_footer.data[view_task_footer]
Elements.delete_task_button.refresh(elements.delete_task_button, task)
Elements.edit_task_button.refresh(elements.edit_task_button, task)
elements.title_label.caption = task.title
elements.body_label.caption = task.body
elements.task = task
end
--- Refresh the view task with previously selected task
--- If task is provided, then will only update if task is the selected task
--- @param view_task_footer LuaGuiElement
--- @param task ExpGui_TaskList.Task?
function Elements.view_task_footer.update(view_task_footer, task)
local elements = Elements.view_task_footer.data[view_task_footer]
if elements.task and (task == nil or task == elements.task) then
Elements.view_task_footer.refresh(view_task_footer, elements.task)
end
end
--- Refresh the view task with the previously selected task
--- @param player LuaPlayer
function Elements.view_task_footer.update_player(player)
for _, view_task_footer in Elements.view_task_footer:tracked_elements(player) do
Elements.view_task_footer.update(view_task_footer)
end
end
--- @class ExpGui_TaskList.elements.task_message_textfield.elements
--- @field confirm_task_button LuaGuiElement?
--- Textfield element used in both the task create and edit footers
--- @class ExpGui_TaskList.elements.task_message_textfield: ExpElement
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.task_message_textfield.elements>
--- @overload fun(parent: LuaGuiElement, confirm_task_button: LuaGuiElement?): LuaGuiElement
Elements.task_message_textfield = Gui.define("task_list/task_message_textfield")
:draw{
type = "text-box",
text = "",
}
:style{
maximal_width = 268,
minimal_height = 100,
horizontally_stretchable = true,
}
:element_data{
confirm_task_button = Gui.from_argument(1),
}
:on_text_changed(function(def, player, task_message_textfield)
--- @cast def ExpGui_TaskList.elements.task_message_textfield
local confirm_task_button = def.data[task_message_textfield].confirm_task_button
confirm_task_button.enabled = string.len(task_message_textfield.text) > 5
end) --[[ @as any ]]
--- Set the confirm task button to update on text changed
--- @param task_message_textfield LuaGuiElement
--- @param confirm_task_button LuaGuiElement
function Elements.task_message_textfield.set_confirm_task_button(task_message_textfield, confirm_task_button)
Elements.task_message_textfield.data[task_message_textfield].confirm_task_button = confirm_task_button
end
--- Refresh the task message field
--- @param task_message_textfield LuaGuiElement
--- @param task ExpGui_TaskList.Task
function Elements.task_message_textfield.refresh(task_message_textfield, task)
local elements = Elements.task_message_textfield.data[task_message_textfield]
local message = task.new and "" or task.title .. "\n" .. task.body
elements.confirm_task_button.enabled = string.len(message) > 5
task_message_textfield.text = message
task_message_textfield.focus()
end
--- @class ExpGui_TaskList.elements.confirm_task_button.elements
--- @field task ExpGui_TaskList.Task?
--- @field task_message_textfield LuaGuiElement
--- @field container LuaGuiElement
--- Textfield element used in both the task create and edit footers
--- @class ExpGui_TaskList.elements.confirm_task_button: ExpElement
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.confirm_task_button.elements>
--- @overload fun(parent: LuaGuiElement, task_message_textfield: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
Elements.confirm_task_button = Gui.define("task_list/confirm_task_button")
:draw{
type = "button",
name = Gui.from_name,
caption = { "exp-gui_task-list.caption-confirm" },
tooltip = { "exp-gui_task-list.tooltip-confirm" },
style = "shortcut_bar_button_green",
}
:style(Styles.footer_button)
:element_data{
task_message_textfield = Gui.from_argument(1),
container = Gui.from_argument(2),
}
:on_click(function(def, player, confirm_task_button)
--- @cast def ExpGui_TaskList.elements.confirm_task_button
local elements = def.data[confirm_task_button]
local task_message_textfield = elements.task_message_textfield
local task = assert(elements.task)
local parsed = Elements.confirm_task_button.parse(task_message_textfield.text)
task.last_edit_tick = game.tick
task.last_user = player
task.title = parsed.title
task.body = parsed.body
Elements.container.close_footers(elements.container)
local force = player.force --[[ @as LuaForce ]]
if task.new then
Elements.container.add_task(force, task)
else
Elements.container.refresh_force_online(force, task)
end
end) --[[ @as any ]]
--- Refresh the confirm task button
--- @param confirm_task_button LuaGuiElement
--- @param task ExpGui_TaskList.Task
function Elements.confirm_task_button.refresh(confirm_task_button, task)
Elements.confirm_task_button.data[confirm_task_button].task = task
end
--- Parse a task message into its two parts
--- @param message string
--- @return { title: string, body: string }
function Elements.confirm_task_button.parse(message)
-- Trim the spaces of the string
local trimmed = string.gsub(message, "^%s*(.-)%s*$", "%1")
local title, body = string.match(trimmed, "(.-)\n(.*)")
local parsed = { title = title, body = body }
if not title then
-- If it doesn't match the pattern return the str as a title
parsed.title = trimmed
parsed.body = ""
end
return parsed
end
--- @class ExpGui_TaskList.elements.discard_task_button.elements
--- @field task ExpGui_TaskList.Task?
--- @field container LuaGuiElement
--- Button used to close an open task details
--- @class ExpGui_TaskList.element.discard_task_button: ExpElement
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.discard_task_button.elements>
--- @overload fun(parent: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
Elements.discard_task_button = Gui.define("task_list/discard_task_button")
:draw{
type = "button",
caption = { "exp-gui_task-list.caption-discard" },
tooltip = { "exp-gui_task-list.tooltip-discard" },
style = "shortcut_bar_button_red",
}
:style(Styles.footer_button)
:element_data{
container = Gui.from_argument(1),
}
:on_click(function(def, player, discard_task_button)
--- @cast def ExpGui_TaskList.element.discard_task_button
local elements = def.data[discard_task_button]
local task = assert(elements.task)
if task.new then
Elements.container.close_footers(elements.container)
else
Elements.container.open_view_task(elements.container, task)
end
end) --[[ @as any ]]
--- Refresh the discard task button
--- @param discard_task_button LuaGuiElement
--- @param task ExpGui_TaskList.Task
function Elements.discard_task_button.refresh(discard_task_button, task)
Elements.discard_task_button.data[discard_task_button].task = task
end
--- @class ExpGui_TaskList.elements.edit_task_footer.elements
--- @field task ExpGui_TaskList.Task?
--- @field header LuaGuiElement
--- @field task_message_textfield LuaGuiElement
--- @field discard_task_button LuaGuiElement
--- @field confirm_task_button LuaGuiElement
--- The view footer used for view task details
--- @class ExpGui_TaskList.elements.edit_task_footer: ExpElement
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.edit_task_footer.elements>
--- @overload fun(parent: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
Elements.edit_task_footer = Gui.define("task_list/edit_task_footer")
:draw(function(def, parent, container)
--- @cast def ExpGui_TaskList.elements.edit_task_footer
local edit_task_footer = Elements.task_footer(parent)
local header = edit_task_footer.add{
type = "label",
style = "frame_title",
caption = { "exp-gui_task-list.caption-edit-footer" },
}
local task_message_textfield = Elements.task_message_textfield(edit_task_footer)
local action_flow = edit_task_footer.add{ type = "flow" }
local discard_task_button = Elements.discard_task_button(action_flow, container)
local confirm_task_button = Elements.confirm_task_button(action_flow, task_message_textfield, container)
Elements.task_message_textfield.set_confirm_task_button(task_message_textfield, confirm_task_button)
def.data[edit_task_footer] = {
task = nil,
header = header,
task_message_textfield = task_message_textfield,
discard_task_button = discard_task_button,
confirm_task_button = confirm_task_button,
}
return edit_task_footer
end) --[[ @as any ]]
--- Refresh the view task with new task details
--- @param edit_task_footer LuaGuiElement
--- @param task ExpGui_TaskList.Task
function Elements.edit_task_footer.refresh(edit_task_footer, task)
local player = Gui.get_player(edit_task_footer)
local elements = Elements.edit_task_footer.data[edit_task_footer]
task.editing[player.index] = player.name
if elements.task and elements.task ~= task then
elements.task.editing[player.index] = nil
end
elements.header.caption = {
task.new and "exp-gui_task-list.caption-create-footer" or "exp-gui_task-list.caption-edit-footer"
}
Elements.task_message_textfield.refresh(elements.task_message_textfield, task)
Elements.confirm_task_button.refresh(elements.confirm_task_button, task)
Elements.discard_task_button.refresh(elements.discard_task_button, task)
elements.task = task
end
--- Refresh the view task with previously selected task
--- If task is provided, then will only update if task is the selected task
--- @param edit_task_footer LuaGuiElement
--- @param task ExpGui_TaskList.Task?
function Elements.edit_task_footer.update(edit_task_footer, task)
local elements = Elements.edit_task_footer.data[edit_task_footer]
if elements.task and (task == nil or task == elements.task) then
Elements.edit_task_footer.refresh(edit_task_footer, elements.task)
end
end
--- Refresh the view task with the previously selected task
--- @param player LuaPlayer
function Elements.edit_task_footer.update_player(player)
for _, edit_task_footer in Elements.edit_task_footer:tracked_elements(player) do
Elements.edit_task_footer.update(edit_task_footer)
end
end
--- Clear the previously edited task
--- @param edit_task_footer LuaGuiElement
function Elements.edit_task_footer.clear(edit_task_footer)
local elements = Elements.edit_task_footer.data[edit_task_footer]
if elements.task then
local player = Gui.get_player(edit_task_footer)
elements.task.editing[player.index] = nil
elements.task = nil
end
end
--- @class ExpGui_TaskList.elements.container.elements
--- @field task_list LuaGuiElement
--- @field view_task_footer LuaGuiElement
--- @field edit_task_footer LuaGuiElement
--- Container added to the left gui flow
--- @class ExpGui_TaskList.elements.container: ExpElement
--- @field data table<LuaForce, ExpGui_TaskList.Task[]> | table<LuaGuiElement, ExpGui_TaskList.elements.container.elements> | table<"global_data", { next_task_id: number }>
Elements.container = Gui.define("task_list/container")
:track_all_elements()
:draw(function(def, parent)
--- @cast def ExpGui_TaskList.elements.container
local container = Gui.elements.container(parent) -- width 268
local root = Gui.elements.container.get_root_element(container)
local elements = {}
-- Add the header
local header = Gui.elements.header(container, {
caption = { "exp-gui_task-list.caption-main" },
tooltip = { "exp-gui_task-list.tooltip-sub" },
})
-- Add buttons to the header
local new_task_button = Elements.new_task_button(header, root)
Elements.new_task_button.refresh(new_task_button)
-- Add the task table and footers
elements.task_list = Elements.task_list(container)
-- Add tasks to the list if there are any
local player = Gui.get_player(parent)
local force = player.force --[[ @as LuaForce ]]
local tasks = def.data[force]
if tasks then
for _, task in ipairs(tasks) do
Elements.task_list.add_task(elements.task_list, task)
end
end
-- Add the footers
elements.view_task_footer = Elements.view_task_footer(container, root)
elements.edit_task_footer = Elements.edit_task_footer(container, root)
elements.view_task_footer.visible = false
elements.edit_task_footer.visible = false
-- Set the data and return
def.data[root] = elements --[[ @as any ]]
return root
end)
:global_data{ next_task_id = 1 }
:force_data{} --[[ @as any ]]
--- Check if a force has tasks
--- @param force LuaForce
--- @return boolean
function Elements.container.has_tasks(force)
local tasks = Elements.container.data[force]
return tasks and #tasks > 0
end
--- Get the next task id
--- @return number
function Elements.container.next_task_id()
local next_task_id = Elements.container.data.global_data.next_task_id
Elements.container.data.global_data.next_task_id = next_task_id + 1
return next_task_id
end
--- Add a new task for a force
--- @param force LuaForce
--- @param task ExpGui_TaskList.Task
function Elements.container.add_task(force, task)
local tasks = Elements.container.data[force]
tasks[#tasks + 1] = task
task.deleted = nil
task.new = nil
Elements.no_tasks_header.refresh_force_online(force)
for _, container in Elements.container:online_elements(force) do
local task_list = Elements.container.data[container].task_list
Elements.task_list.add_task(task_list, task)
end
end
--- Remove a task from a force
--- @param force LuaForce
--- @param task ExpGui_TaskList.Task
function Elements.container.remove_task(force, task)
local tasks = Elements.container.data[force]
table.remove_element(tasks, task)
task.deleted = true
Elements.no_tasks_header.refresh_force_online(force)
for _, container in Elements.container:online_elements(force) do
local task_list = Elements.container.data[container].task_list
Elements.task_list.remove_task(task_list, task)
end
end
--- Open the view footer
--- @param container LuaGuiElement
--- @param task ExpGui_TaskList.Task
function Elements.container.open_view_task(container, task)
local elements = Elements.container.data[container]
Elements.view_task_footer.refresh(elements.view_task_footer, task)
Elements.edit_task_footer.clear(elements.edit_task_footer)
Elements.task_list.select_task_button(elements.task_list, task)
elements.view_task_footer.visible = true
elements.edit_task_footer.visible = false
end
--- Open the edit footer
--- @param container LuaGuiElement
--- @param task ExpGui_TaskList.Task
function Elements.container.open_edit_task(container, task)
local elements = Elements.container.data[container]
Elements.edit_task_footer.refresh(elements.edit_task_footer, task)
Elements.task_list.select_task_button(elements.task_list, task)
elements.edit_task_footer.visible = true
elements.view_task_footer.visible = false
end
--- Close the footers
--- @param container LuaGuiElement
function Elements.container.close_footers(container)
local elements = Elements.container.data[container]
Elements.edit_task_footer.clear(elements.edit_task_footer)
Elements.task_list.select_task_button(elements.task_list)
elements.view_task_footer.visible = false
elements.edit_task_footer.visible = false
end
--- Refresh all tasks for a player
--- @param player LuaPlayer
function Elements.container.refresh_player(player)
local tasks = Elements.container.data[ player.force --[[ @as LuaForce ]] ]
for _, container in Elements.container:tracked_elements(player) do
local elements = Elements.container.data[container]
Elements.task_list.refresh_tasks(elements.task_list, tasks)
Elements.view_task_footer.update(elements.view_task_footer)
Elements.edit_task_footer.update(elements.edit_task_footer)
end
end
--- Refresh a tasks for a force
--- @param force LuaForce
--- @param task ExpGui_TaskList.Task
function Elements.container.refresh_force_online(force, task)
for _, container in Elements.container:online_elements(force) do
local elements = Elements.container.data[container]
Elements.task_list.refresh_task(elements.task_list, task)
Elements.view_task_footer.update(elements.view_task_footer, task)
Elements.edit_task_footer.update(elements.edit_task_footer, task)
end
end
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(Elements.container, function(player)
return Elements.container.has_tasks(player.force --[[ @as LuaForce ]])
end)
Gui.toolbar.create_button{
name = "toggle_task_list",
left_element = Elements.container,
sprite = "utility/not_enough_repair_packs_icon",
tooltip = { "exp-gui_task-list.tooltip-main" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/task-list")
end
}
--- Update the gui when the player joins because it is likely to be outdated
--- @param event EventData.on_player_joined_game
local function refresh_player_tasks(event)
local player = Gui.get_player(event)
Elements.container.refresh_player(player)
Elements.no_tasks_header.refresh_player(player)
end
--- Update the gui when the player joins because it is likely to be outdated
--- @param event EventData.on_player_joined_game
local function refresh_player_permissions(event)
local player = Gui.get_player(event)
Elements.new_task_button.refresh_player(player)
Elements.view_task_footer.update_player(player)
Elements.edit_task_footer.update_player(player)
end
local e = defines.events
return {
elements = Elements,
events = {
[e.on_player_joined_game] = refresh_player_tasks,
[e.on_player_changed_force] = refresh_player_tasks,
[Roles.events.on_role_assigned] = refresh_player_permissions,
[Roles.events.on_role_unassigned] = refresh_player_permissions,
}
}
+96
View File
@@ -346,6 +346,80 @@ tooltip-trains=Set all trains to automatic
caption-waterfill=Waterfill
tooltip-waterfill=Change tiles to water
[exp-gui_readme]
main-tooltip=Information
welcome-tab=Welcome
welcome-tooltip=Welcome to Explosive Gaming
welcome-title=Welcome to __1__
welcome-general=Welcome to Explosive Gaming; we host many Factorio servers. While you are here, we ask you to follow our rules. You can find these in the tab above. You can also find our custom commands and links to our other servers. This map has been online for __2__.\nPlease note that our servers reset periodically, the next reset is: __1__
welcome-roles=We run a custom role system to help protect the work of others. As a result you may not be able to use your deconstruction planner yet or drop items on the ground. Roles also give you access to some custom features such as adding tasks to our task list or making new warp points.\nYou have been assigned the roles: __1__
welcome-chat=Chatting can be difficult for new players because its different than other games! Its very simple, the button you need to press is the “GRAVE/TILDE” key (which is located under the “ESC key”). If you would like to change the key, go to your Controls tab in options.\nThe setting you need to change is “Toggle chat (and Lua console)” you currently have it set to "__CONTROL__toggle-console__"
rules-tab=Rules
rules-tooltip=Rules for our server
rules-general=By playing on our servers you accept the rules below. If you are suspected of breaking a rule then you will be questioned by one of our moderators. If required you will be banned from our servers. Appeals can be made by contacting an Administrator on our Discord.
rules-1=Hacking / cheating / abusing bugs will not be tolerated.
rules-2=Any bugs or exploits found should be reported to our staff members.
rules-3=Be respectful to other players in chat, this includes any other forms of communication.
rules-4=Taking all items from a belt or logistics network is forbidden: sharing resources is mandatory.
rules-5=Spamming of chat, bots, unlimited chests, or concrete is not allowed.
rules-6=Before removing major parts of the factory tell other players why you are removing it.
rules-7=Do not cause unnecessary lag by placing/removing tiles with bots or using active provider chests. If you think something might cause lag please ask staff first.
rules-8=Do not walk in random directions with no reason, this is to reduce map download times.
rules-9=Do not use speakers on global or with alerts without prior permission. No one wants to hear your music at full volume without knowing where the off switch is.
rules-10=Do not rotate belts, deactivate belts with wires, or cause production to stop without good reason. This goes for inserters and splitters as well.
rules-11=Do not make train roundabouts. Other loops such as RoRo stations are allowed.
rules-12=When using trains, use the same size other players have used. Many players use 1-2-1, 2-4-2, or 3-8-3, meaning 1 engine 2 cargo 1 engine.
rules-13=Trains are Left Hand Drive (LHD) only, this means trains drive on the left side of the tracks.
rules-14=Do not beg for roles, advertise other servers, or link to harmful sites.
rules-15=Use common sense, report rule breakers, and Administrators have the final word.
commands-tab=Commands
commands-tooltip=Commands which you are able to use
commands-general=We have lots of custom commands which you are able to use. Below you can find a list of all the commands that you are allowed to use and what they do. If you need more information or want to search for a command you can use our /search-help command.
servers-tab=Servers
servers-tooltip=Links to our other servers and sites
servers-general=This is only one of our Factorio servers, we host many others as well. Below you can find details about all the servers that we host as well as links to our external services such as Discord or GitHub.
servers-factorio=Factorio Servers
servers-external=External Links
servers-open-in-browser=Open in your browser
servers-1=S1 Public
servers-d1=This is our 48 hour reset server.
servers-2=S2 Off
servers-d2=This server is closed.
servers-3=S3 Weekly
servers-d3=This is our weekly reset server.
servers-4=S4 Monthly
servers-d4=This is our monthly reset server.
servers-5=S5 Modded
servers-d5=This is our modded server, see Discord for details.
servers-6=S6 Donator
servers-d6=This is our donator only server, online when requested.
servers-7=S7 Event
servers-d7=This is our event server, we try to run events at least once per week.
servers-8=S8 T̷-̶s̶-̴:̷
servers-d8=N̵o̴ ̶o̷-̶e̵ ̴k̸n̷-̶w̵s̸ ̴w̷h̷a̶-̶ ̷h̴a̴p̷p̴e̷n̷s̸ ̷o̶n̴ ̷t̶h̴-̶s̶ ̷s̷e̶r̸v̸e̴r̷,̶ ̸i̸t̴ ̷m̶-̸g̴h̶t̷ ̸n̸-̶t̵ ̷e̴v̸e̸n̶t̷ ̵-̷x̴i̵s̶t̸.̸
servers-connect-Offline=Server is currently offline
servers-connect-Current=This is your current server
servers-connect-Version=Server is on a different version: __1__
servers-connect-Password=Server requires a password
servers-connect-Modded=Server requires mods to be downloaded
servers-connect-Online=Server is online
backers-tab=Backers
backers-tooltip=People who have helped make our server
backers-general=We would like to thank all our staff and backers who have helped our community grow. Our staff have helped to keep our servers safe from trolls and a fun place to play. Our backers have helped us to cover our running costs and provide a great community for us all to enjoy together.
backers-management=Administrators
backers-board=Board Members and Senior Backers
backers-staff=Staff Members
backers-backers=Sponsors and Supporters
backers-active=Active Players
data-tab=Data
data-tooltip=All of your stored player data
data-general=Our servers will save your player data so that we can sync it between servers. All of your data can be found below. If you wish to save a copy locally then use /save-data. If you want to change what data is saved then use /set-preference.
data-required=Required
data-settings=Settings
data-statistics=Statistics
data-misc=Miscellaneous
data-format=__1____2__
[exp-gui_research-milestones]
tooltip-main=Research Milestones
caption-main=Milestones
@@ -376,6 +450,28 @@ type-player=Player
type-static=Static
type-loop=Loop
[exp-gui_task-list]
caption-main=Task List [img=info]
tooltip-main=Task List
tooltip-sub=Tasks that remain to be done\n- You can use richtext to include images [img=utility/not_enough_repair_packs_icon] or [color=blue]color[/color] your tasks.
caption-no-tasks=No tasks found!
tooltip-no-tasks=Click on the plus button to the top right of this window to add a new task!
tooltip-last-edit=Last edited by __1__ at __2__
tooltip-close=Close task details
tooltip-new=Add new task
caption-confirm=[img=utility/check_mark] Confirm
tooltip-confirm=Save changes (minimum of 5 characters long)
caption-discard=[img=utility/close_black] Discard
tooltip-discard=Discard changes
caption-delete=[img=utility/trash] Delete
tooltip-delete=Delete task
caption-edit=[img=utility/rename_icon] Edit
tooltip-edit=Currently being edited by: __1__
tooltip-edit-none=Currently being edited by: Nobody
caption-create-footer=Create task
caption-edit-footer=Edit task
caption-view-footer=Task details
[exp_afk-kick]
kick-message=All players were kicked because everyone was AFK.
+95
View File
@@ -346,6 +346,79 @@ tooltip-trains=把火車設置為自動模式
caption-waterfill=挖水
tooltip-waterfill=把地換為水。
[exp-gui_readme]
main-tooltip=資訊
welcome-tab=歡迎
welcome-tooltip=歡迎來到 Explosive Gaming
welcome-general=歡迎來到 Explosive Gaming; 你在這裏的時候要遵守規則。 你可以在左上方的介面找到更多資訊。 地圖時間 __2__。\n下次地圖重設: __1__
welcome-roles=我們有自訂的身份組來保護其他用戶。 所以你有機會不能使用拆除規劃器或掉東西。 身份組給予你各種權限\n你有以下身份組: __1__
welcome-chat=你可以使用 「重音符/抑音符」 鍵 (ESC 鍵 下方) - 你也可以在選項中修改按鍵。\n你目前設定為 「__CONTROL__toggle-console__」
rules-tab=規則
rules-tooltip=服務器規則
rules-general=你在本服務器遊玩即等同同意以下規則。管理員有機會就相關事項提問。你可能會因為違反規則而被封禁,你可以在 Discord 中提出反對。
rules-1=開掛或濫用漏洞將不會被容忍。
rules-2=當你找到漏洞可以通知職員。
rules-3=請在聊天室中尊重其他人。
rules-4=請不要把所有物品全部取走。
rules-5=請不要濫發信息,大量鋪設地磚,或不為箱子設置上限,浪費資源。
rules-6=當你想大量拆除原有設施前請通知其他用戶。
rules-7=請不要無故使用機器人來大量鋪設或拆除地磚,濫用主動出貨箱。
rules-8=請不要隨意移動來開大量地圖,這有助減少地圖下載時間。
rules-9=請不要無故使用全圖喇叭。
rules-10=請不要無故轉動傳送帶方向,停用傳送帶,或停止生產。
rules-11=請不要用迴旋處。 火車站尾返程則是例外。
rules-12=請跟其他人使用相同的火車組成。常用的有 1-2-1,2-4-2,或 3-8-3,即1個動車組拖2個貨車組再拖不同方向的1個動車組。
rules-13=火車必須是左上右下 (LHD)。
rules-14=禁止要求身份組,發廣告,發惡意連結。
rules-15=使用常識, 舉報違規人員,管理員有最終決定權。
commands-tab=指令
commands-tooltip=你可用的指令
commands-general=這裏有自訂的指令。你可以在下邊找到你可用的指令及其功能介紹。你可以用 /search-help 來取得更多資訊。
servers-tab=伺服器
servers-tooltip=到其他伺服器及網站的連結
servers-general=這是其中一個伺服器。你可以在下方找到其他同樣是由我們運行的伺服器資料。
servers-factorio=異星工廠伺服器
servers-1=S1 公共
servers-d1=這個服務器每兩日重設。
servers-2=S2 -
servers-d2=這個服務器沒在營運。
servers-3=S3 周
servers-d3=這個服務器每週重設。
servers-4=S4 月
servers-d4=這個服務器每月重設。
servers-5=S5 模組
servers-d5=這個服務器運行模組,可在 Discord 中得到更多資訊。
servers-6=S6 Donator
servers-d6=這個服務器為支持者運行,可在 Discord 中得到更多資訊。
servers-7=S7 項目
servers-d7=這個服務器只在有項目期間運行。
servers-8=S8 T̷-̶s̶-̴:̷
servers-d8=N̵o̴ ̶o̷-̶e̵ ̴k̸n̷-̶w̵s̸ ̴w̷h̷a̶-̶ ̷h̴a̴p̷p̴e̷n̷s̸ ̷o̶n̴ ̷t̶h̴-̶s̶ ̷s̷e̶r̸v̸e̴r̷,̶ ̸i̸t̴ ̷m̶-̸g̴h̶t̷ ̸n̸-̶t̵ ̷e̴v̸e̸n̶t̷ ̵-̷x̴i̵s̶t̸。̸
servers-connect-Offline=該服務器目前沒有在運行
servers-connect-Current=你目前所在的伺服器
servers-connect-Version=該服務器運行不同的遊戲版本: __1__
servers-connect-Password=該服務器需要密碼
servers-connect-Modded=該服務器需要下載模組
servers-connect-Online=該服務器在線
servers-external=外部連結
servers-open-in-browser=可在你的瀏覽器中打開
backers-tab=支持者
backers-tooltip=支持者
backers-general=他們為服務器運行提供了不少幫助。
backers-management=系統管理員
backers-board=議員
backers-staff=職員
backers-backers=支持者
backers-active=活躍玩家
data-tab=資料
data-tooltip=所有已存資料
data-general=資料會自動存到各個服務器中。 你也可以用 /save-data 存一份副本。 也可用 /set-preference 來更改喜好。
data-settings=設定
data-statistics=數據
data-required=需要
data-misc=雜項
data-format=__1____2__
[exp-gui_research-milestones]
tooltip-main=研究介面
caption-main=研究介面
@@ -376,6 +449,28 @@ type-player=用戶
type-static=靜態
type-loop=循環
[exp-gui_task-list]
caption-main=工作流程 [img=info]
tooltip-main=工作流程
tooltip-sub=需要完成的工作流程\n- 你可以用富文本來加上圖片 [img=utility/not_enough_repair_packs_icon] 或 [color=blue] 顏色[/color]。
caption-no-tasks=沒有工作流程
tooltip-no-tasks=按加號加入工作流程
tooltip-last-edit=最後由 __1__ 在 __2__ 修改
tooltip-close=關閉工作流程
tooltip-new=加入工作流程
caption-confirm=[img=utility/check_mark] 確認
confirm-tooltip=儲存工作流程 (最少要5個字長)
caption-discard=[img=utility/close_black] 放棄
tooltip-discard=放棄更改動
caption-delete=[img=utility/trash] 刪除
tooltip-delete=刪除工作流程
caption-edit=[img=utility/rename_icon] 修改工作流程
tooltip-edit=現被 __1__ 修改中
tooltip-edit-none=現沒有被人修改
caption-create-footer=加入工作流程
caption-edit-footer=修改工作流程
caption-view-footer=工作流程細節
[exp_afk-kick]
kick-message=因地圖中沒有活躍玩家,所以所有人都已被請離。
+95
View File
@@ -346,6 +346,79 @@ tooltip-trains=把火車設置為自動模式
caption-waterfill=挖水
tooltip-waterfill=把地換為水。
[exp-gui_readme]
main-tooltip=資訊
welcome-tab=歡迎
welcome-tooltip=歡迎來到 Explosive Gaming
welcome-general=歡迎來到 Explosive Gaming; 你在這裏的時候要遵守規則。 你可以在左上方的介面找到更多資訊。 地圖時間 __2__。\n下次地圖重設: __1__
welcome-roles=我們有自訂的身份組來保護其他用戶。 所以你有機會不能使用拆除規劃器或掉東西。 身份組給予你各種權限\n你有以下身份組: __1__
welcome-chat=你可以使用 「重音符/抑音符」 鍵 (ESC 鍵 下方) - 你也可以在選項中修改按鍵。\n你目前設定為 「__CONTROL__toggle-console__」
rules-tab=規則
rules-tooltip=服務器規則
rules-general=你在本服務器遊玩即等同同意以下規則。管理員有機會就相關事項提問。你可能會因為違反規則而被封禁,你可以在 Discord 中提出反對。
rules-1=開掛或濫用漏洞將不會被容忍。
rules-2=當你找到漏洞可以通知職員。
rules-3=請在聊天室中尊重其他人。
rules-4=請不要把所有物品全部取走。
rules-5=請不要濫發信息,大量鋪設地磚,或不為箱子設置上限,浪費資源。
rules-6=當你想大量拆除原有設施前請通知其他用戶。
rules-7=請不要無故使用機器人來大量鋪設或拆除地磚,濫用主動出貨箱。
rules-8=請不要隨意移動來開大量地圖,這有助減少地圖下載時間。
rules-9=請不要無故使用全圖喇叭。
rules-10=請不要無故轉動傳送帶方向,停用傳送帶,或停止生產。
rules-11=請不要用迴旋處。 火車站尾返程則是例外。
rules-12=請跟其他人使用相同的火車組成。常用的有 1-2-1,2-4-2,或 3-8-3,即1個動車組拖2個貨車組再拖不同方向的1個動車組。
rules-13=火車必須是左上右下 (LHD)。
rules-14=禁止要求身份組,發廣告,發惡意連結。
rules-15=使用常識, 舉報違規人員,管理員有最終決定權。
commands-tab=指令
commands-tooltip=你可用的指令
commands-general=這裏有自訂的指令。你可以在下邊找到你可用的指令及其功能介紹。你可以用 /search-help 來取得更多資訊。
servers-tab=伺服器
servers-tooltip=到其他伺服器及網站的連結
servers-general=這是其中一個伺服器。你可以在下方找到其他同樣是由我們運行的伺服器資料。
servers-factorio=異星工廠伺服器
servers-1=S1 公共
servers-d1=這個服務器每兩日重設。
servers-2=S2 -
servers-d2=這個服務器沒在營運。
servers-3=S3 周
servers-d3=這個服務器每週重設。
servers-4=S4 月
servers-d4=這個服務器每月重設。
servers-5=S5 模組
servers-d5=這個服務器運行模組,可在 Discord 中得到更多資訊。
servers-6=S6 Donator
servers-d6=這個服務器為支持者運行,可在 Discord 中得到更多資訊。
servers-7=S7 項目
servers-d7=這個服務器只在有項目期間運行。
servers-8=S8 T̷-̶s̶-̴:̷
servers-d8=N̵o̴ ̶o̷-̶e̵ ̴k̸n̷-̶w̵s̸ ̴w̷h̷a̶-̶ ̷h̴a̴p̷p̴e̷n̷s̸ ̷o̶n̴ ̷t̶h̴-̶s̶ ̷s̷e̶r̸v̸e̴r̷,̶ ̸i̸t̴ ̷m̶-̸g̴h̶t̷ ̸n̸-̶t̵ ̷e̴v̸e̸n̶t̷ ̵-̷x̴i̵s̶t̸。̸
servers-connect-Offline=該服務器目前沒有在運行
servers-connect-Current=你目前所在的伺服器
servers-connect-Version=該服務器運行不同的遊戲版本: __1__
servers-connect-Password=該服務器需要密碼
servers-connect-Modded=該服務器需要下載模組
servers-connect-Online=該服務器在線
servers-external=外部連結
servers-open-in-browser=可在你的瀏覽器中打開
backers-tab=支持者
backers-tooltip=支持者
backers-general=他們為服務器運行提供了不少幫助。
backers-management=系統管理員
backers-board=議員
backers-staff=職員
backers-backers=支持者
backers-active=活躍玩家
data-tab=資料
data-tooltip=所有已存資料
data-general=資料會自動存到各個服務器中。 你也可以用 /save-data 存一份副本。 也可用 /set-preference 來更改喜好。
data-settings=設定
data-statistics=數據
data-required=需要
data-misc=雜項
data-format=__1____2__
[exp-gui_research-milestones]
tooltip-main=研究介面
caption-main=研究介面
@@ -376,6 +449,28 @@ type-player=用戶
type-static=靜態
type-loop=循環
[exp-gui_task-list]
caption-main=工作流程 [img=info]
tooltip-main=工作流程
tooltip-sub=需要完成的工作流程\n- 你可以用富文本來加上圖片 [img=utility/not_enough_repair_packs_icon] 或 [color=blue] 顏色[/color]。
caption-no-tasks=沒有工作流程
tooltip-no-tasks=按加號加入工作流程
tooltip-last-edit=最後由 __1__ 在 __2__ 修改
tooltip-close=關閉工作流程
tooltip-new=加入工作流程
caption-confirm=[img=utility/check_mark] 確認
confirm-tooltip=儲存工作流程 (最少要5個字長)
caption-discard=[img=utility/close_black] 放棄
tooltip-discard=放棄更改動
caption-delete=[img=utility/trash] 刪除
tooltip-delete=刪除工作流程
caption-edit=[img=utility/rename_icon] 修改工作流程
tooltip-edit=現被 __1__ 修改中
tooltip-edit-none=現沒有被人修改
caption-create-footer=加入工作流程
caption-edit-footer=修改工作流程
caption-view-footer=工作流程細節
[exp_afk-kick]
kick-message=因地圖中沒有活躍玩家,所以所有人都已被請離。
-68
View File
@@ -1,68 +0,0 @@
import React, {
useContext, useEffect, useState,
useCallback, useSyncExternalStore,
} from "react";
// import {
//
// } from "antd";
import {
BaseWebPlugin, PageLayout, PageHeader, Control, ControlContext, notifyErrorHandler,
} from "@clusterio/web_ui";
import {
PluginExampleEvent, PluginExampleRequest,
ExampleSubscribableUpdate, ExampleSubscribableValue,
} from "../messages";
import * as lib from "@clusterio/lib";
function MyTemplatePage() {
const control = useContext(ControlContext);
const plugin = control.plugins.get("exp_scenario") as WebPlugin;
const [subscribableData, synced] = plugin.useSubscribableData();
return <PageLayout nav={[{ name: "exp_scenario" }]}>
<PageHeader title="exp_scenario" />
Synced: {String(synced)} Data: {JSON.stringify([...subscribableData.values()])}
</PageLayout>;
}
export class WebPlugin extends BaseWebPlugin {
subscribableData = new lib.EventSubscriber(ExampleSubscribableUpdate, this.control);
async init() {
this.pages = [
{
path: "/exp_scenario",
sidebarName: "exp_scenario",
// This permission is client side only, so it must match the permission string of a resource request to be secure
// An undefined value means that the page will always be visible
permission: "exp_scenario.example.permission.subscribe",
content: <MyTemplatePage/>,
},
];
this.control.handle(PluginExampleEvent, this.handlePluginExampleEvent.bind(this));
this.control.handle(PluginExampleRequest, this.handlePluginExampleRequest.bind(this));
}
useSubscribableData() {
const control = useContext(ControlContext);
const subscribe = useCallback((callback: () => void) => this.subscribableData.subscribe(callback), [control]);
return useSyncExternalStore(subscribe, () => this.subscribableData.getSnapshot());
}
async handlePluginExampleEvent(event: PluginExampleEvent) {
this.logger.info(JSON.stringify(event));
}
async handlePluginExampleRequest(request: PluginExampleRequest) {
this.logger.info(JSON.stringify(request));
return {
myResponseString: request.myString,
myResponseNumbers: request.myNumberArray,
};
}
}
+68
View File
@@ -0,0 +1,68 @@
import React, {
useContext, useEffect, useState,
useCallback, useSyncExternalStore,
} from "react";
// import {
//
// } from "antd";
import {
BaseWebPlugin, PageLayout, PageHeader, Control, ControlContext, notifyErrorHandler,
} from "@clusterio/web_ui";
import {
PluginExampleEvent, PluginExampleRequest,
ExampleSubscribableUpdate, ExampleSubscribableValue,
} from "../messages";
import * as lib from "@clusterio/lib";
function MyTemplatePage() {
const control = useContext(ControlContext);
const plugin = control.plugins.get("exp_scenario") as WebPlugin;
const [subscribableData, synced] = plugin.useSubscribableData();
return <PageLayout nav={[{ name: "exp_scenario" }]}>
<PageHeader title="exp_scenario" />
Synced: {String(synced)} Data: {JSON.stringify([...subscribableData.values()])}
</PageLayout>;
}
export class WebPlugin extends BaseWebPlugin {
subscribableData = new lib.EventSubscriber(ExampleSubscribableUpdate, this.control);
async init() {
this.pages = [
{
path: "/exp_scenario",
sidebarName: "exp_scenario",
// This permission is client side only, so it must match the permission string of a resource request to be secure
// An undefined value means that the page will always be visible
permission: "exp_scenario.example.permission.subscribe",
content: <MyTemplatePage/>,
},
];
this.control.handle(PluginExampleEvent, this.handlePluginExampleEvent.bind(this));
this.control.handle(PluginExampleRequest, this.handlePluginExampleRequest.bind(this));
}
useSubscribableData() {
const control = useContext(ControlContext);
const subscribe = useCallback((callback: () => void) => this.subscribableData.subscribe(callback), [control]);
return useSyncExternalStore(subscribe, () => this.subscribableData.getSnapshot());
}
async handlePluginExampleEvent(event: PluginExampleEvent) {
this.logger.info(JSON.stringify(event));
}
async handlePluginExampleRequest(request: PluginExampleRequest) {
this.logger.info(JSON.stringify(request));
return {
myResponseString: request.myString,
myResponseNumbers: request.myNumberArray,
};
}
}