This commit is contained in:
2026-07-19 13:22:02 +09:00
63 changed files with 1391 additions and 3103 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json", "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
"workspace.ignoreDir": [ "workspace.ignoreDir": [
".vscode", ".github", "luals", "factorio" ".vscode", ".github", "luals", "factorio", "web"
], ],
"completion.requireSeparator": "/", "completion.requireSeparator": "/",
"runtime.pluginArgs": [ "runtime.pluginArgs": [
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@expcluster/lib_commands", "name": "@expcluster/lib_commands",
"version": "0.1.0", "version": "6.5.0",
"description": "Clusterio plugin providing a Lua command processing library.", "description": "Clusterio plugin providing a Lua command processing library.",
"author": "Cooldude2606 <https://github.com/Cooldude2606>", "author": "Cooldude2606 <https://github.com/Cooldude2606>",
"license": "MIT", "license": "MIT",
+2 -2
View File
@@ -191,8 +191,8 @@ export class ControllerPlugin extends BaseControllerPlugin {
async handleResolvedAssignmentSubscription(request: lib.SubscriptionRequest) { async handleResolvedAssignmentSubscription(request: lib.SubscriptionRequest) {
// Check for any missing assignments to be computed on demand // Check for any missing assignments to be computed on demand
const filters = Array.isArray(request.filters) ? request.filters : [request.filters!]; const filters = request.filters.toJSON();
if (request.filters && filters.length) { if (filters.length) {
const missing = filters.filter(name => !this.resolvedAssignments.has(name)); const missing = filters.filter(name => !this.resolvedAssignments.has(name));
if (missing.length) { if (missing.length) {
this.resolvedAssignments.setMany(await this.computeResolvedAssignments(missing)); this.resolvedAssignments.setMany(await this.computeResolvedAssignments(missing));
+4 -4
View File
@@ -45,7 +45,7 @@ export class InstancePlugin extends BaseInstancePlugin {
async onStart() { async onStart() {
// We use Date.now() because we need to manually initialise the groups on the lua side // We use Date.now() because we need to manually initialise the groups on the lua side
await this.instance.sendTo("controller", new lib.SubscriptionRequest( await this.instance.sendTo("controller", new lib.SubscriptionRequest(
`exp_groups:${messages.GroupUpdatedEvent.name}`, true, Date.now() `exp_groups:${messages.GroupUpdatedEvent.name}`, "subscribe", Date.now()
)); ));
const groups = await this.instance.sendTo("controller", new messages.GroupListRequest()) const groups = await this.instance.sendTo("controller", new messages.GroupListRequest())
await this.luaSendInitialGroups(groups); await this.luaSendInitialGroups(groups);
@@ -118,13 +118,13 @@ export class InstancePlugin extends BaseInstancePlugin {
async subscribePlayerAssignment(playerName: string) { async subscribePlayerAssignment(playerName: string) {
await this.instance.sendTo("controller", new lib.SubscriptionRequest( await this.instance.sendTo("controller", new lib.SubscriptionRequest(
`exp_groups:${messages.ResolvedAssignmentUpdatedEvent.name}`, true, 0, playerName `exp_groups:${messages.ResolvedAssignmentUpdatedEvent.name}`, "subscribe", 0, lib.SubscriptionFilters.fromShorthand(playerName)
)); ));
} }
async unsubscribePlayerAssignment(playerName: string) { async unsubscribePlayerAssignment(playerName: string) {
await this.instance.sendTo("controller", new lib.SubscriptionRequest( await this.instance.sendTo("controller", new lib.SubscriptionRequest(
`exp_groups:${messages.ResolvedAssignmentUpdatedEvent.name}`, false, 0, playerName `exp_groups:${messages.ResolvedAssignmentUpdatedEvent.name}`, "unsubscribe", 0, lib.SubscriptionFilters.fromShorthand(playerName)
)); ));
} }
@@ -166,6 +166,6 @@ export class InstancePlugin extends BaseInstancePlugin {
} }
async luaSend(receiver: string, json: any) { async luaSend(receiver: string, json: any) {
await this.instance.sendRcon(`/c exp_groups.${receiver}(helpers.json_to_table[=[${JSON.stringify(json)}]=])`, true) await this.instance.sendRcon(`/sc exp_groups.${receiver}(helpers.json_to_table[=[${JSON.stringify(json)}]=])`, true)
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@expcluster/permission-groups", "name": "@expcluster/permission-groups",
"version": "0.1.0", "version": "6.5.0",
"description": "Clusterio plugin providing syncing of permission groups", "description": "Clusterio plugin providing syncing of permission groups",
"author": "Cooldude2606 <https://github.com/Cooldude2606>", "author": "Cooldude2606 <https://github.com/Cooldude2606>",
"license": "MIT", "license": "MIT",
+1 -1
View File
@@ -18,7 +18,7 @@ export default function GroupForm({ open, setOpen }: {
const group = await control.send(new messages.GroupCreateRequest( const group = await control.send(new messages.GroupCreateRequest(
values.name, new messages.GroupPermissions(Boolean(values.isBlacklist), []) values.name, new messages.GroupPermissions(Boolean(values.isBlacklist), [])
)); ));
navigate(`/exp_groups/${group.id}/view`); navigate(`/permission_groups/${group.id}/view`);
setOpen(false); setOpen(false);
} }
+3 -3
View File
@@ -66,9 +66,9 @@ function ExpGroupsPage() {
} }
export class WebPlugin extends BaseWebPlugin { export class WebPlugin extends BaseWebPlugin {
groups = new lib.EventSubscriber(messages.GroupUpdatedEvent, this.control); groups = new lib.MapSubscriber(messages.GroupUpdatedEvent, this.control);
assignments = new lib.EventSubscriber(messages.ManualAssignmentUpdatedEvent, this.control); assignments = new lib.MapSubscriber(messages.ManualAssignmentUpdatedEvent, this.control);
roleMappings = new lib.EventSubscriber(messages.RoleMappingUpdatedEvent, this.control); roleMappings = new lib.MapSubscriber(messages.RoleMappingUpdatedEvent, this.control);
async init() { async init() {
this.pages = [ this.pages = [
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@expcluster/lib_gui", "name": "@expcluster/lib_gui",
"version": "0.1.0", "version": "6.5.0",
"description": "Clusterio plugin providing a Lua GUI definition library.", "description": "Clusterio plugin providing a Lua GUI definition library.",
"author": "Cooldude2606 <https://github.com/Cooldude2606>", "author": "Cooldude2606 <https://github.com/Cooldude2606>",
"license": "MIT", "license": "MIT",
@@ -16,14 +16,11 @@ return {
"modules.data.quickbar", "modules.data.quickbar",
"modules.data.alt-view", "modules.data.alt-view",
"modules.data.tag", "modules.data.tag",
-- 'modules.data.bonus',
"modules.data.personal-logistic",
"modules.data.language", "modules.data.language",
--"modules.data.toolbar", --"modules.data.toolbar",
--- GUI --- GUI
"modules.gui.warp-list", "modules.gui.warp-list",
"modules.gui.player-list",
"modules.gui.vlayer", "modules.gui.vlayer",
"modules.gui._role_updates", "modules.gui._role_updates",
-201
View File
@@ -3,10 +3,6 @@
return { return {
--[[ --[[
TODO
force bonus
quick health regeneration
Base point is equal to the amount of standard value in each parameter. Base point is equal to the amount of standard value in each parameter.
CMMS CRS CCS CISB CHB CRDB PBR CMMS CRS CCS CISB CHB CRDB PBR
@@ -28,7 +24,6 @@ return {
scale = 1, scale = 1,
cost = 10, cost = 10,
max_value = 4, max_value = 4,
initial_value = 2,
value_step = 0.5, value_step = 0.5,
is_percentage = true, is_percentage = true,
}, },
@@ -37,7 +32,6 @@ return {
scale = 1, scale = 1,
cost = 60, cost = 60,
max_value = 2, max_value = 2,
initial_value = 1,
value_step = 0.25, value_step = 0.25,
is_percentage = true, is_percentage = true,
}, },
@@ -46,7 +40,6 @@ return {
scale = 1, scale = 1,
cost = 4, cost = 4,
max_value = 12, max_value = 12,
initial_value = 6,
value_step = 1, value_step = 1,
is_percentage = true, is_percentage = true,
}, },
@@ -55,7 +48,6 @@ return {
scale = 10, scale = 10,
cost = 2, cost = 2,
max_value = 100, max_value = 100,
initial_value = 50,
value_step = 10, value_step = 10,
}, },
{ {
@@ -63,7 +55,6 @@ return {
scale = 50, scale = 50,
cost = 4, cost = 4,
max_value = 300, max_value = 300,
initial_value = 150,
value_step = 50, value_step = 50,
}, },
{ {
@@ -71,7 +62,6 @@ return {
scale = 1, scale = 1,
cost = 1, cost = 1,
max_value = 16, max_value = 16,
initial_value = 8,
value_step = 2, value_step = 2,
combined_bonus = { combined_bonus = {
"character_resource_reach_distance_bonus", "character_resource_reach_distance_bonus",
@@ -83,101 +73,12 @@ return {
scale = 4, scale = 4,
cost = 40, cost = 40,
max_value = 8, max_value = 8,
initial_value = 4,
value_step = 1, value_step = 1,
is_special = true, is_special = true,
}, },
--[[
['character_item_pickup_distance_bonus'] = {
initial_value = 0,
max_value = 20,
value_step = 1,
scale = 1,
cost = 1,
is_percentage = false
},
['character_loot_pickup_distance_bonus'] = {
initial_value = 0,
max_value = 20,
value_step = 1,
scale = 1,
cost = 1,
is_percentage = false
},
['character_item_drop_distance_bonus'] = {
initial_value = 0,
max_value = 20,
value_step = 1,
scale = 1,
cost = 1,
is_percentage = false
}
]]
}, },
force_bonus = { force_bonus = {
--[[
['character_mining_speed_modifier'] = {
initial_value = 0,
max_value = 6,
value_step = 0.5,
scale = 1,
cost = 10,
is_percentage = true
},
['character_running_speed_modifier'] = {
initial_value = 0,
max_value = 3,
value_step = 0.25,
scale = 1,
cost = 40,
is_percentage = true
},
['character_crafting_speed_modifier'] = {
initial_value = 0,
max_value = 16,
value_step = 1,
scale = 1,
cost = 4,
is_percentage = true
},
['character_inventory_slots_bonus'] = {
initial_value = 0,
max_value = 200,
value_step = 10,
scale = 100,
cost = 2,
is_percentage = false
},
['character_health_bonus'] = {
initial_value = 0,
max_value = 400,
value_step = 50,
cost = 4,
is_percentage = false
},
['character_reach_distance_bonus'] = {
initial_value = 0,
max_value = 24,
value_step = 2,
scale = 1,
cost = 1,
is_percentage = false,
combined_bonus = {
'character_resource_reach_distance_bonus',
'character_build_distance_bonus'
}
},
['worker_robots_speed_modifier'] = {
initial_value = 0,
max_value = 0,
value_step = 0,
scale = 1,
cost = 1,
is_percentage = false
},
]]
["worker_robots_battery_modifier"] = { ["worker_robots_battery_modifier"] = {
initial_value = 1,
max_value = 1, max_value = 1,
value_step = 1, value_step = 1,
scale = 1, scale = 1,
@@ -185,7 +86,6 @@ return {
is_percentage = false, is_percentage = false,
}, },
["worker_robots_storage_bonus"] = { ["worker_robots_storage_bonus"] = {
initial_value = 1,
max_value = 1, max_value = 1,
value_step = 1, value_step = 1,
scale = 1, scale = 1,
@@ -193,114 +93,13 @@ return {
is_percentage = false, is_percentage = false,
}, },
["following_robots_lifetime_modifier"] = { ["following_robots_lifetime_modifier"] = {
initial_value = 1,
max_value = 1, max_value = 1,
value_step = 1, value_step = 1,
scale = 1, scale = 1,
cost = 1, cost = 1,
is_percentage = false, is_percentage = false,
}, },
--[[
['character_item_pickup_distance_bonus'] = {
initial_value = 0,
max_value = 20,
value_step = 1,
scale = 1,
cost = 1,
is_percentage = false
},
['character_loot_pickup_distance_bonus'] = {
initial_value = 0,
max_value = 20,
value_step = 1,
scale = 1,
cost = 1,
is_percentage = false
},
['character_item_drop_distance_bonus'] = {
initial_value = 0,
max_value = 20,
value_step = 1,
scale = 1,
cost = 1,
is_percentage = false
},
['character_trash_slot_count'] = {
initial_value = 0,
max_value = 0,
value_step = 0,
scale = 1,
cost = 1,
is_percentage = false
},
['mining_drill_productivity_bonus'] = {
initial_value = 0,
max_value = 0,
value_step = 0,
scale = 1,
cost = 1,
is_percentage = false
},
['train_braking_force_bonus'] = {
initial_value = 0,
max_value = 0,
value_step = 0,
scale = 1,
cost = 1,
is_percentage = false
},
['laboratory_speed_modifier'] = {
initial_value = 0,
max_value = 0,
value_step = 0,
scale = 1,
cost = 1,
is_percentage = false
},
['laboratory_productivity_bonus'] = {
initial_value = 0,
max_value = 0,
value_step = 0,
scale = 1,
cost = 1,
is_percentage = false
},
['inserter_stack_size_bonus'] = {
initial_value = 0,
max_value = 0,
value_step = 0,
scale = 1,
cost = 1,
is_percentage = false
},
['stack_inserter_capacity_bonus'] = {
initial_value = 0,
max_value = 0,
value_step = 0,
scale = 1,
cost = 1,
is_percentage = false
},
['artillery_range_modifier'] = {
initial_value = 0,
max_value = 0,
value_step = 0,
scale = 1,
cost = 1,
is_percentage = false
}
]]
}, },
surface_bonus = { surface_bonus = {
--[[
['solar_power_multiplier'] = {
initial_value = 1,
max_value = 1000,
value_step = 1,
scale = 1,
cost = 1,
is_percentage = false
}
]]
}, },
} }
+4 -5
View File
@@ -14,7 +14,7 @@ Roles.define_flag_trigger("is_spectator", function(player, state)
end) end)
Roles.define_flag_trigger("is_jail", function(player, state) Roles.define_flag_trigger("is_jail", function(player, state)
if player.character then if player.character then
player.character.active = not state player.character.disabled_by_script = not state
end end
end) end)
@@ -133,6 +133,9 @@ Roles.new_role("Board Member", "Board")
"command/search-online", "command/search-online",
"command/search-amount", "command/search-amount",
"command/search-recent", "command/search-recent",
"command/spectate",
"command/follow",
"command/clear-blueprints-surface",
} }
Roles.new_role("Supporter", "Sup") Roles.new_role("Supporter", "Sup")
@@ -167,7 +170,6 @@ Roles.new_role("Member", "Mem")
:allow{ :allow{
"fast-tree-decon", "fast-tree-decon",
"gui/bonus", "gui/bonus",
--"command/bonus",
--"command/tag-color", --"command/tag-color",
"command/set-join-message", "command/set-join-message",
"command/remove-join-message", "command/remove-join-message",
@@ -199,9 +201,7 @@ Roles.new_role("Veteran", "Vet")
"command/chat-commands", "command/chat-commands",
"command/clear-ground-items", "command/clear-ground-items",
"command/clear-blueprints", "command/clear-blueprints",
"command/clear-blueprints-radius",
"command/set-trains-to-automatic", "command/set-trains-to-automatic",
"command/set-auto-research",
"command/lawnmower", "command/lawnmower",
"command/waterfill", "command/waterfill",
"command/artillery", "command/artillery",
@@ -261,7 +261,6 @@ local default = Roles.new_role("Guest", "")
"gui/research", "gui/research",
"gui/autofill", "gui/autofill",
"gui/module", "gui/module",
"gui/landfill",
"gui/production", "gui/production",
-- "command/tag", -- "command/tag",
-- "command/tag-clear", -- "command/tag-clear",
@@ -14,9 +14,10 @@ local Jail = require("modules.exp_legacy.modules.control.jail") --- @dep modules
local Colors = require("modules/exp_util/include/color") local Colors = require("modules/exp_util/include/color")
local format_player_name = ExpUtil.format_player_name_locale local format_player_name = ExpUtil.format_player_name_locale
local SelectedPlayer, SelectedAction --- Accessors injected by the gui so the actions can read the selected player and set the selected action
local function set_datastores(player, action) local get_selected_player, set_selected_action
SelectedPlayer, SelectedAction = player, action local function set_accessors(player_getter, action_setter)
get_selected_player, set_selected_action = player_getter, action_setter
end end
-- auth that will only allow when on player's of lower roles -- auth that will only allow when on player's of lower roles
@@ -29,11 +30,10 @@ local function auth_lower_role(player, selected_player_name)
end end
-- gets the action player and a coloured name for the action to be used on -- gets the action player and a coloured name for the action to be used on
local function get_action_player_name(player) local function get_action_player(player)
local selected_player_name = SelectedPlayer:get(player) local selected_player = get_selected_player(player) --[[ @as LuaPlayer ]]
local selected_player = game.players[selected_player_name]
local selected_player_color = format_player_name(selected_player) local selected_player_color = format_player_name(selected_player)
return selected_player_name, selected_player_color return selected_player, selected_player_color
end end
-- teleports one player to another -- teleports one player to another
@@ -63,12 +63,11 @@ end
--- Teleports the user to the action player --- Teleports the user to the action player
-- @element goto_player -- @element goto_player
local goto_player = new_button("utility/export", { "player-list.goto-player" }) local goto_player = new_button("utility/export", { "exp-gui_player-list.goto-player" })
:on_click(function(def, player, element) :on_click(function(def, player, element)
local selected_player_name = get_action_player_name(player) local selected_player = get_action_player(player)
local selected_player = game.players[selected_player_name]
if not player.character or not selected_player.character then if not player.character or not selected_player.character then
player.print({ "expcore-commands.reject-player-alive" }, Colors.orange_red) player.print({ "exp-commands-parse.player-alive" }, Colors.orange_red)
else else
teleport(player, selected_player) teleport(player, selected_player)
end end
@@ -76,12 +75,11 @@ local goto_player = new_button("utility/export", { "player-list.goto-player" })
--- Teleports the action player to the user --- Teleports the action player to the user
-- @element bring_player -- @element bring_player
local bring_player = new_button("utility/import", { "player-list.bring-player" }) local bring_player = new_button("utility/import", { "exp-gui_player-list.bring-player" })
:on_click(function(def, player, element) :on_click(function(def, player, element)
local selected_player_name = get_action_player_name(player) local selected_player = get_action_player(player)
local selected_player = game.players[selected_player_name]
if not player.character or not selected_player.character then if not player.character or not selected_player.character then
player.print({ "expcore-commands.reject-player-alive" }, Colors.orange_red) player.print({ "exp-commands-parse.player-alive" }, Colors.orange_red)
else else
teleport(selected_player, player) teleport(selected_player, player)
end end
@@ -89,83 +87,83 @@ local bring_player = new_button("utility/import", { "player-list.bring-player" }
--- Reports the action player, requires a reason to be given --- Reports the action player, requires a reason to be given
-- @element report_player -- @element report_player
local report_player = new_button("utility/spawn_flag", { "player-list.report-player" }) local report_player = new_button("utility/spawn_flag", { "exp-gui_player-list.report-player" })
:on_click(function(def, player, element) :on_click(function(def, player, element)
local selected_player_name = get_action_player_name(player) local selected_player = get_action_player(player)
if Reports.is_reported(selected_player_name, player.name) then if Reports.is_reported(selected_player.name, player.name) then
player.print({ "exp-commands_report.already-reported" }, Colors.orange_red) player.print({ "exp-commands_report.already-reported" }, Colors.orange_red)
else else
SelectedAction:set(player, "command/report") set_selected_action(player, "command/report")
end end
end) end)
local function report_player_callback(player, reason) local function report_player_callback(player, reason)
local selected_player_name, selected_player_color = get_action_player_name(player) local selected_player, selected_player_color = get_action_player(player)
local by_player_name_color = format_player_name(player) local by_player_name_color = format_player_name(player)
game.print{ "exp-commands_reports.response", selected_player_color, reason } game.print{ "exp-commands_reports.response", selected_player_color, reason }
Roles.print_to_roles_higher("Trainee", { "exp-commands_reports.response-admin", selected_player_color, by_player_name_color, reason }) Roles.print_to_roles_higher("Trainee", { "exp-commands_reports.response-admin", selected_player_color, by_player_name_color, reason })
Reports.report_player(selected_player_name, player.name, reason) Reports.report_player(selected_player.name, player.name, reason)
end end
--- Gives the action player a warning, requires a reason --- Gives the action player a warning, requires a reason
-- @element warn_player -- @element warn_player
local warn_player = new_button("utility/spawn_flag", { "player-list.warn-player" }) local warn_player = new_button("utility/spawn_flag", { "exp-gui_player-list.warn-player" })
:on_click(function(def, player, element) :on_click(function(def, player, element)
SelectedAction:set(player, "command/give-warning") set_selected_action(player, "command/give-warning")
end) end)
local function warn_player_callback(player, reason) local function warn_player_callback(player, reason)
local selected_player_name, selected_player_color = get_action_player_name(player) local selected_player, selected_player_color = get_action_player(player)
local by_player_name_color = format_player_name(player) local by_player_name_color = format_player_name(player)
game.print{ "exp-commands_warnings.create", selected_player_color, by_player_name_color, reason } game.print{ "exp-commands_warnings.create", selected_player_color, by_player_name_color, reason }
Warnings.add_warning(selected_player_name, player.name, reason) Warnings.add_warning(selected_player.name, player.name, reason)
end end
--- Jails the action player, requires a reason --- Jails the action player, requires a reason
-- @element jail_player -- @element jail_player
local jail_player = new_button("utility/multiplayer_waiting_icon", { "player-list.jail-player" }) local jail_player = new_button("utility/multiplayer_waiting_icon", { "exp-gui_player-list.jail-player" })
:on_click(function(def, player, element) :on_click(function(def, player, element)
local selected_player_name, selected_player_color = get_action_player_name(player) local selected_player, selected_player_color = get_action_player(player)
if Jail.is_jailed(selected_player_name) then if Jail.is_jailed(selected_player.name) then
player.print({ "exp-commands_jail.already-jailed", selected_player_color }, Colors.orange_red) player.print({ "exp-commands_jail.already-jailed", selected_player_color }, Colors.orange_red)
else else
SelectedAction:set(player, "command/jail") set_selected_action(player, "command/jail")
end end
end) end)
local function jail_player_callback(player, reason) local function jail_player_callback(player, reason)
local selected_player_name, selected_player_color = get_action_player_name(player) local selected_player, selected_player_color = get_action_player(player)
local by_player_name_color = format_player_name(player) local by_player_name_color = format_player_name(player)
game.print{ "exp-commands_jail.jailed", selected_player_color, by_player_name_color, reason } game.print{ "exp-commands_jail.jailed", selected_player_color, by_player_name_color, reason }
Jail.jail_player(selected_player_name, player.name, reason) Jail.jail_player(selected_player.name, player.name, reason)
end end
--- Kicks the action player, requires a reason --- Kicks the action player, requires a reason
-- @element kick_player -- @element kick_player
local kick_player = new_button("utility/warning_icon", { "player-list.kick-player" }) local kick_player = new_button("utility/warning_icon", { "exp-gui_player-list.kick-player" })
:on_click(function(def, player, element) :on_click(function(def, player, element)
SelectedAction:set(player, "command/kick") set_selected_action(player, "command/kick")
end) end)
local function kick_player_callback(player, reason) local function kick_player_callback(player, reason)
local selected_player = get_action_player_name(player) local selected_player = get_action_player(player)
game.kick_player(selected_player, reason) game.kick_player(selected_player, reason)
end end
--- Bans the action player, requires a reason --- Bans the action player, requires a reason
-- @element ban_player -- @element ban_player
local ban_player = new_button("utility/danger_icon", { "player-list.ban-player" }) local ban_player = new_button("utility/danger_icon", { "exp-gui_player-list.ban-player" })
:on_click(function(def, player, element) :on_click(function(def, player, element)
SelectedAction:set(player, "command/ban") set_selected_action(player, "command/ban")
end) end)
local function ban_player_callback(player, reason) local function ban_player_callback(player, reason)
local selected_player = get_action_player_name(player) local selected_player = get_action_player(player)
game.ban_player(selected_player, reason) game.ban_player(selected_player, reason)
end end
return { return {
set_datastores = set_datastores, set_accessors = set_accessors,
buttons = { buttons = {
["command/teleport"] = { ["command/teleport"] = {
auth = function(player, selected_player) auth = function(player, selected_player)
+1
View File
@@ -4,4 +4,5 @@
return { return {
fluid = true, --- @setting fluid When true, checks for for fluid pipes when removing miners fluid = true, --- @setting fluid When true, checks for for fluid pipes when removing miners
chest = true, --- @setting chest When true, checks for for chest when removing miners chest = true, --- @setting chest When true, checks for for chest when removing miners
beacon = true, --- @setting beacon When true, checks for for beacon when removing miners
} }
File diff suppressed because it is too large Load Diff
+2 -4
View File
@@ -5,15 +5,13 @@ return {
repeat_lifetime = 3600 * 20, --- @setting repeat_lifetime The length of time, in ticks, that protected removals will be remembered for repeat_lifetime = 3600 * 20, --- @setting repeat_lifetime The length of time, in ticks, that protected removals will be remembered for
refresh_rate = 3600 * 5, --- @setting refresh_rate How often the age of protected removals are checked against repeat_lifetime refresh_rate = 3600 * 5, --- @setting refresh_rate How often the age of protected removals are checked against repeat_lifetime
always_protected_names = { --- @setting always_protected_names Names of entities which are always protected always_protected_names = { --- @setting always_protected_names Names of entities which are always protected
}, },
always_protected_types = { --- @setting always_protected_types Types of entities which are always protected always_protected_types = { --- @setting always_protected_types Types of entities which are always protected
"boiler", "generator", "offshore-pump", "power-switch", "reactor", "rocket-silo", "boiler", "generator", "offshore-pump", "reactor", "heat-exchanger", "heat-pipe", "fusion-reactor", "fusion-generator", "power-switch", "rocket-silo",
}, },
always_trigger_repeat_names = { --- @setting always_trigger_repeat_names Names of entities which always trigger repeated removal protection always_trigger_repeat_names = { --- @setting always_trigger_repeat_names Names of entities which always trigger repeated removal protection
}, },
always_trigger_repeat_types = { --- @setting always_trigger_repeat_types Types of entities which always trigger repeated removal protection always_trigger_repeat_types = { --- @setting always_trigger_repeat_types Types of entities which always trigger repeated removal protection
"reactor", "rocket-silo", "reactor", "fusion-reactor", "rocket-silo",
}, },
} }
-8
View File
@@ -2,14 +2,6 @@
-- @config Repair -- @config Repair
return { return {
disallow = { --- @setting disallow items in this list will never be repaired
["loader"] = true,
["fast-loader"] = true,
["express-loader"] = true,
["electric-energy-interface"] = true,
["infinity-chest"] = true,
},
max_range = 50, --- @setting max_range the max range that can be used with the repair command
allow_blueprint_repair = false, --- @setting allow_blueprint_repair when true will allow blueprints (things not destroyed by biters) to be build instantly using the repair command allow_blueprint_repair = false, --- @setting allow_blueprint_repair when true will allow blueprints (things not destroyed by biters) to be build instantly using the repair command
allow_ghost_revive = true, --- @setting allow_ghost_revive when true will allow ghosts (things destroyed by biters) to be build instantly using the repair command allow_ghost_revive = true, --- @setting allow_ghost_revive when true will allow ghosts (things destroyed by biters) to be build instantly using the repair command
allow_heal_entities = true, --- @setting allow_heal_entities when true will heal entities to full health that are within range allow_heal_entities = true, --- @setting allow_heal_entities when true will heal entities to full health that are within range
-10
View File
@@ -12,16 +12,6 @@ return {
-- this enable 20 more inventory for each mining productivity level up to 4 -- this enable 20 more inventory for each mining productivity level up to 4
bonus_inventory = { bonus_inventory = {
enabled = true, enabled = true,
log = {
["base"] = {
["name"] = "mining-productivity-4",
["level"] = 4
},
["space-age"] = {
["name"] = "mining-productivity-3",
["level"] = 3
},
},
res = { res = {
-- Mining Productivity -- Mining Productivity
["mining-productivity"] = true, ["mining-productivity"] = true,
+4 -10
View File
@@ -349,7 +349,7 @@ function vlayer.create_input_interface(surface, position, circuit, last_user)
end end
interface.destructible = false interface.destructible = false
interface.minable = false interface.minable_flag = false
interface.operable = true interface.operable = true
return interface return interface
@@ -427,7 +427,7 @@ function vlayer.create_output_interface(surface, position, circuit, last_user)
end end
interface.destructible = false interface.destructible = false
interface.minable = false interface.minable_flag = false
interface.operable = true interface.operable = true
return interface return interface
@@ -514,9 +514,6 @@ function vlayer.get_statistics()
energy_sustained = vdp * gsm, energy_sustained = vdp * gsm,
energy_capacity = vlayer_data.properties.capacity * mega, energy_capacity = vlayer_data.properties.capacity * mega,
energy_storage = vlayer_data.storage.energy, energy_storage = vlayer_data.storage.energy,
day_time = math.floor(vlayer_data.surface.daytime * vlayer_data.surface.ticks_per_day),
day_length = vlayer_data.surface.ticks_per_day,
tick = game.tick,
} }
end end
@@ -540,9 +537,6 @@ function vlayer.get_circuits()
energy_sustained = "signal-S", energy_sustained = "signal-S",
energy_capacity = "signal-C", energy_capacity = "signal-C",
energy_storage = "signal-E", energy_storage = "signal-E",
day_time = "signal-D",
day_length = "signal-L",
tick = "signal-T",
} }
end end
@@ -574,7 +568,7 @@ function vlayer.create_circuit_interface(surface, position, circuit, last_user)
end end
interface.destructible = false interface.destructible = false
interface.minable = false interface.minable_flag = false
interface.operable = true interface.operable = true
return interface return interface
@@ -653,7 +647,7 @@ function vlayer.create_energy_interface(surface, position, last_user)
end end
interface.destructible = false interface.destructible = false
interface.minable = false interface.minable_flag = false
interface.operable = false interface.operable = false
interface.electric_buffer_size = 0 interface.electric_buffer_size = 0
interface.power_production = 0 interface.power_production = 0
+1 -1
View File
@@ -182,7 +182,7 @@ function Warps.make_warp_area(warp_id)
} }
entity.destructible = false entity.destructible = false
entity.health = 0 entity.health = 0
entity.minable = false entity.minable_flag = false
entity.rotatable = false entity.rotatable = false
-- Save reference of the last power pole -- Save reference of the last power pole
-99
View File
@@ -1,99 +0,0 @@
--[[-- Commands Module - Bonus
- Adds a command that allows players to have increased stats
@data Bonus
]]
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local config = require("modules.exp_legacy.config.bonus") --- @dep config.bonuses
local Commands = require("modules/exp_commands")
-- Stores the bonus for the player
local PlayerData = require("modules.exp_legacy.expcore.player_data") --- @dep expcore.player_data
local PlayerBonus = PlayerData.Settings:combine("Bonus")
PlayerBonus:set_default(0)
PlayerBonus:set_metadata{
permission = "command/bonus",
stringify = function(value)
if not value or value == 0 then
return "None set"
end
return value
end,
}
--- Apply a bonus to a player
local function apply_bonus(player, stage)
if not player.character then
return
end
for k, v in pairs(config.player_bonus) do
player[k] = v.value * stage / 10
if v.combined_bonus then
for i = 1, #v.combined_bonus, 1 do
player[v.combined_bonus[i]] = v.value * stage / 10
end
end
end
end
--- When store is updated apply new bonus to the player
PlayerBonus:on_update(function(player_name, player_bonus)
apply_bonus(game.players[player_name], player_bonus or 0)
end)
--- Changes the amount of bonus you receive
Commands.new("bonus", { "bonus.description" })
:optional("amount", { "bonus.arg-amount" }, Commands.types.integer_range(0, 10))
:register(function(player, amount)
--- @cast amount number?
if amount then
PlayerBonus:set(player, amount)
return Commands.status.success{ "bonus.set", amount }
else
return Commands.status.success{ "bonus.get", PlayerBonus:get(player) }
end
end)
--- When a player respawns re-apply bonus
Event.add(defines.events.on_player_respawned, function(event)
local player = game.players[event.player_index]
apply_bonus(player, PlayerBonus:get(player))
end)
--- Remove bonus if a player no longer has access to the command
local function role_update(event)
local player = game.players[event.player_index]
if not Roles.player_allowed(player, "command/bonus") then
apply_bonus(player, 0)
end
end
--- When a player dies allow them to have instant respawn
Event.add(defines.events.on_player_died, function(event)
local player = game.players[event.player_index]
if Roles.player_has_flag(player, "instant-respawn") then
player.ticks_to_respawn = 120
end
end)
Event.add(defines.events.on_player_created, function(event)
if event.player_index ~= 1 then
return
end
for k, v in pairs(config.force_bonus) do
game.players[event.player_index].force[k] = v.value
end
for k, v in pairs(config.surface_bonus) do
game.players[event.player_index].surface[k] = v.value
end
end)
Event.add(Roles.events.on_role_assigned, role_update)
Event.add(Roles.events.on_role_unassigned, role_update)
@@ -1,89 +0,0 @@
local Commands = require("modules/exp_commands")
local config = require("modules.exp_legacy.config.personal_logistic") --- @dep config.personal-logistic
---@param target LuaEntity | LuaPlayer
---@param amount number
local function pl(target, amount)
local c
local s
--- @cast target any Remove cast once implemented
error("Needs updating to use 2.0 logistics")
if target.object_name == "LuaPlayer" then
c = target.clear_personal_logistic_slot
s = target.set_personal_logistic_slot
else
c = target.clear_vehicle_logistic_slot
s = target.set_vehicle_logistic_slot
end
for _, v in pairs(config.request) do
c(config.start + v.key)
end
if (amount < 0) then
return
end
local stats = target.force.get_item_production_statistics(target.surface)
for k, v in pairs(config.request) do
local v_min = math.ceil(v.min * amount)
local v_max = math.ceil(v.max * amount)
if v.stack ~= nil and v.stack ~= 1 and v.type ~= "weapon" then
v_min = math.floor(v_min / v.stack) * v.stack
v_max = math.ceil(v_max / v.stack) * v.stack
end
if v.upgrade_of == nil then
if v.type ~= nil then
if stats.get_input_count(k) < config.production_required[v.type] then
if v_min > 0 then
if v_min == v_max then
v_min = math.floor((v_max * 0.5) / v.stack) * v.stack
end
else
v_min = 0
end
end
end
s(config.start + v.key, { name = k, min = v_min, max = v_max })
else
if v.type ~= nil then
if stats.get_input_count(k) >= config.production_required[v.type] then
s(config.start + v.key, { name = k, min = v_min, max = v_max })
local vuo = v.upgrade_of
while (vuo ~= nil) do
s(config.start + config.request[vuo].key, { name = vuo, min = 0, max = 0 })
vuo = config.request[vuo].upgrade_of
end
else
s(config.start + v.key, { name = k, min = 0, max = v_max })
end
end
end
end
end
Commands.new("personal-logistic", "Set Personal Logistic (-1 to cancel all) (Select spidertron to edit spidertron)")
:argument("amount", "", Commands.types.integer_range(-1, 10))
:add_aliases{ "pl" }
:add_flags{ "disabled" } -- Remove once implemented above
:register(function(player, amount)
--- @cast amount number
if player.force.technologies["logistic-robotics"].researched then
if player.selected ~= nil then
if player.selected.name == "spidertron" then
pl(player.selected, amount / 10)
end
else
pl(player, amount / 10)
end
else
return Commands.status.error("Personal Logistic not researched")
end
end)
@@ -43,10 +43,14 @@ local ignored_items = {
--- Saves your quickbar preset to the script-output folder --- Saves your quickbar preset to the script-output folder
Commands.new("save-quickbar", "Saves your Quickbar preset items to file") Commands.new("save-quickbar", "Saves your Quickbar preset items to file")
:add_aliases{ "save-toolbar" } :add_aliases{ "save-toolbar" }
:add_flags{ "disabled" }
:register(function(player) :register(function(player)
local filters = {} local filters = {}
error("2.1 changes to get_quick_bar_slot beak compatibility with 2.0; waiting for upstream")
-- Upstream may add method to compat, or change inventory sync to have a quickbar only mode
for i = 1, 100 do for i = 1, 100 do
--[[
local slot = player.get_quick_bar_slot(i) local slot = player.get_quick_bar_slot(i)
-- Need to filter out blueprint and blueprint books because the slot is a LuaItemPrototype and does not contain a way to export blueprint data -- Need to filter out blueprint and blueprint books because the slot is a LuaItemPrototype and does not contain a way to export blueprint data
if slot ~= nil then if slot ~= nil then
@@ -55,6 +59,7 @@ Commands.new("save-quickbar", "Saves your Quickbar preset items to file")
filters[i] = slot.name filters[i] = slot.name
end end
end end
]]
end end
if next(filters) then if next(filters) then
@@ -1,448 +0,0 @@
--[[-- Gui Module - Player List
- Adds a player list to show names and play time; also includes action buttons which can preform actions to players
@gui Player-List
@alias player_list
]]
-- luacheck:ignore 211/Colors
local ExpUtil = require("modules/exp_util")
local Gui = require("modules/exp_gui")
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
local Datastore = require("modules.exp_legacy.expcore.datastore") --- @dep expcore.datastore
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local config = require("modules.exp_legacy.config.gui.player_list_actions") --- @dep config.gui.player_list_actions
--- Stores all data for the warp gui
local PlayerListData = Datastore.connect("PlayerListData")
PlayerListData:set_serializer(Datastore.name_serializer)
local SelectedPlayer = PlayerListData:combine("SelectedPlayer")
local SelectedAction = PlayerListData:combine("SelectedAction")
-- Set the config to use these stores
config.set_datastores(SelectedPlayer, SelectedAction)
--- Button used to open the action bar
-- @element open_action_bar
local open_action_bar = Gui.define("open_action_bar")
:draw{
type = "sprite-button",
sprite = "utility/expand_dots",
tooltip = { "player-list.open-action-bar" },
style = "frame_button",
name = Gui.from_name,
}
:style{
padding = -2,
width = 8,
height = 14,
}
:on_click(function(def, player, element)
local selected_player_name = element.parent.name
local old_selected_player_name = SelectedPlayer:get(player)
if selected_player_name == old_selected_player_name then
SelectedPlayer:remove(player)
else
SelectedPlayer:set(player, selected_player_name)
end
end)
--- Button used to close the action bar
-- @element close_action_bar
local close_action_bar = Gui.define("close_action_bar")
:draw{
type = "sprite-button",
sprite = "utility/close_black",
tooltip = { "player-list.close-action-bar" },
style = "slot_sized_button_red",
}
:style(Gui.styles.sprite{
size = 20,
padding = -1,
top_margin = -1,
right_margin = -1,
})
:on_click(function(def, player, element)
SelectedPlayer:remove(player)
SelectedAction:remove(player)
end)
--- Button used to confirm a reason
-- @element reason_confirm
local reason_confirm = Gui.define("reason_confirm")
:draw{
type = "sprite-button",
sprite = "utility/confirm_slot",
tooltip = { "player-list.reason-confirm" },
style = "slot_sized_button_green",
}
:style(Gui.styles.sprite{
size = 30,
padding = -1,
left_margin = -2,
right_margin = -1,
})
:on_click(function(def, player, element)
local reason = element.parent.entry.text
local action_name = SelectedAction:get(player)
local reason_callback = config.buttons[action_name].reason_callback
if reason == nil or not reason:find("%S") then reason = "no reason given" end
reason_callback(player, reason)
SelectedPlayer:remove(player)
SelectedAction:remove(player)
element.parent.entry.text = ""
end)
--- Set of elements that are used to make up a row of the player table
-- @element add_player_base
local add_player_base = Gui.define("add_player_base")
:draw(function(_, parent, player_data)
-- Add the button to open the action bar
local toggle_action_bar_flow = parent.add{ type = "flow", name = player_data.name }
open_action_bar(toggle_action_bar_flow)
-- Add the player name
local player_name = parent.add{
type = "label",
name = "player-name-" .. player_data.index,
caption = player_data.name,
tooltip = { "player-list.open-map", player_data.name, player_data.tag, player_data.role_name },
}
player_name.style.padding = { 0, 2, 0, 0 }
player_name.style.font_color = player_data.chat_color
-- Add the time played label
local alignment = Gui.elements.aligned_flow(parent, { name = "player-time-" .. player_data.index })
local time_label = alignment.add{
name = "label",
type = "label",
caption = player_data.caption,
tooltip = player_data.tooltip,
}
time_label.style.padding = 0
return player_name
end)
:on_click(function(def, player, element, event)
local selected_player_name = element.caption
local selected_player = game.players[selected_player_name]
if event.button == defines.mouse_button_type.left then
-- LMB will open the map to the selected player
player.set_controller{
type = defines.controllers.remote,
position = selected_player.physical_position,
surface = selected_player.physical_surface
}
else
-- RMB will toggle the settings
local old_selected_player_name = SelectedPlayer:get(player)
if selected_player_name == old_selected_player_name then
SelectedPlayer:remove(player)
SelectedAction:remove(player)
else
SelectedPlayer:set(player, selected_player_name)
end
end
end)
-- Removes the three elements that are added as part of the base
local function remove_player_base(parent, player)
Gui.destroy_if_valid(parent[player.name])
Gui.destroy_if_valid(parent["player-name-" .. player.index])
Gui.destroy_if_valid(parent["player-time-" .. player.index])
end
-- Update the time label for a player using there player time data
local function update_player_base(parent, player_time)
local time_element = parent[player_time.element_name]
if time_element and time_element.valid then
time_element.label.caption = player_time.caption
time_element.label.tooltip = player_time.tooltip
end
end
--- Adds all the buttons and flows that make up the action bar
-- @element add_action_bar
local add_action_bar_buttons = Gui.define("add_action_bar_buttons")
:draw(function(_, parent)
close_action_bar(parent)
-- Loop over all the buttons in the config
for action_name, button_data in pairs(config.buttons) do
-- Added the permission flow
local permission_flow = parent.add{ type = "flow", name = action_name }
permission_flow.visible = false
-- Add the buttons under that permission
for _, button in ipairs(button_data) do
button(permission_flow)
end
end
return parent
end)
--- Updates the visible state of the action bar buttons
local function update_action_bar(element)
local player = Gui.get_player(element)
local selected_player_name = SelectedPlayer:get(player)
if not selected_player_name then
-- Hide the action bar when no player is selected
element.visible = false
else
local selected_player = game.players[selected_player_name]
if not selected_player.connected then
-- If the player is offline then reest stores
element.visible = false
SelectedPlayer:remove(player)
SelectedAction:remove(player)
else
-- Otherwise check what actions the player is allowed to use
element.visible = true
for action_name, buttons in pairs(config.buttons) do
if buttons.auth and not buttons.auth(player, selected_player) then
element[action_name].visible = false
elseif Roles.player_allowed(player, action_name) then
element[action_name].visible = true
end
end
end
end
end
--- Main player list container for the left flow
-- @element player_list_container
local player_list_container = Gui.define("player_list_container")
:draw(function(definition, parent)
-- Draw the internal container
local container = Gui.elements.container(parent)
-- Draw the scroll table for the players
local scroll_table = Gui.elements.scroll_table(container, 184, 3, "scroll")
-- Change the style of the scroll table
local scroll_table_style = scroll_table.style
scroll_table_style.padding = { 1, 0, 1, 2 }
-- Add the action bar
local action_bar = Gui.elements.footer(container, { name = "action_bar", no_flow = true })
-- Change the style of the action bar
local action_bar_style = action_bar.style
action_bar_style.height = 35
action_bar_style.padding = { 1, 3 }
action_bar.visible = false
-- Add the buttons to the action bar
add_action_bar_buttons(action_bar)
-- Add the reason bar
local reason_bar = Gui.elements.footer(container, { name = "reason_bar", no_flow = true })
-- Change the style of the reason bar
local reason_bar_style = reason_bar.style
reason_bar_style.height = 35
reason_bar_style.padding = { -1, 3 }
reason_bar.visible = false
-- Add the text entry for the reason bar
local reason_field =
reason_bar.add{
name = "entry",
type = "textfield",
style = "stretchable_textfield",
tooltip = { "player-list.reason-entry" },
}
-- Change the style of the text entry
local reason_entry_style = reason_field.style
reason_entry_style.padding = 0
reason_entry_style.height = 28
reason_entry_style.minimal_width = 160
-- Add the confirm reason button
reason_confirm(reason_bar)
-- Return the exteral container
return container.parent
end)
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(player_list_container, true)
Gui.toolbar.create_button{
name = "player_list_toggle",
left_element = player_list_container,
sprite = "entity/character",
tooltip = { "player-list.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/player-list")
end
}
local online_time_format = ExpUtil.format_time_factory_locale{ format = "short", hours = true, minutes = true }
local afk_time_format = ExpUtil.format_time_factory_locale{ format = "long", minutes = true }
-- Get caption and tooltip format for a player
local function get_time_formats(online_time, afk_time)
local tick = game.tick > 0 and game.tick or 1
local percent = math.round(online_time / tick, 3) * 100
local caption = online_time_format(online_time)
local tooltip = { "player-list.afk-time", percent, afk_time_format(afk_time) }
return caption, tooltip
end
-- Get the player time to be used to update time label
local function get_player_times()
local ctn = 0
local player_times = {}
for _, player in pairs(game.connected_players) do
ctn = ctn + 1
-- Add the player time details to the array
local caption, tooltip = get_time_formats(player.online_time, player.afk_time)
player_times[ctn] = {
element_name = "player-time-" .. player.index,
caption = caption,
tooltip = tooltip,
}
end
return player_times
end
-- Get a sorted list of all online players
local function get_player_list_order()
-- Sort all the online players into roles
local players = {}
for _, player in pairs(game.connected_players) do
local highest_role = Roles.get_player_highest_role(player)
if not players[highest_role.name] then
players[highest_role.name] = {}
end
table.insert(players[highest_role.name], player)
end
-- Sort the players from roles into a set order
local ctn = 0
local player_list_order = {}
for _, role_name in pairs(Roles.config.order) do
if players[role_name] then
for _, player in pairs(players[role_name]) do
ctn = ctn + 1
-- Add the player data to the array
local caption, tooltip = get_time_formats(player.online_time, player.afk_time)
player_list_order[ctn] = {
name = player.name,
index = player.index,
tag = player.tag,
role_name = role_name,
chat_color = player.chat_color,
caption = caption,
tooltip = tooltip,
}
end
end
end
--[[Adds fake players to the player list
local tick = game.tick+1
for i = 1, 10 do
local online_time = math.random(1, tick)
local afk_time = math.random(online_time-(tick/10), tick)
local caption, tooltip = get_time_formats(online_time, afk_time)
player_list_order[ctn+i] = {
name='Player '..i,
index=0-i,
tag='',
role_name = 'Fake Player',
chat_color = table.get_random(Colors),
caption = caption,
tooltip = tooltip
}
end--]]
return player_list_order
end
--- Update the play times every 30 sections
Event.on_nth_tick(1800, function()
local player_times = get_player_times()
for _, player in pairs(game.connected_players) do
local container = Gui.get_left_element(player_list_container, player)
local scroll_table = container.frame.scroll.table
for _, player_time in pairs(player_times) do
update_player_base(scroll_table, player_time)
end
end
end)
--- When a player leaves only remove they entry
Event.add(defines.events.on_player_left_game, function(event)
local remove_player = game.players[event.player_index]
for _, player in pairs(game.connected_players) do
local container = Gui.get_left_element(player_list_container, player)
local scroll_table = container.frame.scroll.table
remove_player_base(scroll_table, remove_player)
local selected_player_name = SelectedPlayer:get(player)
if selected_player_name == remove_player.name then
SelectedPlayer:remove(player)
SelectedAction:remove(player)
end
end
end)
--- All other events require a full redraw of the table
local function redraw_player_list()
local player_list_order = get_player_list_order()
for _, player in pairs(game.connected_players) do
local container = Gui.get_left_element(player_list_container, player)
local scroll_table = container.frame.scroll.table
scroll_table.clear()
for _, next_player_data in ipairs(player_list_order) do
add_player_base(scroll_table, next_player_data)
end
end
end
Event.add(defines.events.on_player_joined_game, redraw_player_list)
Event.add(Roles.events.on_role_assigned, redraw_player_list)
Event.add(Roles.events.on_role_unassigned, redraw_player_list)
--- When the action player is changed the action bar will update
SelectedPlayer:on_update(function(player_name, selected_player)
local player = game.players[player_name]
local container = Gui.get_left_element(player_list_container, player)
local scroll_table = container.frame.scroll.table
update_action_bar(container.frame.action_bar)
for _, next_player in pairs(game.connected_players) do
local element = scroll_table[next_player.name][open_action_bar.name]
local style = "frame_button"
if next_player.name == selected_player then
style = "tool_button"
end
element.style = style
local element_style = element.style --[[@as LuaStyle]]
element_style.padding = -2
element_style.width = 8
element_style.height = 14
end
end)
--- When the action name is changed the reason input will update
SelectedAction:on_update(function(player_name, selected_action)
local player = game.players[player_name]
local container = Gui.get_left_element(player_list_container, player)
local element = container.frame.reason_bar
if selected_action then
-- if there is a new value then check the player is still online
local selected_player_name = SelectedPlayer:get(player_name)
local selected_player = game.players[selected_player_name]
if selected_player.connected then
element.visible = true
else
-- Clear if the player is offline
SelectedPlayer:remove(player)
SelectedAction:remove(player)
end
else
element.visible = false
end
end)
+2 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@expcluster/legacy", "name": "@expcluster/legacy",
"version": "0.1.0", "version": "6.5.0",
"description": "Clusterio plugin implementing the legacy v6 scenario updated for factorio 2.0", "description": "Clusterio plugin implementing the legacy v6 scenario updated for factorio 2.0",
"author": "Cooldude2606 <https://github.com/Cooldude2606>", "author": "Cooldude2606 <https://github.com/Cooldude2606>",
"license": "MIT", "license": "MIT",
@@ -26,6 +26,7 @@
"dependencies": { "dependencies": {
"@expcluster/lib_commands": "workspace:^", "@expcluster/lib_commands": "workspace:^",
"@expcluster/lib_util": "workspace:^", "@expcluster/lib_util": "workspace:^",
"@expcluster/lib_gui": "workspace:^",
"@sinclair/typebox": "catalog:" "@sinclair/typebox": "catalog:"
}, },
"publishConfig": { "publishConfig": {
@@ -14,7 +14,7 @@ Commands.new("clear-inventory", { "exp-commands_clear-inventory.description" })
:register(function(player, other_player) :register(function(player, other_player)
local inventory = other_player.get_main_inventory() local inventory = other_player.get_main_inventory()
if not inventory then if not inventory then
return Commands.status.error{ "expcore-commands.reject-player-alive" } return Commands.status.error{ "exp-commands-parse.player-alive" }
end end
transfer_inventory{ transfer_inventory{
+1 -1
View File
@@ -2,7 +2,7 @@
Adds a command that opens the debug frame Adds a command that opens the debug frame
]] ]]
local DebugView = require("modules.exp_legacy.modules.gui.debug.main_view") --- @dep modules.gui.debug.main_view local DebugView = require("modules/exp_scenario/gui/debug/main_view")
local Commands = require("modules/exp_commands") local Commands = require("modules/exp_commands")
--- Opens the debug gui. --- Opens the debug gui.
+15 -5
View File
@@ -4,10 +4,12 @@ Adds a command that allows setting and teleporting to your home position
local ExpUtil = require("modules/exp_util") local ExpUtil = require("modules/exp_util")
local teleport = ExpUtil.teleport_player local teleport = ExpUtil.teleport_player
local Commands = require("modules/exp_commands") local Commands = require("modules/exp_commands")
local Storage = require("modules/exp_util/storage") local Storage = require("modules/exp_util/storage")
--- @class ExpCommand_Home.commands
local commands = {}
--- @type table<number, table<number, [MapPosition?, MapPosition?]>> --- @type table<number, table<number, [MapPosition?, MapPosition?]>>
local homes = {} -- homes[player_index][surface_index] = { home_pos, return_pos } local homes = {} -- homes[player_index][surface_index] = { home_pos, return_pos }
Storage.register(homes, function(tbl) Storage.register(homes, function(tbl)
@@ -28,7 +30,8 @@ local function align_to_grid(position)
end end
--- Teleports you to your home location on the current surface --- Teleports you to your home location on the current surface
Commands.new("home", { "exp-commands_home.description-home" }) --- @class ExpCommand_Home.commands.home: ExpCommand
commands.home = Commands.new("home", { "exp-commands_home.description-home" })
:add_flags{ "character_only" } :add_flags{ "character_only" }
:register(function(player) :register(function(player)
local surface = player.surface local surface = player.surface
@@ -50,7 +53,8 @@ Commands.new("home", { "exp-commands_home.description-home" })
end) end)
--- Teleports you to your previous location on the current surface --- Teleports you to your previous location on the current surface
Commands.new("return", { "exp-commands_home.description-return" }) --- @class ExpCommand_Home.commands._return: ExpCommand
commands.home_return = Commands.new("return", { "exp-commands_home.description-return" })
:add_flags{ "character_only" } :add_flags{ "character_only" }
:register(function(player) :register(function(player)
local surface = player.surface local surface = player.surface
@@ -72,7 +76,8 @@ Commands.new("return", { "exp-commands_home.description-return" })
end) end)
--- Sets your home location on your current surface to your current position --- Sets your home location on your current surface to your current position
Commands.new("set-home", { "exp-commands_home.description-set" }) --- @class ExpCommand_Home.commands.set_home: ExpCommand
commands.set_home = Commands.new("set-home", { "exp-commands_home.description-set" })
:add_flags{ "character_only" } :add_flags{ "character_only" }
:register(function(player) :register(function(player)
local home_position, floor_position = align_to_grid(player.position) local home_position, floor_position = align_to_grid(player.position)
@@ -97,7 +102,8 @@ Commands.new("set-home", { "exp-commands_home.description-set" })
end) end)
--- Gets your home location on your current surface, is allowed in remote view --- Gets your home location on your current surface, is allowed in remote view
Commands.new("get-home", { "exp-commands_home.description-get" }) --- @class ExpCommand_Home.commands.get_home: ExpCommand
commands.get_home = Commands.new("get-home", { "exp-commands_home.description-get" })
:register(function(player) :register(function(player)
local surface = player.surface local surface = player.surface
@@ -114,3 +120,7 @@ Commands.new("get-home", { "exp-commands_home.description-get" })
local _, floor_position = align_to_grid(player_home[1]) local _, floor_position = align_to_grid(player_home[1])
return Commands.status.success{ "exp-commands_home.home-get", surface.localised_name, floor_position.x, floor_position.y } return Commands.status.success{ "exp-commands_home.home-get", surface.localised_name, floor_position.x, floor_position.y }
end) end)
return {
commands = commands,
}
+33 -12
View File
@@ -2,17 +2,35 @@
Adds a command that clean up biter corpse and nuclear hole Adds a command that clean up biter corpse and nuclear hole
]] ]]
local AABB = require("modules/exp_util/aabb")
local Commands = require("modules/exp_commands") local Commands = require("modules/exp_commands")
local config = require("modules.exp_legacy.config.lawnmower") --- @dep config.lawnmower local Selection = require("modules/exp_util/selection")
local SelectArea = Selection.connect("ExpCommand_Lawnmower")
local config = require("modules.exp_legacy.config.lawnmower")
Commands.new("lawnmower", { "exp-commands_lawnmower.description" }) --- @class ExpCommand_Lawnmower.commands
:argument("range", { "exp-commands_lawnmower.arg-range" }, Commands.types.integer_range(1, 200)) local commands = {}
:register(function(player, range)
--- @cast range number
local surface = player.surface
-- Intentionally left as player.position to allow use in remote view --- Toggle player selection mode for lawnmower
local entities = surface.find_entities_filtered{ position = player.position, radius = range, type = "corpse" } --- @class ExpCommands_Lawnmower.commands.lawnmower: ExpCommand
--- @overload fun(player: LuaPlayer)
commands.lawnmower = Commands.new("lawnmower", { "exp-commands_lawnmower.description" })
:register(function(player)
if SelectArea:stop(player) then
return Commands.status.success{ "exp-commands_lawnmower.exit" }
end
SelectArea:start(player)
return Commands.status.success{ "exp-commands_lawnmower.enter" }
end) --[[ @as any ]]
--- When an area is selected to be handled
SelectArea:on_selection(function(event)
local player = assert(game.get_player(event.player_index))
local area = AABB.expand(event.area)
local surface = event.surface
local entities = surface.find_entities_filtered{ area = area, type = "corpse" }
for _, entity in pairs(entities) do for _, entity in pairs(entities) do
if (entity.name ~= "transport-caution-corpse" and entity.name ~= "invisible-transport-caution-corpse") then if (entity.name ~= "transport-caution-corpse" and entity.name ~= "invisible-transport-caution-corpse") then
entity.destroy() entity.destroy()
@@ -20,14 +38,16 @@ Commands.new("lawnmower", { "exp-commands_lawnmower.description" })
end end
local replace_tiles = {} local replace_tiles = {}
local tiles = surface.find_tiles_filtered{ position = player.position, radius = range, name = { "nuclear-ground" } } local tiles = surface.find_tiles_filtered{ area = area, name = { "nuclear-ground" } }
for i, tile in pairs(tiles) do for i, tile in pairs(tiles) do
replace_tiles[i] = { name = "grass-1", position = tile.position } replace_tiles[i] = { name = "grass-1", position = tile.position }
end end
surface.set_tiles(replace_tiles) surface.set_tiles(replace_tiles)
surface.destroy_decoratives{ position = player.position, radius = range } surface.destroy_decoratives{ area = area }
end)
player.print({ "exp-commands_lawnmower.complete", #replace_tiles }, Commands.print_settings.default)
end)
--- @param event EventData.on_built_entity | EventData.on_robot_built_entity | EventData.script_raised_built | EventData.script_raised_revive --- @param event EventData.on_built_entity | EventData.on_robot_built_entity | EventData.script_raised_built | EventData.script_raised_revive
local function destroy_decoratives(event) local function destroy_decoratives(event)
@@ -48,5 +68,6 @@ if config.destroy_decoratives then
end end
return { return {
events = events events = events,
commands = commands,
} }
+31 -14
View File
@@ -2,31 +2,45 @@
Adds a command that allows an admin to repair and revive a large area Adds a command that allows an admin to repair and revive a large area
]] ]]
local AABB = require("modules/exp_util/aabb")
local Commands = require("modules/exp_commands") local Commands = require("modules/exp_commands")
local config = require("modules.exp_legacy.config.repair") --- @dep config.repair local config = require("modules.exp_legacy.config.repair") --- @dep config.repair
local Selection = require("modules/exp_util/selection")
local SelectArea = Selection.connect("ExpCommand_Waterfill")
--- Repairs entities on your force around you --- @class ExpCommands_Repair.commands
Commands.new("repair", { "exp-commands_repair.description" }) local commands = {}
:argument("range", { "exp-commands_repair.arg-range" }, Commands.types.integer_range(1, config.max_range))
:register(function(player, range) --- Toggle player selection mode
--- @cast range number --- @class ExpCommands_Repair.commands.repair: ExpCommand
commands.repair = Commands.new("repair", { "exp-commands_repair.description" })
:register(function(player)
if SelectArea:stop(player) then
return Commands.status.success{ "exp-commands_repair.exit" }
end
SelectArea:start(player)
return Commands.status.success{ "exp-commands_repair.enter" }
end)
--- When an area is selected to be converted to water
SelectArea:on_selection(function(event)
local player = assert(game.get_player(event.player_index))
local area = AABB.expand(event.area)
local surface = event.surface
local force = player.force local force = player.force
local surface = player.surface -- Allow remote view
local position = player.position -- Allow remote view
local response = { "" } --- @type LocalisedString local response = { "" } --- @type LocalisedString
if config.allow_ghost_revive then if config.allow_ghost_revive then
local revive_count = 0 local revive_count = 0
local entities = surface.find_entities_filtered{ local entities = surface.find_entities_filtered{
type = "entity-ghost", type = "entity-ghost",
position = position, area = area,
radius = range,
force = force, force = force,
} }
local param = { raise_revive = true } --- @type LuaEntity.silent_revive_param local param = { raise_revive = true } --- @type LuaEntity.silent_revive_param
for _, entity in ipairs(entities) do for _, entity in ipairs(entities) do
if not config.disallow[entity.ghost_name] and (config.allow_blueprint_repair or entity.created_by_corpse) then if not (entity.ghost_prototype and entity.ghost_prototype.hidden) and (config.allow_blueprint_repair or entity.created_by_corpse) then
revive_count = revive_count + 1 revive_count = revive_count + 1
entity.silent_revive(param) entity.silent_revive(param)
end end
@@ -38,8 +52,7 @@ Commands.new("repair", { "exp-commands_repair.description" })
if config.allow_heal_entities then if config.allow_heal_entities then
local healed_count = 0 local healed_count = 0
local entities = surface.find_entities_filtered{ local entities = surface.find_entities_filtered{
position = position, area = area,
radius = range,
force = force, force = force,
} }
@@ -53,5 +66,9 @@ Commands.new("repair", { "exp-commands_repair.description" })
response[#response + 1] = { "exp-commands_repair.response-heal", healed_count } response[#response + 1] = { "exp-commands_repair.response-heal", healed_count }
end end
return Commands.status.success(response) return player.print(response)
end) end)
return {
commands = commands,
}
-90
View File
@@ -1,90 +0,0 @@
--[[-- Commands - Research
Adds a command to enable automatic research queueing
]]
local Storage = require("modules/exp_util/storage")
local Commands = require("modules/exp_commands")
local format_player_name = Commands.format_player_name_locale
local config = require("modules.exp_legacy.config.research") --- @dep config.research
--- @class ExpCommands_Research.commands
local commands = {}
local research = {
res_queue_enable = false
}
Storage.register(research, function(tbl)
research = tbl
end)
--- @param force LuaForce
--- @param silent boolean True when no message should be printed
local function queue_research(force, silent)
local res_q = force.research_queue
local res = force.technologies[config.bonus_inventory.log[config.mod_set].name]
if #res_q < config.queue_amount then
for i = #res_q, config.queue_amount - 1 do
force.add_research(res)
if not silent then
game.print{ "exp-commands_research.queue", res.name, res.level + i }
end
end
end
end
--- @param state boolean? use nil to toggle current state
--- @return boolean # New auto research state
local function set_auto_research(state)
local new_state
if state == nil then
new_state = not research.res_queue_enable
else
new_state = state ~= false
end
research.res_queue_enable = new_state
return new_state
end
--- Sets the auto research state
--- @class ExpCommand_Artillery.commands.artillery: ExpCommand
--- @overload fun(player: LuaPlayer, state: boolean?)
commands.set_auto_research = Commands.new("set-auto-research", { "exp-commands_research.description" })
:optional("state", { "exp-commands_research.arg-state" }, Commands.types.boolean)
:add_aliases{ "auto-research" }
:register(function(player, state)
--- @cast state boolean?
local enabled = set_auto_research(state)
if enabled then
queue_research(player.force --[[@as LuaForce]], true)
end
local player_name = format_player_name(player)
game.print{ "exp-commands_research.auto-research", player_name, enabled }
end) --[[ @as any ]]
--- @param event EventData.on_research_finished
local function on_research_finished(event)
if not research.res_queue_enable then return end
local force = event.research.force
local log_research = assert(config.bonus_inventory.log[config.mod_set], "Unknown mod set: " .. tostring(config.mod_set))
local technology = assert(force.technologies[log_research.name], "Unknown technology: " .. tostring(log_research.name))
if technology.level > log_research.level then
queue_research(force, event.by_script)
end
end
local e = defines.events
return {
commands = commands,
events = {
[e.on_research_finished] = on_research_finished,
},
}
+54 -53
View File
@@ -2,12 +2,17 @@
Adds a command that clear item on ground so blueprint can deploy safely Adds a command that clear item on ground so blueprint can deploy safely
]] ]]
local AABB = require("modules/exp_util/aabb")
local Commands = require("modules/exp_commands")
local ExpUtil = require("modules/exp_util") local ExpUtil = require("modules/exp_util")
local move_items = ExpUtil.move_items_to_surface local move_items = ExpUtil.move_items_to_surface
local Selection = require("modules/exp_util/selection")
local Commands = require("modules/exp_commands") local SelectArea = Selection.connect("ExpCommand_ClearBlueprint")
local format_player_name = Commands.format_player_name_locale local format_player_name = Commands.format_player_name_locale
--- @class ExpCommand_ClearBlueprint.commands
local commands = {}
--- @param surface LuaSurface --- @param surface LuaSurface
--- @return LuaItemStack[] --- @return LuaItemStack[]
local function get_ground_items(surface) local function get_ground_items(surface)
@@ -19,71 +24,67 @@ local function get_ground_items(surface)
return items return items
end end
--- Clear all items on the ground, optional to select a single surface --- Clear all items on the ground on a surface
Commands.new("clear-ground-items", { "exp-commands_surface.description-items" }) commands.clear_ground_items = Commands.new("clear-ground-items", { "exp-commands_surface.description-items" })
:optional("surface", { "exp-commands_surface.arg-surface" }, Commands.types.surface) :optional("surface", { "exp-commands_surface.arg-surface" }, Commands.types.surface)
:defaults{
surface = function(player) return player.surface end
}
:register(function(player, surface) :register(function(player, surface)
--- @cast surface LuaSurface? --- @cast surface LuaSurface
local player_name = format_player_name(player)
if surface then
move_items{ move_items{
surface = surface, surface = surface,
items = get_ground_items(surface), items = get_ground_items(surface),
allow_creation = true, allow_creation = true,
name = "iron-chest", name = "iron-chest",
} }
game.print{ "exp-commands_surface.items-surface", player_name, surface.localised_name } local player_name = format_player_name(player)
else game.print{ "exp-commands_surface.items", player_name, surface.localised_name }
for _, surface in pairs(game.surfaces) do
move_items{
surface = surface,
items = get_ground_items(surface),
allow_creation = true,
name = "iron-chest",
}
end
game.print{ "exp-commands_surface.items-all", player_name }
end
end) end)
--- Clear all blueprints, optional to select a single surface --- Clear all blueprints on a surface
Commands.new("clear-blueprints", { "exp-commands_surface.description-blueprints" }) commands.clear_blueprints_surface = Commands.new("clear-blueprints-surface", { "exp-commands_surface.description-blueprints-surface" })
:optional("surface", { "exp-commands_surface.arg-surface" }, Commands.types.surface) :optional("surface", { "exp-commands_surface.arg-surface" }, Commands.types.surface)
:register(function(player, surface) :defaults{
--- @cast surface LuaSurface? surface = function(player) return player.surface end
local player_name = format_player_name(player)
if surface then
local entities = surface.find_entities_filtered{ type = "entity-ghost" }
for _, entity in ipairs(entities) do
entity.destroy()
end
game.print{ "exp-commands_surface.blueprint-surface", player_name, surface.localised_name }
else
for _, surface in pairs(game.surfaces) do
local entities = surface.find_entities_filtered{ type = "entity-ghost" }
for _, entity in ipairs(entities) do
entity.destroy()
end
end
game.print{ "exp-commands_surface.blueprint-all", player_name }
end
end)
--- Clear all blueprints in a radius around you
Commands.new("clear-blueprints-radius", { "exp-commands_surface.description-radius" })
:argument("radius", { "exp-commands_surface.arg-radius" }, Commands.types.number_range(1, 100))
:register(function(player, radius)
--- @cast radius number
local player_name = format_player_name(player)
local entities = player.surface.find_entities_filtered{
type = "entity-ghost",
position = player.position,
radius = radius,
} }
:register(function(player, surface)
--- @cast surface LuaSurface
local entities = surface.find_entities_filtered{ type = "entity-ghost" }
for _, entity in ipairs(entities) do
entity.destroy()
end
local player_name = format_player_name(player)
game.print{ "exp-commands_surface.blueprints", player_name, surface.localised_name }
end)
--- Clear all blueprint in the area, selected by toggle player selection mode
--- @class ExpCommands_ClearBlueprint.commands.clear_blueprint: ExpCommand
--- @overload fun(player: LuaPlayer)
commands.clear_blueprints = Commands.new("clear-blueprints", { "exp-commands_surface.description-blueprints" })
:register(function(player)
if SelectArea:stop(player) then
return Commands.status.success{ "exp-commands_surface.exit" }
end
SelectArea:start(player)
return Commands.status.success{ "exp-commands_surface.enter" }
end) --[[ @as any ]]
--- When an area is selected
SelectArea:on_selection(function(event)
local player = assert(game.get_player(event.player_index))
local area = AABB.expand(event.area)
local surface = event.surface
local entities = surface.find_entities_filtered{ type = "entity-ghost", area = area }
for _, entity in ipairs(entities) do for _, entity in ipairs(entities) do
entity.destroy() entity.destroy()
end end
game.print{ "exp-commands_surface.blueprint-radius", player_name, radius, player.surface.localised_name } game.print({ "exp-commands_surface.complete", #entities }, Commands.print_settings.default)
end) end)
return {
commands = commands,
}
+9 -1
View File
@@ -5,8 +5,12 @@ Adds a virtual layer to store power to save space.
local Commands = require("modules/exp_commands") local Commands = require("modules/exp_commands")
local vlayer = require("modules.exp_legacy.modules.control.vlayer") local vlayer = require("modules.exp_legacy.modules.control.vlayer")
--- @class ExpCommand_vlayer.commands
local commands = {}
--- Print all vlayer information --- Print all vlayer information
Commands.new("vlayer-info", { "exp-commands_vlayer.description" }) --- @class ExpCommands_vlayer.commands.vlayer: ExpCommand
commands.vlayer = Commands.new("vlayer-info", { "exp-commands_vlayer.description" })
:register(function(player) :register(function(player)
local index = 3 local index = 3
local response = { "", "exp-commands_vlayer.title" } --- @type LocalisedString local response = { "", "exp-commands_vlayer.title" } --- @type LocalisedString
@@ -16,3 +20,7 @@ Commands.new("vlayer-info", { "exp-commands_vlayer.description" })
end end
return Commands.status.success(response) return Commands.status.success(response)
end) end)
return {
commands = commands,
}
+1 -2
View File
@@ -41,6 +41,7 @@ commands.waterfill = Commands.new("waterfill", { "exp-commands_waterfill.descrip
--- When an area is selected to be converted to water --- When an area is selected to be converted to water
SelectArea:on_selection(function(event) SelectArea:on_selection(function(event)
local area = AABB.expand(event.area) local area = AABB.expand(event.area)
local area_size = AABB.size(area)
local player = game.players[event.player_index] local player = game.players[event.player_index]
local surface = event.surface local surface = event.surface
@@ -51,8 +52,6 @@ SelectArea:on_selection(function(event)
end end
]] ]]
local area_size = (area.right_bottom.x - area.left_top.x) * (area.right_bottom.y - area.left_top.y)
if area_size > 1000 then if area_size > 1000 then
player.print({ "exp-commands_waterfill.area-too-large", 1000, area_size }, Commands.print_settings.error) player.print({ "exp-commands_waterfill.area-too-large", 1000, area_size }, Commands.print_settings.error)
return return
+3 -2
View File
@@ -11,7 +11,6 @@ require("modules/exp_scenario/commands/_types")
--- Commands with events --- Commands with events
add(require("modules/exp_scenario/commands/protected_entities")) add(require("modules/exp_scenario/commands/protected_entities"))
add(require("modules/exp_scenario/commands/protected_tags")) add(require("modules/exp_scenario/commands/protected_tags"))
add(require("modules/exp_scenario/commands/research"))
--- Commands --- Commands
require("modules/exp_scenario/commands/admin_chat") require("modules/exp_scenario/commands/admin_chat")
@@ -70,9 +69,9 @@ add(require("modules/exp_scenario/control/station_auto_name"))
--- Guis --- Guis
add(require("modules/exp_scenario/gui/autofill")) add(require("modules/exp_scenario/gui/autofill"))
add(require("modules/exp_scenario/gui/elements")) add(require("modules/exp_scenario/gui/elements"))
add(require("modules/exp_scenario/gui/landfill_blueprint"))
add(require("modules/exp_scenario/gui/module_inserter")) add(require("modules/exp_scenario/gui/module_inserter"))
add(require("modules/exp_scenario/gui/player_bonus")) add(require("modules/exp_scenario/gui/player_bonus"))
add(require("modules/exp_scenario/gui/player_list"))
add(require("modules/exp_scenario/gui/player_stats")) add(require("modules/exp_scenario/gui/player_stats"))
add(require("modules/exp_scenario/gui/production_stats")) add(require("modules/exp_scenario/gui/production_stats"))
add(require("modules/exp_scenario/gui/quick_actions")) add(require("modules/exp_scenario/gui/quick_actions"))
@@ -82,3 +81,5 @@ add(require("modules/exp_scenario/gui/rocket_info"))
add(require("modules/exp_scenario/gui/science_production")) add(require("modules/exp_scenario/gui/science_production"))
add(require("modules/exp_scenario/gui/surveillance")) add(require("modules/exp_scenario/gui/surveillance"))
add(require("modules/exp_scenario/gui/task_list")) add(require("modules/exp_scenario/gui/task_list"))
add(require("modules/exp_scenario/gui/debug/shim"))
add(require("modules/exp_scenario/gui/debug/event_view"))
+2 -2
View File
@@ -11,7 +11,7 @@ local config = require("modules/exp_legacy/config/bonus")
local function apply_force_bonus(event) local function apply_force_bonus(event)
local force = event.force local force = event.force
for k, v in pairs(config.force_bonus) do for k, v in pairs(config.force_bonus) do
force[k] = v.initial_value force[k] = math.floor(v.max_value / 2)
end end
end end
@@ -19,7 +19,7 @@ end
local function apply_surface_bonus(event) local function apply_surface_bonus(event)
local surface = assert(game.get_surface(event.surface_index)) local surface = assert(game.get_surface(event.surface_index))
for k, v in pairs(config.surface_bonus) do for k, v in pairs(config.surface_bonus) do
surface[k] = v.initial_value surface[k] = math.floor(v.max_value / 2)
end end
end end
+49 -8
View File
@@ -43,7 +43,7 @@ local function prevent_deconstruction(entity)
end end
-- Not minable, selectable, or deconstructive -- Not minable, selectable, or deconstructive
if not entity.minable or not entity.prototype.selectable_in_game or entity.has_flag("not-deconstructable") then if not entity.minable_flag or not entity.prototype.selectable_in_game or entity.has_flag("not-deconstructable") then
return true return true
end end
@@ -88,6 +88,37 @@ local function try_deconstruct_output_chest(entity)
order_deconstruction_async:start_after(10, target) order_deconstruction_async:start_after(10, target)
end end
--- Check if beacon should be deconstructed
--- @param entity LuaEntity
local function try_deconstruct_nearby_beacon(entity)
-- Get a valid chest as the target
local beacons = entity.get_beacons()
local beacon_in_use = false
-- if beacon is not supported
if not beacons then
return
end
for _, b in pairs(beacons) do
if not b.to_be_deconstructed() then
for _, r in pairs(b.get_beacon_effect_receivers()) do
if r ~= entity then
if not r.to_be_deconstructed() then
beacon_in_use = true
break
end
end
end
-- Deconstruct the beacon
if not beacon_in_use then
order_deconstruction_async:start_after(10, { name = b.name, position = b.position })
end
end
end
end
--- Check if a miner should be deconstructed --- Check if a miner should be deconstructed
--- @param entity LuaEntity --- @param entity LuaEntity
local function try_deconstruct_miner(entity) local function try_deconstruct_miner(entity)
@@ -117,11 +148,13 @@ local function try_deconstruct_miner(entity)
try_deconstruct_output_chest(entity) try_deconstruct_output_chest(entity)
end end
--[[ -- Try deconstruct the beacon
TODO Fluidbox is changed to fluidbox_neighbours if config.beacon then
try_deconstruct_nearby_beacon(entity)
end
-- Skip pipe build if not required -- Skip pipe build if not required
if not config.fluid or #entity.fluidbox == 0 then if not config.fluid or entity.fluids_count > 1 then
return return
end end
@@ -189,11 +222,19 @@ local function try_deconstruct_miner(entity)
]] ]]
end end
local max_quality = ""
for _, q in pairs(prototypes.quality) do
if not q.next then
max_quality = q.name
end
end
--- Get the max mining radius --- Get the max mining radius
local max_mining_radius = 0 local max_mining_radius = 0
for _, proto in pairs(prototypes.get_entity_filtered{ { filter = "type", type = "mining-drill" } }) do for _, proto in pairs(prototypes.get_entity_filtered{ { filter = "type", type = "mining-drill" } }) do
if proto.mining_drill_radius > max_mining_radius then if proto.mining_drill_radius then
max_mining_radius = proto.mining_drill_radius max_mining_radius = math.max(assert(proto.get_mining_drill_radius(max_quality)), max_mining_radius)
end end
end end
@@ -218,8 +259,8 @@ local function on_resource_depleted(event)
-- Check which could have reached this resource -- Check which could have reached this resource
for _, drill in pairs(drills) do for _, drill in pairs(drills) do
local radius = drill.prototype.mining_drill_radius local radius = drill.prototype.mining_drill_radius
local dx = math.abs(drill.position.x - resource.position.x) local dx = math.abs(drill.position.x - position.x)
local dy = math.abs(drill.position.y - resource.position.y) local dy = math.abs(drill.position.y - position.y)
if dx <= radius and dy <= radius then if dx <= radius and dy <= radius then
try_deconstruct_miner(drill) try_deconstruct_miner(drill)
end end
+2
View File
@@ -41,6 +41,8 @@ local e = defines.events
return { return {
events = { events = {
[e.on_research_finished] = on_research_finished, [e.on_research_finished] = on_research_finished,
[e.on_research_reversed] = on_research_finished,
[e.on_research_started] = on_research_started, [e.on_research_started] = on_research_started,
[e.on_research_queued] = on_research_started,
} }
} }
+1 -1
View File
@@ -56,7 +56,7 @@ end
local function protect_entity(entity) local function protect_entity(entity)
if entity and entity.valid then if entity and entity.valid then
entity.destructible = false entity.destructible = false
entity.minable = false entity.minable_flag = false
entity.rotatable = false entity.rotatable = false
entity.operable = false entity.operable = false
end end
@@ -1,5 +1,5 @@
local Gui = require("modules.exp_legacy.utils.gui") --- @dep utils.gui local Gui = require("modules/exp_scenario/gui/debug/shim")
local Model = require("modules.exp_legacy.modules.gui.debug.model") --- @dep modules.gui.debug.model local Model = require("modules/exp_scenario/gui/debug/model")
local Color = require("modules/exp_util/include/color") local Color = require("modules/exp_util/include/color")
local dump = Model.dump local dump = Model.dump
@@ -1,7 +1,6 @@
local Event = require("modules/exp_legacy/utils/event")
local Storage = require("modules/exp_util/storage") local Storage = require("modules/exp_util/storage")
local Gui = require("modules.exp_legacy.utils.gui") local Gui = require("modules/exp_scenario/gui/debug/shim")
local Model = require("modules.exp_legacy.modules.gui.debug.model") local Model = require("modules/exp_scenario/gui/debug/model")
local format = string.format local format = string.format
local insert = table.insert local insert = table.insert
@@ -14,6 +13,7 @@ local events_to_keep = 10
-- Local vars -- Local vars
local Public = { local Public = {
name = "Events", name = "Events",
events = {}
} }
local name_lookup = {} local name_lookup = {}
@@ -168,7 +168,7 @@ Gui.on_click(
-- Event registers (TODO: turn to removable hooks.. maybe) -- Event registers (TODO: turn to removable hooks.. maybe)
for name, id in pairs(events) do for name, id in pairs(events) do
name_lookup[id] = name name_lookup[id] = name
Event.add(id, event_callback) Public.events[id] = event_callback
end end
return Public return Public
@@ -1,7 +1,7 @@
local Gui = require("modules.exp_legacy.utils.gui") --- @dep utils.gui local Gui = require("modules/exp_scenario/gui/debug/shim")
local Datastore = require("modules.exp_legacy.expcore.datastore") --- @dep expcore.datastore local Datastore = require("modules.exp_legacy.expcore.datastore")
local Color = require("modules/exp_util/include/color") local Color = require("modules/exp_util/include/color")
local Model = require("modules.exp_legacy.modules.gui.debug.model") --- @dep modules.gui.debug.model local Model = require("modules/exp_scenario/gui/debug/model")
local dump = Model.dump local dump = Model.dump
local concat = table.concat local concat = table.concat
@@ -3,8 +3,8 @@ local ExpData = require("modules/exp_gui/data")
local ExpIter = require("modules/exp_gui/iter") local ExpIter = require("modules/exp_gui/iter")
local Color = require("modules/exp_util/include/color") local Color = require("modules/exp_util/include/color")
local Gui = require("modules.exp_legacy.utils.gui") --- @dep utils.gui local Gui = require("modules/exp_scenario/gui/debug/shim")
local Model = require("modules.exp_legacy.modules.gui.debug.model") --- @dep modules.gui.debug.model local Model = require("modules/exp_scenario/gui/debug/model")
local dump = Model.dump local dump = Model.dump
local dump_text = Model.dump_text local dump_text = Model.dump_text
@@ -1,5 +1,5 @@
local Gui = require("modules.exp_legacy.utils.gui") --- @dep utils.gui local Gui = require("modules/exp_scenario/gui/debug/shim")
local Model = require("modules.exp_legacy.modules.gui.debug.model") --- @dep modules.gui.debug.model local Model = require("modules/exp_scenario/gui/debug/model")
local Color = require("modules/exp_util/include/color") local Color = require("modules/exp_util/include/color")
local dump = Model.dump local dump = Model.dump
@@ -1,16 +1,16 @@
local Gui = require("modules.exp_legacy.utils.gui") --- @dep utils.gui local Gui = require("modules/exp_scenario/gui/debug/shim")
local Color = require("modules/exp_util/include/color") local Color = require("modules/exp_util/include/color")
local Public = {} local Public = {}
local pages = { local pages = {
require("modules.exp_legacy.modules.gui.debug.redmew_global_view"), require("modules/exp_scenario/gui/debug/redmew_global_view"),
require("modules.exp_legacy.modules.gui.debug.expcore_datastore_view"), require("modules/exp_scenario/gui/debug/expcore_datastore_view"),
require("modules.exp_legacy.modules.gui.debug.expcore_gui_view"), require("modules/exp_scenario/gui/debug/expcore_gui_view"),
require("modules.exp_legacy.modules.gui.debug.global_view"), require("modules/exp_scenario/gui/debug/global_view"),
require("modules.exp_legacy.modules.gui.debug.package_view"), require("modules/exp_scenario/gui/debug/package_view"),
require("modules.exp_legacy.modules.gui.debug._g_view"), require("modules/exp_scenario/gui/debug/_g_view"),
require("modules.exp_legacy.modules.gui.debug.event_view"), require("modules/exp_scenario/gui/debug/event_view"),
} }
local main_frame_name = Gui.uid_name() local main_frame_name = Gui.uid_name()
@@ -1,4 +1,4 @@
local Gui = require("modules.exp_legacy.utils.gui") --- @dep utils.gui local Gui = require("modules/exp_scenario/gui/debug/shim") --- @dep utils.gui
local ExpUtil = require("modules/exp_util") local ExpUtil = require("modules/exp_util")
local concat = table.concat local concat = table.concat
@@ -1,6 +1,6 @@
local Gui = require("modules.exp_legacy.utils.gui") --- @dep utils.gui local Gui = require("modules/exp_scenario/gui/debug/shim") --- @dep utils.gui
local Color = require("modules/exp_util/include/color") local Color = require("modules/exp_util/include/color")
local Model = require("modules.exp_legacy.modules.gui.debug.model") --- @dep modules.gui.debug.model local Model = require("modules/exp_scenario/gui/debug/model") --- @dep modules.gui.debug.model
local dump_function = Model.dump_function local dump_function = Model.dump_function
local loaded = _G.package.loaded local loaded = _G.package.loaded
@@ -1,7 +1,7 @@
local Gui = require("modules.exp_legacy.utils.gui") --- @dep utils.gui local Gui = require("modules/exp_scenario/gui/debug/shim") --- @dep utils.gui
local Storage = require("modules/exp_util/storage") local Storage = require("modules/exp_util/storage")
local Color = require("modules/exp_util/include/color") local Color = require("modules/exp_util/include/color")
local Model = require("modules.exp_legacy.modules.gui.debug.model") --- @dep modules.gui.debug.model local Model = require("modules/exp_scenario/gui/debug/model") --- @dep modules.gui.debug.model
local dump = Model.dump local dump = Model.dump
local dump_text = Model.dump_text local dump_text = Model.dump_text
@@ -1,5 +1,4 @@
local Storage = require("modules/exp_util/storage") local Storage = require("modules/exp_util/storage")
local Event = require("modules/exp_legacy/utils/event")
local mod_gui = require "mod-gui" local mod_gui = require "mod-gui"
local Gui = {} local Gui = {}
@@ -70,14 +69,15 @@ function Gui.clear(element)
element.clear() element.clear()
end end
Gui.events = {}
local function handler_factory(event_name) local function handler_factory(event_name)
return function(element_name, handler) return function(element_name, handler)
Event.add(defines.events[event_name], function(event) Gui.events[defines.events[event_name]] = function(event)
if event.element and event.element.valid and event.element.name == element_name then if event.element and event.element.valid and event.element.name == element_name then
event.player = game.players[event.player_index] event.player = game.players[event.player_index]
handler(event) handler(event)
end end
end) end
end end
end end
@@ -1,175 +0,0 @@
--[[-- Gui - Landfill Blueprint
Adds a button to the toolbar which adds landfill to the held blueprint
]]
local Gui = require("modules/exp_gui")
local Roles = require("modules/exp_legacy/expcore/roles")
--- @param box BoundingBox
local function rotate_bounding_box(box)
box.left_top.x, box.left_top.y, box.right_bottom.x, box.right_bottom.y
= -box.right_bottom.y, box.left_top.x, -box.left_top.y, box.right_bottom.x
end
local function curve_flip_lr(oc)
local nc = table.deep_copy(oc)
for r = 1, 8 do
for c = 1, 8 do
nc[r][c] = oc[r][9 - c]
end
end
return nc
end
local function curve_flip_d(oc)
local nc = table.deep_copy(oc)
for r = 1, 8 do
for c = 1, 8 do
nc[r][c] = oc[c][r]
end
end
return nc
end
local curve_masks = {} do
local curves = { {
{ 0, 0, 0, 0, 0, 1, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 0 },
{ 0, 0, 0, 1, 1, 1, 1, 0 },
{ 0, 0, 0, 1, 1, 1, 0, 0 },
{ 0, 0, 1, 1, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0 },
} }
curves[6] = curve_flip_d(curves[1])
curves[3] = curve_flip_lr(curves[6])
curves[4] = curve_flip_d(curves[3])
curves[5] = curve_flip_lr(curves[4])
curves[2] = curve_flip_d(curves[5])
curves[7] = curve_flip_lr(curves[2])
curves[8] = curve_flip_d(curves[7])
for i, map in ipairs(curves) do
local index = 0
local mask = {}
curve_masks[i] = mask
for row = 1, 8 do
for col = 1, 8 do
if map[row][col] == 1 then
index = index + 1
mask[index] = {
x = col - 5,
y = row - 5,
}
end
end
end
end
end
local rolling_stocks = {}
for name, _ in pairs(prototypes.get_entity_filtered{ { filter = "rolling-stock" } }) do
rolling_stocks[name] = true
end
--- @param blueprint LuaItemStack
--- @return table
local function landfill_gui_add_landfill(blueprint)
local entities = assert(blueprint.get_blueprint_entities())
local tile_index = 0
local new_tiles = {}
for _, entity in pairs(entities) do
if rolling_stocks[entity.name] or entity.name == "offshore-pump" then
goto continue
end
if entity.name == "curved-rail" then
-- Curved rail
local curve_mask = curve_masks[entity.direction or 8]
for _, offset in ipairs(curve_mask) do
tile_index = tile_index + 1
new_tiles[tile_index] = {
name = "landfill",
position = { entity.position.x + offset.x, entity.position.y + offset.y },
}
end
else
-- Any other entity
local proto = prototypes.entity[entity.name]
if proto.collision_mask["ground-tile"] ~= nil then
goto continue
end
-- Rotate the collision box to be north facing
local box = proto.collision_box or proto.selection_box
if entity.direction then
if entity.direction ~= defines.direction.north then
rotate_bounding_box(box)
if entity.direction ~= defines.direction.east then
rotate_bounding_box(box)
if entity.direction ~= defines.direction.south then
rotate_bounding_box(box)
end
end
end
end
-- Add the landfill
for y = math.floor(entity.position.y + box.left_top.y), math.floor(entity.position.y + box.right_bottom.y), 1 do
for x = math.floor(entity.position.x + box.left_top.x), math.floor(entity.position.x + box.right_bottom.x), 1 do
tile_index = tile_index + 1
new_tiles[tile_index] = {
name = "landfill",
position = { x, y },
}
end
end
end
::continue::
end
local old_tiles = blueprint.get_blueprint_tiles()
if old_tiles then
for _, old_tile in pairs(old_tiles) do
tile_index = tile_index + 1
new_tiles[tile_index] = {
name = "landfill",
position = old_tile.position,
}
end
end
return { tiles = new_tiles }
end
--- Add the toolbar button
Gui.toolbar.create_button{
name = "trigger_landfill_blueprint",
sprite = "item/landfill",
tooltip = { "exp-gui_landfill-blueprint.tooltip-main" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/landfill")
end
}:on_click(function(def, player, element)
local stack = player.cursor_stack
if stack and stack.valid_for_read and stack.type == "blueprint" and stack.is_blueprint_setup() then
local modified = landfill_gui_add_landfill(stack)
if modified and next(modified.tiles) then
stack.set_blueprint_tiles(modified.tiles)
end
else
player.print{ "exp-gui_landfill-blueprint.error-no-blueprint" }
end
end)
return {}

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

+2 -3
View File
@@ -16,7 +16,6 @@ local Elements = {}
--- @field cost number --- @field cost number
--- @field scale number --- @field scale number
--- @field max_value number --- @field max_value number
--- @field initial_value number
--- @field is_percentage boolean --- @field is_percentage boolean
--- @field is_special boolean --- @field is_special boolean
--- @field value_step number --- @field value_step number
@@ -229,7 +228,7 @@ Elements.bonus_slider = Gui.define("player_bonus/bonus_slider")
local player = Gui.get_player(parent) local player = Gui.get_player(parent)
local value = Elements.container.get_player_bonus(player, bonus_data.name) local value = Elements.container.get_player_bonus(player, bonus_data.name)
if not value then if not value then
value = bonus_data.initial_value value = math.floor(bonus_data.max_value / 2)
elements.apply_button.enabled = true elements.apply_button.enabled = true
end end
@@ -290,7 +289,7 @@ function Elements.bonus_slider.reset_value(slider)
local player = Gui.get_player(slider) local player = Gui.get_player(slider)
local element_data = Elements.bonus_slider.data[slider] local element_data = Elements.bonus_slider.data[slider]
local bonus_data = element_data.bonus_data local bonus_data = element_data.bonus_data
local value = Elements.container.get_player_bonus(player, bonus_data.name) or bonus_data.initial_value local value = Elements.container.get_player_bonus(player, bonus_data.name) or math.floor(bonus_data.max_value / 2)
slider.slider_value = value slider.slider_value = value
element_data.label.caption = Elements.bonus_slider.calculate_slider_caption(bonus_data, value) element_data.label.caption = Elements.bonus_slider.calculate_slider_caption(bonus_data, value)
element_data.previous_value = value element_data.previous_value = value
+543
View File
@@ -0,0 +1,543 @@
--[[-- Gui - Player List
Adds a player list to show names and play time; also includes action buttons which can perform actions on players.
]]
local ExpUtil = require("modules/exp_util")
local Gui = require("modules/exp_gui")
local Roles = require("modules/exp_legacy/expcore/roles")
local config = require("modules/exp_legacy/config/gui/player_list_actions")
--- @class ExpGui_PlayerList.elements
local Elements = {}
local online_time_format = ExpUtil.format_time_factory_locale{ format = "short", hours = true, minutes = true }
local afk_time_format = ExpUtil.format_time_factory_locale{ format = "long", minutes = true }
--- Get the caption and tooltip for a player time
--- @param online_time number
--- @param afk_time number
--- @return LocalisedString, LocalisedString
local function get_time_formats(online_time, afk_time)
local tick = game.tick > 0 and game.tick or 1
local percent = math.round(online_time / tick, 3) * 100
local caption = online_time_format(online_time)
local tooltip = { "exp-gui_player-list.afk-time", percent, afk_time_format(afk_time) }
return caption, tooltip
end
--- Button used to open the action bar for a player
--- @class ExpGui_PlayerList.elements.open_action_bar: ExpElement
--- @field data table<LuaGuiElement, LuaPlayer>
--- @overload fun(parent: LuaGuiElement, selected_player: LuaPlayer): LuaGuiElement
Elements.open_action_bar = Gui.define("player_list/open_action_bar")
:draw{
type = "sprite-button",
sprite = "utility/expand_dots",
tooltip = { "exp-gui_player-list.open-action-bar" },
style = "frame_button",
}
:style{
padding = -2,
width = 8,
height = 14,
}
:element_data(
Gui.from_argument(1)
)
:on_click(function(def, player, element)
--- @cast def ExpGui_PlayerList.elements.open_action_bar
Elements.container.toggle_selected_player(player, def.data[element])
Elements.player_table.refresh_player(player)
end) --[[ @as any ]]
--- Set whether an open action bar button shows as highlighted
--- @param open_button LuaGuiElement
--- @param highlighted boolean
function Elements.open_action_bar.set_highlight(open_button, highlighted)
open_button.style = highlighted and "tool_button" or "frame_button"
local style = open_button.style
style.padding = -2
style.width = 8
style.height = 14
end
--- Button used to close the action bar
--- @class ExpGui_PlayerList.elements.close_action_bar: ExpElement
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
Elements.close_action_bar = Gui.define("player_list/close_action_bar")
:draw{
type = "sprite-button",
sprite = "utility/close_black",
tooltip = { "exp-gui_player-list.close-action-bar" },
style = "slot_sized_button_red",
}
:style{
size = 30,
padding = -1,
top_margin = -1,
right_margin = -1,
}
:on_click(function(_, player)
Elements.container.set_selected_player(player, nil)
Elements.player_table.refresh_player(player)
end) --[[ @as any ]]
--- Button used to confirm a reason
--- @class ExpGui_PlayerList.elements.reason_confirm: ExpElement
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
Elements.reason_confirm = Gui.define("player_list/reason_confirm")
:draw{
type = "sprite-button",
sprite = "utility/confirm_slot",
tooltip = { "exp-gui_player-list.reason-confirm" },
style = "slot_sized_button_green",
}
:style{
size = 30,
padding = -1,
left_margin = -2,
right_margin = -1,
}
:on_click(function(_, player, element)
local action_name = Elements.container.get_selected_action(player)
local button_data = action_name and config.buttons[action_name]
if button_data and button_data.reason_callback then
local reason = element.parent.entry.text
if reason == nil or not reason:find("%S") then reason = "no reason given" end
button_data.reason_callback(player, reason)
end
element.parent.entry.text = ""
Elements.container.set_selected_player(player, nil)
Elements.player_table.refresh_player(player)
end) --[[ @as any ]]
--- Clickable player name label, left click opens the map and right click toggles the action bar
--- @class ExpGui_PlayerList.elements.player_name_label: ExpElement
--- @field data table<LuaGuiElement, LuaPlayer>
--- @overload fun(parent: LuaGuiElement, opts: { name: string, player: LuaPlayer, tooltip: LocalisedString }): LuaGuiElement
Elements.player_name_label = Gui.define("player_list/player_name_label")
:draw{
type = "label",
caption = Gui.from_argument("name"),
tooltip = Gui.from_argument("tooltip"),
}
:style{
padding = { 0, 2, 0, 0 },
}
:element_data(
Gui.from_argument("player")
)
:on_click(function(def, player, element, event)
--- @cast def ExpGui_PlayerList.elements.player_name_label
local selected_player = def.data[element]
if event.button == defines.mouse_button_type.left then
-- Left click opens remote view at the player
player.set_controller{
type = defines.controllers.remote,
position = selected_player.physical_position,
surface = selected_player.physical_surface,
}
else
-- Right click toggles the action bar
Elements.container.toggle_selected_player(player, selected_player)
Elements.player_table.refresh_player(player)
end
end) --[[ @as any ]]
--- @class ExpGui_PlayerList.elements.player_table.row
--- @field open_button LuaGuiElement
--- @field name_label LuaGuiElement
--- @field time_label LuaGuiElement
--- @class ExpGui_PlayerList.elements.player_table.element_data
--- @field rows table<string, ExpGui_PlayerList.elements.player_table.row>
--- @field action_bar LuaGuiElement
--- @field reason_bar LuaGuiElement
--- Scroll table containing a row for each online player
--- @class ExpGui_PlayerList.elements.player_table: ExpElement
--- @field data table<LuaGuiElement, ExpGui_PlayerList.elements.player_table.element_data>
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
Elements.player_table = Gui.define("player_list/player_table")
:track_all_elements()
:draw(function(_, parent)
return Gui.elements.scroll_table(parent, 184, 3, "scroll")
end)
:style{
padding = { 1, 0, 1, 2 },
} --[[ @as any ]]
--- Store the action and reason bars associated with a player table
--- @param player_table LuaGuiElement
--- @param action_bar LuaGuiElement
--- @param reason_bar LuaGuiElement
function Elements.player_table.set_bars(player_table, action_bar, reason_bar)
Elements.player_table.data[player_table] = {
rows = {},
action_bar = action_bar,
reason_bar = reason_bar,
}
end
--- @class ExpGui_PlayerList.elements.player_table.row_data
--- @field player LuaPlayer
--- @field tag string
--- @field role_name string
--- @field chat_color Color
--- @field caption LocalisedString
--- @field tooltip LocalisedString
--- Calculate the ordered list of online players sorted by their highest role
--- @return ExpGui_PlayerList.elements.player_table.row_data[]
function Elements.player_table.calculate_row_data()
-- Sort all the online players into roles
local players = {}
for _, player in pairs(game.connected_players) do
local highest_role = Roles.get_player_highest_role(player)
if not players[highest_role.name] then
players[highest_role.name] = {}
end
table.insert(players[highest_role.name], player)
end
-- Flatten the roles into a single ordered list
local count = 0
local row_data = {}
for _, role_name in pairs(Roles.config.order) do
if players[role_name] then
for _, player in pairs(players[role_name]) do
count = count + 1
local caption, tooltip = get_time_formats(player.online_time, player.afk_time)
row_data[count] = {
player = player,
tag = player.tag,
role_name = role_name,
chat_color = player.chat_color,
caption = caption,
tooltip = tooltip,
}
end
end
end
return row_data
end
--- Calculate the latest play time captions for each online player keyed by name
--- @return table<string, { caption: LocalisedString, tooltip: LocalisedString }>
function Elements.player_table.calculate_time_data()
local time_data = {}
for _, player in pairs(game.connected_players) do
local caption, tooltip = get_time_formats(player.online_time, player.afk_time)
time_data[player.name] = { caption = caption, tooltip = tooltip }
end
return time_data
end
--- Add a player row to the table and store its elements
--- @param player_table LuaGuiElement
--- @param row_data ExpGui_PlayerList.elements.player_table.row_data
function Elements.player_table.add_row(player_table, row_data)
local rows = Elements.player_table.data[player_table].rows
local player = row_data.player
-- Add the button used to open the action bar
local open_flow = player_table.add{ type = "flow" }
local open_button = Elements.open_action_bar(open_flow, player)
-- Add the clickable player name
local name_label = Elements.player_name_label(player_table, {
name = player.name,
player = player,
tooltip = { "exp-gui_player-list.open-map", player.name, row_data.tag, row_data.role_name },
})
name_label.style.font_color = row_data.chat_color
-- Add the time played label
local alignment = Gui.elements.aligned_flow(player_table)
local time_label = alignment.add{
type = "label",
caption = row_data.caption,
tooltip = row_data.tooltip,
}
time_label.style.padding = 0
rows[player.name] = { open_button = open_button, name_label = name_label, time_label = time_label }
end
--- Rebuild all rows of a player table, used when the sort order changes
--- @param player_table LuaGuiElement
--- @param row_data ExpGui_PlayerList.elements.player_table.row_data[]
function Elements.player_table.rebuild(player_table, row_data)
player_table.clear()
Elements.player_table.data[player_table].rows = {}
for _, row in ipairs(row_data) do
Elements.player_table.add_row(player_table, row)
end
end
--- Remove a single player row from the table
--- @param player_table LuaGuiElement
--- @param player_name string
function Elements.player_table.remove_row(player_table, player_name)
local rows = Elements.player_table.data[player_table].rows
local row = rows[player_name]
if not row then return end
rows[player_name] = nil
Gui.destroy_if_valid(row.open_button.parent)
Gui.destroy_if_valid(row.name_label)
Gui.destroy_if_valid(row.time_label.parent)
end
--- Refresh the time labels of a player table from precomputed time data
--- @param player_table LuaGuiElement
--- @param time_data table<string, { caption: LocalisedString, tooltip: LocalisedString }>
function Elements.player_table.refresh_times(player_table, time_data)
for player_name, row in pairs(Elements.player_table.data[player_table].rows) do
local data = time_data[player_name]
if data then
row.time_label.caption = data.caption
row.time_label.tooltip = data.tooltip
end
end
end
--- Refresh the action bar, reason bar and row highlights for a player to match their selection
--- @param player LuaPlayer
function Elements.player_table.refresh_player(player)
local selected_player = Elements.container.get_selected_player(player)
local selected_action = Elements.container.get_selected_action(player)
for _, player_table in Elements.player_table:online_elements(player) do
local element_data = Elements.player_table.data[player_table]
Elements.action_bar.refresh(element_data.action_bar, player, selected_player)
Elements.reason_bar.refresh(element_data.reason_bar, selected_player, selected_action)
-- Highlight the open button of the selected player
for player_name, row in pairs(element_data.rows) do
Elements.open_action_bar.set_highlight(row.open_button, selected_player ~= nil and player_name == selected_player.name)
end
end
end
--- Action bar footer holding the close button and the per action button flows
--- @class ExpGui_PlayerList.elements.action_bar: ExpElement
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
Elements.action_bar = Gui.define("player_list/action_bar")
:draw(function(_, parent)
local action_bar = Gui.elements.subframe_base(parent, "subfooter_frame", "action_bar")
action_bar.visible = false
Elements.close_action_bar(action_bar)
for action_name, button_data in pairs(config.buttons) do
local permission_flow = action_bar.add{ type = "flow", name = action_name }
permission_flow.visible = false
for _, button in ipairs(button_data) do
button(permission_flow)
end
end
return action_bar
end)
:style{
height = 35,
padding = { 1, 3 },
} --[[ @as any ]]
--- Update the action bar buttons to match the selection for a player, hidden when nothing is selected
--- @param action_bar LuaGuiElement
--- @param player LuaPlayer
--- @param selected_player LuaPlayer?
function Elements.action_bar.refresh(action_bar, player, selected_player)
if not selected_player then
action_bar.visible = false
return
end
action_bar.visible = true
for action_name, buttons in pairs(config.buttons) do
local flow = action_bar[action_name]
if buttons.auth and not buttons.auth(player, selected_player) then
flow.visible = false
else
flow.visible = Roles.player_allowed(player, action_name)
end
end
end
--- Reason bar footer holding the reason entry and confirm button
--- @class ExpGui_PlayerList.elements.reason_bar: ExpElement
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
Elements.reason_bar = Gui.define("player_list/reason_bar")
:draw(function(_, parent)
local reason_bar = Gui.elements.subframe_base(parent, "subfooter_frame", "reason_bar")
reason_bar.visible = false
local reason_field = reason_bar.add{
name = "entry",
type = "textfield",
style = "stretchable_textfield",
tooltip = { "exp-gui_player-list.reason-entry" },
}
local entry_style = reason_field.style
entry_style.padding = 0
entry_style.height = 28
entry_style.minimal_width = 158
Elements.reason_confirm(reason_bar)
return reason_bar
end)
:style{
height = 35,
padding = { -1, 3 },
} --[[ @as any ]]
--- Update the reason bar visibility to match the selection, shown only while an action awaits a reason
--- @param reason_bar LuaGuiElement
--- @param selected_player LuaPlayer?
--- @param selected_action string?
function Elements.reason_bar.refresh(reason_bar, selected_player, selected_action)
reason_bar.visible = selected_player ~= nil and selected_action ~= nil
end
--- @class ExpGui_PlayerList.elements.container.selection
--- @field selected_player LuaPlayer?
--- @field selected_action string?
--- Container added to the left gui flow
--- @class ExpGui_PlayerList.elements.container: ExpElement
--- @field data table<LuaPlayer, ExpGui_PlayerList.elements.container.selection>
Elements.container = Gui.define("player_list/container")
:draw(function(_, parent)
local container = Gui.elements.container(parent)
local player_table = Elements.player_table(container)
local action_bar = Elements.action_bar(container)
local reason_bar = Elements.reason_bar(container)
Elements.player_table.set_bars(player_table, action_bar, reason_bar)
local row_data = Elements.player_table.calculate_row_data()
Elements.player_table.rebuild(player_table, row_data)
return Gui.elements.container.get_root_element(container)
end) --[[ @as any ]]
--- Get or create the selection state for a player
--- @param player LuaPlayer
--- @return ExpGui_PlayerList.elements.container.selection
function Elements.container._get_selection(player)
local selection = Elements.container.data[player]
if not selection then
selection = {}
Elements.container.data[player] = selection
end
return selection
end
--- Get the player that actions will be performed on
--- @param player LuaPlayer
--- @return LuaPlayer?
function Elements.container.get_selected_player(player)
return Elements.container._get_selection(player).selected_player
end
--- Get the action awaiting a reason
--- @param player LuaPlayer
--- @return string?
function Elements.container.get_selected_action(player)
return Elements.container._get_selection(player).selected_action
end
--- Set the player that actions will be performed on, nil to clear the selection
--- @param player LuaPlayer
--- @param selected_player LuaPlayer?
function Elements.container.set_selected_player(player, selected_player)
local selection = Elements.container._get_selection(player)
selection.selected_player = selected_player
if not selected_player then
selection.selected_action = nil
end
end
--- Toggle the player that actions will be performed on
--- @param player LuaPlayer
--- @param selected_player LuaPlayer
function Elements.container.toggle_selected_player(player, selected_player)
if Elements.container.get_selected_player(player) == selected_player then
Elements.container.set_selected_player(player, nil)
else
Elements.container.set_selected_player(player, selected_player)
end
end
--- Set the action awaiting a reason, nil to clear it
--- @param player LuaPlayer
--- @param selected_action string?
function Elements.container.set_selected_action(player, selected_action)
Elements.container._get_selection(player).selected_action = selected_action
end
-- Inject the accessors the actions config needs; setting an action also refreshes the gui to show the reason bar
local function select_action(player, selected_action)
Elements.container.set_selected_action(player, selected_action)
Elements.player_table.refresh_player(player)
end
config.set_accessors(Elements.container.get_selected_player, select_action)
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(Elements.container, true)
Gui.toolbar.create_button{
name = "toggle_player_list",
left_element = Elements.container,
sprite = "entity/character",
tooltip = { "exp-gui_player-list.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/player-list")
end
}
--- Rebuild the player list for all online players, used when the sort order changes
local function redraw_player_list()
local row_data = Elements.player_table.calculate_row_data()
for _, player_table in Elements.player_table:online_elements() do
Elements.player_table.rebuild(player_table, row_data)
Elements.player_table.refresh_player(Gui.get_player(player_table))
end
end
--- Refresh the play times for all online players
local function refresh_player_times()
local time_data = Elements.player_table.calculate_time_data()
for _, player_table in Elements.player_table:online_elements() do
Elements.player_table.refresh_times(player_table, time_data)
end
end
--- Remove a player from the list when they leave and clear any selection pointing at them
--- @param event EventData.on_player_left_game
local function on_player_left_game(event)
local left_player = game.players[event.player_index]
for _, player_table in Elements.player_table:online_elements() do
Elements.player_table.remove_row(player_table, left_player.name)
local viewing_player = Gui.get_player(player_table)
if Elements.container.get_selected_player(viewing_player) == left_player then
Elements.container.set_selected_player(viewing_player, nil)
Elements.player_table.refresh_player(viewing_player)
end
end
end
local e = defines.events
return {
elements = Elements,
events = {
[e.on_player_joined_game] = redraw_player_list,
[e.on_player_left_game] = on_player_left_game,
[Roles.events.on_role_assigned] = redraw_player_list,
[Roles.events.on_role_unassigned] = redraw_player_list,
},
on_nth_tick = {
[1800] = refresh_player_times,
}
}
+34 -13
View File
@@ -7,10 +7,14 @@ local Commands = require("modules/exp_commands")
local Roles = require("modules/exp_legacy/expcore/roles") local Roles = require("modules/exp_legacy/expcore/roles")
local addon_artillery = require("modules/exp_scenario/commands/artillery") local addon_artillery = require("modules/exp_scenario/commands/artillery")
local addon_research = require("modules/exp_scenario/commands/research")
local addon_trains = require("modules/exp_scenario/commands/trains") local addon_trains = require("modules/exp_scenario/commands/trains")
local addon_teleport = require("modules/exp_scenario/commands/teleport") local addon_teleport = require("modules/exp_scenario/commands/teleport")
local addon_waterfill = require("modules/exp_scenario/commands/waterfill") local addon_waterfill = require("modules/exp_scenario/commands/waterfill")
local addon_surface = require("modules/exp_scenario/commands/surface")
local addon_lawnmower = require("modules/exp_scenario/commands/lawnmower")
local addon_home = require("modules/exp_scenario/commands/home")
local addon_vlayer = require("modules/exp_scenario/commands/vlayer")
local addon_repair = require("modules/exp_scenario/commands/repair")
--- @class ExpGui_QuickActions.elements --- @class ExpGui_QuickActions.elements
local Elements = {} local Elements = {}
@@ -18,15 +22,23 @@ local Elements = {}
--- @type table<string, { command: ExpCommand, element: ExpElement }> --- @type table<string, { command: ExpCommand, element: ExpElement }>
local Actions = {} local Actions = {}
--- @param name string
--- @param command ExpCommand | function (this is needed because of the overload on commands) --- @param command ExpCommand | function (this is needed because of the overload on commands)
--- @param on_click? ExpElement.EventHandler<EventData.on_gui_click> --- @param on_click? ExpElement.EventHandler<EventData.on_gui_click>
local function new_quick_action(name, command, on_click) local function new_quick_action(command, on_click)
local element = Gui.define("quick_actions/" .. name) local command_name = command.name
local element = Gui.define("quick_actions/" .. command_name)
:draw{ :draw{
type = "button", type = "button",
caption = { "exp-gui_quick-actions.caption-" .. name }, caption = { "?",
tooltip = { "exp-gui_quick-actions.tooltip-" .. name }, { "exp-gui_quick-actions.caption-" .. command_name },
command_name,
},
tooltip = { "?",
{ "exp-gui_quick-actions.tooltip-" .. command_name },
command.description,
""
},
} }
:style{ :style{
width = 160, width = 160,
@@ -35,22 +47,31 @@ local function new_quick_action(name, command, on_click)
command(player) command(player)
end) end)
Elements[name] = element Elements[command_name] = element
Actions[name] = { Actions[command_name] = {
command = command --[[ @as ExpCommand ]], command = command --[[ @as ExpCommand ]],
element = element, element = element,
} }
end end
new_quick_action("artillery", addon_artillery.commands.artillery) new_quick_action(addon_artillery.commands.artillery)
new_quick_action("trains", addon_trains.commands.set_trains_to_automatic) new_quick_action(addon_trains.commands.set_trains_to_automatic)
new_quick_action("research", addon_research.commands.set_auto_research)
new_quick_action("spawn", addon_teleport.commands.spawn, function(def, player, element, event) new_quick_action(addon_teleport.commands.spawn, function(def, player, element, event)
addon_teleport.commands.spawn(player, player) addon_teleport.commands.spawn(player, player)
end) end)
new_quick_action("waterfill", addon_waterfill.commands.waterfill) new_quick_action(addon_waterfill.commands.waterfill)
new_quick_action(addon_lawnmower.commands.lawnmower)
new_quick_action(addon_surface.commands.clear_ground_items)
new_quick_action(addon_surface.commands.clear_blueprints_surface)
new_quick_action(addon_surface.commands.clear_blueprints)
new_quick_action(addon_home.commands.home)
new_quick_action(addon_home.commands.home_return)
new_quick_action(addon_home.commands.set_home)
new_quick_action(addon_home.commands.get_home)
new_quick_action(addon_vlayer.commands.vlayer)
new_quick_action(addon_repair.commands.repair)
--- Container added to the left gui flow --- Container added to the left gui flow
--- @class ExpGui_QuickActions.elements.container: ExpElement --- @class ExpGui_QuickActions.elements.container: ExpElement
+2 -2
View File
@@ -200,7 +200,7 @@ define_tab(
local container = parent.add{ type = "flow", direction = "vertical" } local container = parent.add{ type = "flow", direction = "vertical" }
local top_flow = container.add{ type = "flow" } local top_flow = container.add{ type = "flow" }
top_flow.add{ type = "sprite", sprite = "file/modules/exp_legacy/modules/gui/logo.png" } top_flow.add{ type = "sprite", sprite = "file/modules/exp_scenario/gui/logo.png" }
local center_flow = top_flow.add{ type = "flow", direction = "vertical" } local center_flow = top_flow.add{ type = "flow", direction = "vertical" }
center_flow.style.horizontal_align = "center" center_flow.style.horizontal_align = "center"
@@ -208,7 +208,7 @@ define_tab(
Gui.elements.title_label(center_flow, 62, { "exp-gui_readme.welcome-title", server_details.name }) 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) Gui.elements.centered_label(center_flow, 380, server_details.welcome)
top_flow.add{ type = "sprite", sprite = "file/modules/exp_legacy/modules/gui/logo.png" } top_flow.add{ type = "sprite", sprite = "file/modules/exp_scenario/gui/logo.png" }
Gui.elements.bar(container) Gui.elements.bar(container)
container.add{ type = "flow" }.style.height = 4 container.add{ type = "flow" }.style.height = 4
+27 -23
View File
@@ -103,6 +103,9 @@ lower-role=You must have a higher role than the target.
[exp-commands_lawnmower] [exp-commands_lawnmower]
description=Clean up biter corpse, decoratives and nuclear hole. description=Clean up biter corpse, decoratives and nuclear hole.
arg-range=Range to clean up. arg-range=Range to clean up.
enter=Entered selection mode, select the area to fill with water.
exit=Exited selection mode.
complete=__1__ tiles were handled.
[exp-commands_locate] [exp-commands_locate]
description=Opens remote view at the location of the player's last location. description=Opens remote view at the location of the player's last location.
@@ -155,7 +158,8 @@ machine-count=And you will need __1__ machines (with the same speed as this one)
[exp-commands_repair] [exp-commands_repair]
description=Repairs entities on your force around you. description=Repairs entities on your force around you.
arg-range=Range to repair entities within. enter=Entered selection mode, select the area.
exit=Exited selection mode.
# Intentional space left on the two lines below so they can be joined together. # Intentional space left on the two lines below so they can be joined together.
response-revive=__1__ entities were revived. response-revive=__1__ entities were revived.
response-heal=__1__ entities were healed. response-heal=__1__ entities were healed.
@@ -182,12 +186,6 @@ list-element=__1__: __2__
removed-all=__1__ has has all of their reports removed by __2__. removed-all=__1__ has has all of their reports removed by __2__.
removed=__1__ has a report removed by __2__. removed=__1__ has a report removed by __2__.
[exp-commands_research]
description=Sets research to be automatically queued.
arg-state=State to set, default is to toggle.
auto-research=__1__ set auto research to __2__
queue=[color=255, 255, 255] Research added to queue - [technology=__1__] - __2__[/color]
[exp-commands_roles] [exp-commands_roles]
description-assign=Assigns a role to a player. description-assign=Assigns a role to a player.
description-unassign=Unassigns a role from a player. description-unassign=Unassigns a role from a player.
@@ -221,14 +219,13 @@ follow-self=You can not follow yourself.
[exp-commands_surface] [exp-commands_surface]
description-items=Clear all items on the ground. description-items=Clear all items on the ground.
description-blueprints=Clear all blueprints. description-blueprints=Clear all blueprints.
description-radius=Clear all blueprints in an radius around you. description-blueprints-surface=Clear all blueprints on the current surface.
arg-surface=Surface to clear on, default all. arg-surface=Surface to clear on, default all.
arg-radius=Radius to clear. items=__1__ cleared all items on the ground of __2__.
items-surface=__1__ cleared all items on the ground of __2__. blueprints=__1__ cleared all blueprints on __2__.
items-all=__1__ cleared all items on the ground for all surfaces. enter=Entered selection mode, select the area.
blueprints-surface=__1__ cleared all blueprints on __2__. exit=Exited selection mode.
blueprints-all=__1__ cleared all blueprints for all surfaces. complete=__1__ blueprint ghost were removed.
blueprints-radius=__1__ cleared all blueprints in a __2__ radius around them on __3__.
[exp-commands_trains] [exp-commands_trains]
description=Set All Trains to Automatic, does not effect trains with passengers. description=Set All Trains to Automatic, does not effect trains with passengers.
@@ -295,10 +292,6 @@ tooltip-amount=Amount of items to insert into the __1__ slots
invalid=Autofill set to maximum amount: __1__ __2__ for __3__ invalid=Autofill set to maximum amount: __1__ __2__ for __3__
inserted=Inserted __1__ __2__ into __3__ inserted=Inserted __1__ __2__ into __3__
[exp-gui_landfill-blueprint]
tooltip-main=Landfill Blueprint
error-no-blueprint=You need to hold the blueprint in cursor
[exp-gui_module-inserter] [exp-gui_module-inserter]
tooltip-main=Module Inserter tooltip-main=Module Inserter
caption-main=Modules caption-main=Modules
@@ -324,6 +317,22 @@ tooltip-character_reach_distance_bonus=Character reach distance bonus
caption-personal_battery_recharge=Battery caption-personal_battery_recharge=Battery
tooltip-personal_battery_recharge=Armor battery recharge tooltip-personal_battery_recharge=Armor battery recharge
[exp-gui_player-list]
main-tooltip=Player List
open-action-bar=Options
close-action-bar=Close Options
reason-confirm=Confirm Reason
reason-entry=Enter Reason
goto-player=Goto player
bring-player=Bring player
report-player=Report player
warn-player=Warn player
jail-player=Jail player
kick-player=Kick player
ban-player=Ban player
afk-time=__1__% of total map time\nLast moved __2__ ago
open-map=__1__ __2__\n__3__\nClick to open map
[exp-gui_player-stats] [exp-gui_player-stats]
tooltip-main=Player Stats tooltip-main=Player Stats
caption-main=Player Stats caption-main=Player Stats
@@ -336,15 +345,10 @@ caption-net=Net
[exp-gui_quick-actions] [exp-gui_quick-actions]
tooltip-main=Quick Actions tooltip-main=Quick Actions
caption-artillery=Artillery caption-artillery=Artillery
tooltip-artillery=Select artillery targets
caption-research=Auto Research caption-research=Auto Research
tooltip-research=Toggle auto research queue
caption-spawn=Teleport Spawn caption-spawn=Teleport Spawn
tooltip-spawn-tooltip=Teleport to spawn
caption-trains=Set Auto Train caption-trains=Set Auto Train
tooltip-trains=Set all trains to automatic
caption-waterfill=Waterfill caption-waterfill=Waterfill
tooltip-waterfill=Change tiles to water
[exp-gui_readme] [exp-gui_readme]
main-tooltip=Information main-tooltip=Information
+7 -17
View File
@@ -182,12 +182,6 @@ list-element=__1__: __2__
removed-all=__1__ 被舉報的所有紀錄已被 __2__ 清除。 removed-all=__1__ 被舉報的所有紀錄已被 __2__ 清除。
removed=__1__ 被舉報的一個紀錄已被 __2__ 清除。 removed=__1__ 被舉報的一個紀錄已被 __2__ 清除。
[exp-commands_research]
description=啟用自動研究
arg-state=狀態
auto-research=__1__ 把自動研究設置為 __2__ 。
queue=[color=255, 255, 255] 研究已加入隊列 - [technology=__1__] - __2__[/color]
[exp-commands_roles] [exp-commands_roles]
description-assign=為用戶指配用戶組 description-assign=為用戶指配用戶組
description-unassign=為用戶取消指配用戶組 description-unassign=為用戶取消指配用戶組
@@ -221,14 +215,14 @@ follow-self=你不能追蹤自己
[exp-commands_surface] [exp-commands_surface]
description-items=清除地面上的物品 description-items=清除地面上的物品
description-blueprints=清除藍圖 description-blueprints=清除藍圖
description-radius=清除周邊的藍圖 description-blueprints-surface=清除藍圖
arg-surface=位面 arg-surface=位面
arg-radius=周邊半徑 items=__1__ 清除了所有在 __2__ 中掉地上的東西。
items-surface=__1__ 清除了所有在 __2__ 中地上的東西 blueprints=__1__ 清除了所有在 __2__ 中地上的藍圖
items-all=__1__ 清除了所有掉地上的東西。 enter=現在進入區域選擇
blueprints-surface=__1__ 清除了所有在 __2__ 中在地上的藍圖。 exit=已進入區域選擇
blueprints-all=__1__ 清除了所有在地上的藍圖 area-too-large=區域太大了,需少過 __1__ 格,你選了 __2__ 格
blueprints-radius=__1__ 清除了在 __3__ 上,周邊 __2__ 格的地上的藍圖。 complete=__1__ 地上的藍圖已被清除
[exp-commands_trains] [exp-commands_trains]
description=把火車設置為自動模式 description=把火車設置為自動模式
@@ -295,10 +289,6 @@ tooltip-amount=自動填入 __1__ 的數量
invalid=自動填入最大值 __1__ __2__ 給 __3__ invalid=自動填入最大值 __1__ __2__ 給 __3__
inserted=自動填入 __1__ __2__ 到 __3__ inserted=自動填入 __1__ __2__ 到 __3__
[exp-gui_landfill-blueprint]
tooltip-main=藍圖填海
error-no-blueprint=您需要將藍圖保持在遊標處
[exp-gui_module-inserter] [exp-gui_module-inserter]
tooltip-main=模組 tooltip-main=模組
caption-main=Modules caption-main=Modules
+7 -17
View File
@@ -182,12 +182,6 @@ list-element=__1__: __2__
removed-all=__1__ 被舉報的所有紀錄已被 __2__ 清除。 removed-all=__1__ 被舉報的所有紀錄已被 __2__ 清除。
removed=__1__ 被舉報的一個紀錄已被 __2__ 清除。 removed=__1__ 被舉報的一個紀錄已被 __2__ 清除。
[exp-commands_research]
description=啟用自動研究
arg-state=狀態
auto-research=__1__ 把自動研究設置為 __2__ 。
queue=[color=255, 255, 255] 研究已加入隊列 - [technology=__1__] - __2__[/color]
[exp-commands_roles] [exp-commands_roles]
description-assign=為用戶指配用戶組 description-assign=為用戶指配用戶組
description-unassign=為用戶取消指配用戶組 description-unassign=為用戶取消指配用戶組
@@ -221,14 +215,14 @@ follow-self=你不能追蹤自己
[exp-commands_surface] [exp-commands_surface]
description-items=清除地面上的物品 description-items=清除地面上的物品
description-blueprints=清除藍圖 description-blueprints=清除藍圖
description-radius=清除周邊的藍圖 description-blueprints-surface=清除藍圖
arg-surface=位面 arg-surface=位面
arg-radius=周邊半徑 items=__1__ 清除了所有在 __2__ 中掉地上的東西。
items-surface=__1__ 清除了所有在 __2__ 中地上的東西 blueprints=__1__ 清除了所有在 __2__ 中地上的藍圖
items-all=__1__ 清除了所有掉地上的東西。 enter=現在進入區域選擇
blueprints-surface=__1__ 清除了所有在 __2__ 中在地上的藍圖。 exit=已進入區域選擇
blueprints-all=__1__ 清除了所有在地上的藍圖 area-too-large=區域太大了,需少過 __1__ 格,你選了 __2__ 格
blueprints-radius=__1__ 清除了在 __3__ 上,周邊 __2__ 格的地上的藍圖。 complete=__1__ 地上的藍圖已被清除
[exp-commands_trains] [exp-commands_trains]
description=把火車設置為自動模式 description=把火車設置為自動模式
@@ -295,10 +289,6 @@ tooltip-amount=自動填入 __1__ 的數量
invalid=自動填入最大值 __1__ __2__ 給 __3__ invalid=自動填入最大值 __1__ __2__ 給 __3__
inserted=自動填入 __1__ __2__ 到 __3__ inserted=自動填入 __1__ __2__ 到 __3__
[exp-gui_landfill-blueprint]
tooltip-main=藍圖填海
error-no-blueprint=您需要將藍圖保持在遊標處
[exp-gui_module-inserter] [exp-gui_module-inserter]
tooltip-main=模組 tooltip-main=模組
caption-main=Modules caption-main=Modules
+2 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@expcluster/scenario", "name": "@expcluster/scenario",
"version": "0.1.0", "version": "6.5.0",
"description": "Clusterio plugin implementing the Explosive Gaming scenario.", "description": "Clusterio plugin implementing the Explosive Gaming scenario.",
"author": "Cooldude2606 <https://github.com/Cooldude2606>", "author": "Cooldude2606 <https://github.com/Cooldude2606>",
"license": "MIT", "license": "MIT",
@@ -31,6 +31,7 @@
"dependencies": { "dependencies": {
"@expcluster/lib_commands": "workspace:^", "@expcluster/lib_commands": "workspace:^",
"@expcluster/lib_util": "workspace:^", "@expcluster/lib_util": "workspace:^",
"@expcluster/lib_gui": "workspace:^",
"@sinclair/typebox": "catalog:" "@sinclair/typebox": "catalog:"
}, },
"publishConfig": { "publishConfig": {
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@expcluster/server-ups", "name": "@expcluster/server-ups",
"version": "0.1.0", "version": "6.5.0",
"description": "Clusterio plugin providing in game server ups counter", "description": "Clusterio plugin providing in game server ups counter",
"author": "Cooldude2606 <https://github.com/Cooldude2606>", "author": "Cooldude2606 <https://github.com/Cooldude2606>",
"license": "MIT", "license": "MIT",
@@ -25,9 +25,9 @@
}, },
"dependencies": { "dependencies": {
"@sinclair/typebox": "catalog:", "@sinclair/typebox": "catalog:",
"@expcluster/lib_commands": "workspace:^",
"@expcluster/lib_util": "workspace:^", "@expcluster/lib_util": "workspace:^",
"@expcluster/lib_gui": "workspace:^", "@expcluster/lib_gui": "workspace:^"
"@expcluster/lib_commands": "workspace:^"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
+7
View File
@@ -17,6 +17,13 @@ function AABB.valid(aabb)
return aabb.left_top.x < aabb.right_bottom.x and aabb.left_top.y < aabb.right_bottom.y return aabb.left_top.x < aabb.right_bottom.x and aabb.left_top.y < aabb.right_bottom.y
end end
--- Returns the size of the area contained within an AABB
--- @param aabb BoundingBox
--- @return number
function AABB.size(aabb)
return (aabb.right_bottom.x - aabb.left_top.x) * (aabb.right_bottom.y - aabb.left_top.y)
end
--- Clone an area, allows for safe mutation of an input value --- Clone an area, allows for safe mutation of an input value
--- @param aabb BoundingBox --- @param aabb BoundingBox
--- @return BoundingBox --- @return BoundingBox
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@expcluster/lib_util", "name": "@expcluster/lib_util",
"version": "0.1.0", "version": "6.5.0",
"description": "Clusterio plugin providing Lua libraries and other utilities.", "description": "Clusterio plugin providing Lua libraries and other utilities.",
"author": "Cooldude2606 <https://github.com/Cooldude2606>", "author": "Cooldude2606 <https://github.com/Cooldude2606>",
"license": "MIT", "license": "MIT",
+141
View File
@@ -0,0 +1,141 @@
export async function githubFetch(
path: string,
query: Record<string, string>= {},
init: RequestInit & { headers?: Record<string, string> } = {},
) {
const url = new URL("https://api.github.com/");
url.pathname = path;
for (const [name, value] of Object.entries(query)) {
url.searchParams.set(name, value);
}
init = {
...init,
headers: {
...init.headers as Record<string, string> ?? {},
"Accept": "application/vnd.github+json",
}
};
if (process.env.GH_TOKEN) {
init.headers!["Authorization"] = `Bearer ${process.env.GH_TOKEN}`;
}
console.log("Fetching", url.href);
const response = await fetch(url, init);
if (!response.ok) {
console.log(response.headers);
throw new Error(`GitHub replied with ${response.status} ${response.statusText}: ${await response.text()}`);
}
if (!response.headers.get("Content-Type")?.startsWith("application/json")) {
throw new Error(
`GitHub replied with ${response.status} ${response.statusText}: ${response.headers.get("Content-Type")}`
);
}
return response;
}
export async function githubFetchJson<T>(
path: string,
query: Record<string, string> = {},
init: RequestInit & { headers?: Record<string, string> } = {},
): Promise<T> {
const response = await githubFetch(path, query, init);
return await response.json();
}
export async function githubFetchJsonPaginated<T>(
path: string,
query: Record<string, string> = {},
init: RequestInit & { headers?: Record<string, string> } = {},
shouldContinue: (result: T[]) => boolean = () => true,
): Promise<T[]> {
const data: T[] = [];
let pagesRemaining = true;
query = {
...query,
per_page: "100",
};
while (pagesRemaining) {
const response = await githubFetch(path, query, init);
const responseItems = await response.json();
data.push(...responseItems);
const linkHeader = response.headers.get("Link");
pagesRemaining = linkHeader?.includes(`rel="next"`) ?? false;
if (pagesRemaining) {
pagesRemaining = shouldContinue(responseItems);
}
if (pagesRemaining) {
query = {
...query,
page: String((query.page ? (Number(query.page)) : 1) + 1),
}
}
}
return data;
}
// Minimal types that include only what is used and useful for our scripts. See
// https://docs.github.com/en/rest for complete reference.
export interface PullRequest {
number: number,
url: string,
html_url: string,
state: string,
title: string,
body: string | null,
created_at: string,
updated_at: string,
closed_at: string | null,
merged_at: string | null,
merge_commit_sha: string | null,
}
export interface Issue {
number: number,
title: string,
url: string,
html_url: string,
state: string,
body?: string | null,
user: {
login: string,
url: string,
html_url: string,
}
pull_request?: {
url: string,
html_url: string,
merged_at?: string | null,
},
}
export interface Release {
tag_name: string,
target_commitish: string,
name: string | null,
body: string | null,
created_at: string,
published_at: string | null,
updated_at?: string | null,
}
export interface Commit {
sha: string,
commit: {
message: string,
},
author: null | {} | {
name?: null | string,
login: string,
type: string,
},
parents: {
sha: string,
}[],
}
export interface Reference {
ref: string,
object: {
type: string,
sha: string,
},
}
+260
View File
@@ -0,0 +1,260 @@
import {
type Release,
type Issue,
githubFetchJson,
githubFetchJsonPaginated,
} from "./github_api.mts";
type Changelog = Record<string, string[]>;
const repository = "explosivegaming/ExpCluster";
const branch = "main";
const changelogSections = [
"Major Features",
"Features",
"Fixes",
"Changes",
"Breaking Changes",
"Meta",
];
// Generic result codes
const success = 0;
const failure = 1;
type ParserState = {
pos: number,
lines: string[],
};
function createParser(lines: string[]): ParserState {
return { pos: 0, lines };
}
function atEnd(parser: ParserState) {
return parser.pos >= parser.lines.length;
}
function currentLine(parser: ParserState) {
if (atEnd(parser)) {
throw new Error("Attempt to access line past end");
}
return parser.lines[parser.pos];
}
function skipToLine(
parser: ParserState,
pattern: RegExp
) {
for (; parser.pos < parser.lines.length; parser.pos++) {
if (pattern.test(currentLine(parser))) {
return success;
}
}
return failure;
}
function skipEmptyLines(parser: ParserState) {
while (parser.pos < parser.lines.length && /^ *$/.test(currentLine(parser))) {
parser.pos += 1;
}
}
function extractPullRequestChangelog(
parser: ParserState,
):
| [typeof success, string[]]
| [typeof failure, undefined, string]
{
const headerFail = skipToLine(parser, /^###? Changelog *$/);
if (headerFail) {
return [failure, , "Header not found"];
}
parser.pos += 1;
skipEmptyLines(parser);
if (!atEnd(parser) && /^None/i.test(currentLine(parser))) {
parser.pos += 1;
return [success, []];
}
const blockStartFail = skipToLine(parser, /```/);
if (blockStartFail) {
return [failure, , "Code block not found after header"];
}
parser.pos += 1
const blockStartPos = parser.pos;
const blockEndFail = skipToLine(parser, /```/);
if (blockEndFail) {
return [failure, , "End of code block not found after header"];
}
return [success, parser.lines.slice(blockStartPos, parser.pos)];
}
function parseChangelog(parser: ParserState, pr: Issue, issues: Issue[]): [Changelog, string[]] {
let section: string | undefined;
let changelog: Changelog = Object.create(null);
let warnings: string[] = [];
while (!atEnd(parser)) {
const line = currentLine(parser);
parser.pos += 1;
if (line.trim() === "") {
continue;
}
if (line.startsWith("###")) {
section = line.slice(4).trim();
if (!changelogSections.includes(section)) {
warnings.push(
`Changelog section ${section} on line ${parser.pos} is not one of ${changelogSections.join(", ")}`
);
}
changelog[section] = changelog[section] ?? [];
continue;
}
if (!section) {
warnings.push(`Unexpected content "${line}" outside of a changelog section on line ${parser.pos}`);
continue;
}
if (line.startsWith("- ")) {
const entry = line
.slice(2)
.trim()
.replace(new RegExp(`\\[#(\\d+)\\]\\(https://github.com/${repository}/(issues|pull)/\\d+\\)`, "g"), "#$1");
let hasRef = false
for (const ref of entry.matchAll(/#(\d+)/g)) {
hasRef = true;
const id = Number.parseInt(ref[1], 10);
const refIssue = issues.find(issue => issue.number === id);
if (!refIssue) {
warnings.push(
`Issue reference ${ref[0]} on line ${parser.pos} does not exist ` +
"or has not been updated since the last release"
);
} else if (refIssue.pull_request && refIssue.number !== pr.number) {
warnings.push(
`Pull request reference ${ref[0]} on line ${parser.pos} is to a ` +
"different Pull request than this one."
);
}
}
if (!hasRef) {
warnings.push(`Changelog entry on line ${parser.pos} does not reference an issue`);
}
changelog[section].push(entry);
continue;
}
warnings.push(`Unexpected content "${line}" on line ${parser.pos}`);
}
return [changelog, warnings];
}
function changelogFromPullRequests(pullRequests: Issue[], issues: Issue[]) {
const mergedChangelog: Changelog = Object.create(null);
for (const pr of pullRequests) {
if (!pr.body) {
console.error(`Failed to extract changelog in ${pr.html_url}: No body`)
console.log(pr.title)
continue;
}
const [
changelogFail,
changelogLines,
changelogFailReason
] = extractPullRequestChangelog(createParser(pr.body!.split(/\r?\n/)));
if (changelogFail) {
console.error(`Failed to extract changelog in ${pr.html_url}: ${changelogFailReason}`)
console.log(pr.title)
console.log(pr.body)
continue;
}
const [changelog, warnings] = parseChangelog(createParser(changelogLines), pr, issues);
for (const warning of warnings) {
console.warn(`Warning in changelog for ${pr.html_url}: ${warning}`)
}
for (const [section, items] of Object.entries(changelog)) {
mergedChangelog[section] = [...mergedChangelog[section] ?? [], ...items];
}
}
return mergedChangelog;
}
async function fetchLastRelease() {
// Assumes releases are ordered by their creation or tag time in descending order.
const releases = await githubFetchJson<Release[]>(`/repos/${repository}/releases`);
const latest = releases[0]
if (!latest) {
throw new Error(`Unable to find latest release on ${branch}`);
}
return latest;
}
async function fetchIssuesUpdatedSince(since: string) {
return await githubFetchJsonPaginated<Issue>(
`/repos/${repository}/issues`,
{ state: "all", since, sort: "updated", direction: "asc" },
);
}
function printMarkdown(
changelog: Changelog,
issues: Issue[],
users: Issue["user"][],
refText: (issue: Issue) => string,
userText: (user: Issue["user"]) => string,
) {
const sections = new Set([...changelogSections, ...Object.keys(changelog)]);
for (const section of sections) {
if (section in changelog) {
console.log(`### ${section}\n`);
const entries = changelog[section];
console.log(entries.map(text => (
`- ${text.replaceAll(/#(\d+)/g, (ref, idAsText) => {
const id = Number.parseInt(idAsText, 10);
if (!Number.isFinite(id)) {
return ref;
}
const issue = issues.find(issue => issue.number === id);
if (!issue) {
return ref;
}
return refText(issue);
})}`
)).join("\n"));
console.log();
}
}
console.log(`Many thanks to the following for contributing to this release:`);
console.log(users.map(userText).join("\n"))
}
async function main() {
const lastRelease = await fetchLastRelease();
const issues = await fetchIssuesUpdatedSince(lastRelease.created_at);
const pullRequests = issues.filter(issue => (
issue.pull_request
&& issue.pull_request.merged_at
&& issue.pull_request.merged_at > lastRelease.created_at
));
const changelog = changelogFromPullRequests(pullRequests, issues);
const users = [...new Map(pullRequests.map(issue => [issue.user.login, issue.user])).values()]
console.log();
console.log("=== Github Release Markdown ===");
printMarkdown(changelog, issues, users,
issue => `${repository}#${issue.number}`,
user => `@${user.login}`,
);
console.log();
console.log("=== Discord / Changelog Markdown ===");
printMarkdown(changelog, issues, users,
issue => `[#${issue.number}](<${issue.html_url}>)`,
user => `[@${user.login}](<${user.html_url}>)`
);
}
if (import.meta.main) {
main().catch(console.error);
}