From a45f53bc4889fe1de7bedd17566f81b1bba33c66 Mon Sep 17 00:00:00 2001 From: PHIDIAS Date: Wed, 3 Dec 2025 03:33:38 +0900 Subject: [PATCH 1/2] GUI Research Milestone Hide Hidden (#412) --- .../module/gui/research_milestones.lua | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/exp_scenario/module/gui/research_milestones.lua b/exp_scenario/module/gui/research_milestones.lua index 42ca5f7e..d7cb2716 100644 --- a/exp_scenario/module/gui/research_milestones.lua +++ b/exp_scenario/module/gui/research_milestones.lua @@ -46,16 +46,18 @@ do --- Calculate the research targets local research_index = 1 local total_time = 0 for name, time in pairs(config.milestone[config.mod_set]) do - research_targets.index_lookup[name] = research_index - total_time = total_time + time * 60 + if prototypes.technology[name] and (not prototypes.technology[name].hidden) then + research_targets.index_lookup[name] = research_index + total_time = total_time + time * 60 - research_targets.target_times[research_index] = { - name = name, - target = total_time, - label = research_time_format(total_time), - } + research_targets.target_times[research_index] = { + name = name, + target = total_time, + label = research_time_format(total_time), + } - research_index = research_index + 1 + research_index = research_index + 1 + end end research_targets.length = research_index - 1 research_targets.max_start_index = math.max(1, research_index - display_size) From 9bd699ebf1f3354739fdea6fa212bedd9ad7c68c Mon Sep 17 00:00:00 2001 From: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com> Date: Tue, 2 Dec 2025 18:34:24 +0000 Subject: [PATCH 2/2] Refactor legacy addons into Clusterio format (#413) * Refactor custom start * Refactor afk kick * Fix use of assert get player * Refactor chat popup * Refactor chat auto reply * Refactor help bubbles * Refactor damage popups * Refactor death markers * Refactor deconstruction log * Remove FAGC logging * Refactor discord alerts * Refactor insert pickup * Refactor inventory clear * Refactor extra logging * Refactor nuke protection * Refactor pollution grading * Refactor protection jail * Refactor report jail * Refactor mine depletion * Refactor degrading tiles * Refactor station auto name * Refactor spawn area * Refactor fast deconstruction * Bug Fixes --- exp_legacy/module/config/_file_loader.lua | 22 -- exp_legacy/module/config/advanced_start.lua | 1 + exp_legacy/module/config/afk_kick.lua | 6 +- exp_legacy/module/config/chat_reply.lua | 134 +++---- exp_legacy/module/config/compilatron.lua | 32 +- exp_legacy/module/config/death_logger.lua | 7 +- exp_legacy/module/config/expcore/roles.lua | 2 +- exp_legacy/module/config/logging.lua | 22 +- exp_legacy/module/config/nukeprotect.lua | 2 +- exp_legacy/module/config/spawn_area.lua | 9 +- exp_legacy/module/expcore/roles.lua | 2 +- exp_legacy/module/locale/en/addons.cfg | 64 ---- exp_legacy/module/locale/en/gui.cfg | 6 - exp_legacy/module/locale/zh-CN/addons.cfg | 64 ---- exp_legacy/module/locale/zh-CN/gui.cfg | 6 - exp_legacy/module/locale/zh-TW/addons.cfg | 64 ---- exp_legacy/module/locale/zh-TW/gui.cfg | 6 - .../module/modules/addons/advanced-start.lua | 48 --- exp_legacy/module/modules/addons/afk-kick.lua | 66 ---- .../module/modules/addons/chat-popups.lua | 41 --- .../module/modules/addons/chat-reply.lua | 50 --- .../module/modules/addons/compilatron.lua | 114 ------ .../module/modules/addons/damage-popups.lua | 40 --- .../module/modules/addons/death-logger.lua | 165 --------- exp_legacy/module/modules/addons/deconlog.lua | 120 ------- .../module/modules/addons/discord-alerts.lua | 305 ---------------- exp_legacy/module/modules/addons/fagc.lua | 21 -- exp_legacy/module/modules/addons/inserter.lua | 23 -- .../module/modules/addons/inventory-clear.lua | 19 - exp_legacy/module/modules/addons/logging.lua | 69 ---- exp_legacy/module/modules/addons/miner.lua | 179 ---------- .../module/modules/addons/nukeprotect.lua | 45 --- .../modules/addons/pollution-grading.lua | 16 - .../module/modules/addons/protection-jail.lua | 41 --- .../module/modules/addons/report-jail.lua | 29 -- .../module/modules/addons/spawn-area.lua | 247 ------------- .../modules/addons/station-auto-name.lua | 93 ----- .../module/modules/addons/tree-decon.lua | 140 -------- exp_scenario/module/commands/_types.lua | 12 +- exp_scenario/module/control.lua | 21 ++ exp_scenario/module/control/afk_kick.lua | 84 +++++ .../module/control/chat_auto_reply.lua | 63 ++++ exp_scenario/module/control/chat_popup.lua | 48 +++ exp_scenario/module/control/custom_start.lua | 64 ++++ exp_scenario/module/control/damage_popups.lua | 50 +++ exp_scenario/module/control/death_markers.lua | 157 +++++++++ .../module/control/deconstruction_log.lua | 181 ++++++++++ .../module/control/degrading_tiles.lua | 99 +++--- .../module/control/discord_alerts.lua | 332 ++++++++++++++++++ exp_scenario/module/control/extra_logging.lua | 83 +++++ .../module/control/fast_deconstruction.lua | 181 ++++++++++ exp_scenario/module/control/help_bubbles.lua | 100 ++++++ .../module/control/inserter_pickup.lua | 34 ++ .../module/control/inventory_clear.lua | 27 ++ .../module/control/mine_depletion.lua | 231 ++++++++++++ .../module/control/nuke_protection.lua | 56 +++ .../module/control/pollution_grading.lua | 27 ++ .../module/control/protection_jail.lua | 47 +++ exp_scenario/module/control/report_jail.lua | 38 ++ exp_scenario/module/control/spawn_area.lua | 292 +++++++++++++++ .../module/control/station_auto_name.lua | 104 ++++++ exp_scenario/module/gui/autofill.lua | 2 +- exp_scenario/module/gui/module_inserter.lua | 2 +- exp_scenario/module/gui/player_bonus.lua | 2 +- exp_scenario/module/locale/en.cfg | 66 ++++ exp_scenario/module/locale/zh-CN.cfg | 66 ++++ exp_scenario/module/locale/zh-TW.cfg | 66 ++++ exp_server_ups/module/control.lua | 6 +- exp_util/module/async.lua | 13 +- 69 files changed, 2614 insertions(+), 2260 deletions(-) delete mode 100644 exp_legacy/module/modules/addons/advanced-start.lua delete mode 100644 exp_legacy/module/modules/addons/afk-kick.lua delete mode 100644 exp_legacy/module/modules/addons/chat-popups.lua delete mode 100644 exp_legacy/module/modules/addons/chat-reply.lua delete mode 100644 exp_legacy/module/modules/addons/compilatron.lua delete mode 100644 exp_legacy/module/modules/addons/damage-popups.lua delete mode 100644 exp_legacy/module/modules/addons/death-logger.lua delete mode 100644 exp_legacy/module/modules/addons/deconlog.lua delete mode 100644 exp_legacy/module/modules/addons/discord-alerts.lua delete mode 100644 exp_legacy/module/modules/addons/fagc.lua delete mode 100644 exp_legacy/module/modules/addons/inserter.lua delete mode 100644 exp_legacy/module/modules/addons/inventory-clear.lua delete mode 100644 exp_legacy/module/modules/addons/logging.lua delete mode 100644 exp_legacy/module/modules/addons/miner.lua delete mode 100644 exp_legacy/module/modules/addons/nukeprotect.lua delete mode 100644 exp_legacy/module/modules/addons/pollution-grading.lua delete mode 100644 exp_legacy/module/modules/addons/protection-jail.lua delete mode 100644 exp_legacy/module/modules/addons/report-jail.lua delete mode 100644 exp_legacy/module/modules/addons/spawn-area.lua delete mode 100644 exp_legacy/module/modules/addons/station-auto-name.lua delete mode 100644 exp_legacy/module/modules/addons/tree-decon.lua create mode 100644 exp_scenario/module/control/afk_kick.lua create mode 100644 exp_scenario/module/control/chat_auto_reply.lua create mode 100644 exp_scenario/module/control/chat_popup.lua create mode 100644 exp_scenario/module/control/custom_start.lua create mode 100644 exp_scenario/module/control/damage_popups.lua create mode 100644 exp_scenario/module/control/death_markers.lua create mode 100644 exp_scenario/module/control/deconstruction_log.lua rename exp_legacy/module/modules/addons/scorched-earth.lua => exp_scenario/module/control/degrading_tiles.lua (50%) create mode 100644 exp_scenario/module/control/discord_alerts.lua create mode 100644 exp_scenario/module/control/extra_logging.lua create mode 100644 exp_scenario/module/control/fast_deconstruction.lua create mode 100644 exp_scenario/module/control/help_bubbles.lua create mode 100644 exp_scenario/module/control/inserter_pickup.lua create mode 100644 exp_scenario/module/control/inventory_clear.lua create mode 100644 exp_scenario/module/control/mine_depletion.lua create mode 100644 exp_scenario/module/control/nuke_protection.lua create mode 100644 exp_scenario/module/control/pollution_grading.lua create mode 100644 exp_scenario/module/control/protection_jail.lua create mode 100644 exp_scenario/module/control/report_jail.lua create mode 100644 exp_scenario/module/control/spawn_area.lua create mode 100644 exp_scenario/module/control/station_auto_name.lua diff --git a/exp_legacy/module/config/_file_loader.lua b/exp_legacy/module/config/_file_loader.lua index 744fece2..9e8c8e9c 100644 --- a/exp_legacy/module/config/_file_loader.lua +++ b/exp_legacy/module/config/_file_loader.lua @@ -6,28 +6,6 @@ return { "expcore.player_data", -- must be loaded first to register event handlers - --- Addons - "modules.addons.chat-popups", - "modules.addons.damage-popups", - "modules.addons.death-logger", - "modules.addons.advanced-start", - "modules.addons.spawn-area", - "modules.addons.compilatron", - "modules.addons.scorched-earth", - "modules.addons.pollution-grading", - "modules.addons.station-auto-name", - "modules.addons.discord-alerts", - "modules.addons.chat-reply", - "modules.addons.tree-decon", - "modules.addons.afk-kick", - "modules.addons.report-jail", - "modules.addons.protection-jail", - "modules.addons.deconlog", - "modules.addons.nukeprotect", - "modules.addons.inserter", - "modules.addons.miner", - "modules.addons.logging", - -- Control "modules.control.vlayer", diff --git a/exp_legacy/module/config/advanced_start.lua b/exp_legacy/module/config/advanced_start.lua index 6653d06b..31fb6154 100644 --- a/exp_legacy/module/config/advanced_start.lua +++ b/exp_legacy/module/config/advanced_start.lua @@ -76,6 +76,7 @@ return { skip_intro = true, --- @setting skip_intro skips the intro given in the default factorio free play scenario skip_victory = true, --- @setting skip_victory will skip the victory screen when a rocket is launched friendly_fire = false, --- @setting friendly_fire weather players will be able to attack each other on the same force + disable_crashsite = true, --- @setting disable_crashsite weather to disable creation of the crashsite enemy_expansion = false, --- @setting enemy_expansion a catch all for in case the map settings file fails to load chart_radius = 10 * 32, --- @setting chart_radius the number of tiles that will be charted when the map starts items = { --- @setting items items and there condition for being given diff --git a/exp_legacy/module/config/afk_kick.lua b/exp_legacy/module/config/afk_kick.lua index c401af08..cb982ade 100644 --- a/exp_legacy/module/config/afk_kick.lua +++ b/exp_legacy/module/config/afk_kick.lua @@ -1,9 +1,13 @@ +local Roles = require("modules.exp_legacy.expcore.roles") + return { admin_as_active = true, --- @setting admin_as_active When true admins will be treated as active regardless of afk time trust_as_active = true, --- @setting trust_as_active When true trusted players (by playtime) will be treated as active regardless of afk time - active_role = "Veteran", --- @setting active_role When not nil a player with this role will be treated as active regardless of afk time afk_time = 3600 * 10, --- @setting afk_time The time in ticks that must pass for a player to be considered afk kick_time = 3600 * 30, --- @setting kick_time The time in ticks that must pass without any active players for all players to be kicked trust_time = 3600 * 60 * 10, --- @setting trust_time The time in ticks that a player must be online for to count as trusted update_time = 3600 * 30, --- @setting update_time How often in ticks the script checks for active players + custom_active_check = function(player) + return Roles.get_player_highest_role(player).index <= Roles.get_role_from_any("Veteran").index + end, } diff --git a/exp_legacy/module/config/chat_reply.lua b/exp_legacy/module/config/chat_reply.lua index 4a1cd6e8..1321f96d 100644 --- a/exp_legacy/module/config/chat_reply.lua +++ b/exp_legacy/module/config/chat_reply.lua @@ -4,9 +4,14 @@ local ExpUtil = require("modules/exp_util") local Async = require("modules/exp_util/async") +local floor = math.floor +local random = math.random +local format_string = string.format +local locale_reply = "exp_chat-auto-reply.chat-reply" + local send_message_async = Async.register(function(player, message) - if player == true then + if player == nil then game.print(message) else player.print(message) @@ -18,9 +23,7 @@ local afk_time_units = { seconds = true, } --- luacheck:ignore 212/player 212/is_command return { - allow_command_prefix_for_messages = true, --- @setting allow_command_prefix_for_messages when true any message trigger will print to all player when prefixed messages = { --- @setting messages will trigger when ever the word is said ["discord"] = { "info.discord" }, ["expgaming"] = { "info.website" }, @@ -32,14 +35,23 @@ return { ["command"] = { "info.custom-commands" }, ["commands"] = { "info.custom-commands" }, ["softmod"] = { "info.softmod" }, + ["plugin"] = { "info.softmod" }, ["script"] = { "info.softmod" }, - ["loop"] = { "chat-bot.loops" }, + ["redmew"] = { "info.redmew" }, + ["comfy"] = { "info.redmew" }, ["rhd"] = { "info.lhd" }, ["lhd"] = { "info.lhd" }, - ["roundabout"] = { "chat-bot.loops" }, - ["roundabouts"] = { "chat-bot.loops" }, - ["redmew"] = { "info.redmew" }, - ["afk"] = function(player, _is_command) + ["loop"] = { "exp_chat-auto-reply.reply-loops" }, + ["roundabout"] = { "exp_chat-auto-reply.reply-loops" }, + ["roundabouts"] = { "exp_chat-auto-reply.reply-loops" }, + ["clusterio"] = { "exp_chat-auto-reply.reply-clusterio" }, + ["players"] = function() + return { "exp_chat-auto-reply.reply-players", #game.players } + end, + ["online"] = function() + return { "exp_chat-auto-reply.reply-online", #game.connected_players } + end, + ["afk"] = function(player) local max = player for _, next_player in pairs(game.connected_players) do if max.afk_time < next_player.afk_time then @@ -47,84 +59,76 @@ return { end end - return { "chat-bot.afk", max.name, ExpUtil.format_time_locale(max.afk_time, "long", afk_time_units) } - end, - ["players"] = function(_player, _is_command) - return { "chat-bot.players", #game.players } - end, - ["online"] = function(_player, _is_command) - return { "chat-bot.players-online", #game.connected_players } - end, - ["r!verify"] = function(player, _is_command) - return { "chat-bot.verify", player.name } + return { "exp_chat-auto-reply.reply-afk", max.name, ExpUtil.format_time_locale(max.afk_time, "long", afk_time_units) } end, }, + allow_command_prefix_for_messages = true, --- @setting allow_command_prefix_for_messages when true any message trigger will print to all player when prefixed command_admin_only = false, --- @setting command_admin_only when true will only allow chat commands for admins - command_permission = "command/chat-bot", --- @setting command_permission the permission used to allow command prefixes + command_permission = "command/chat-commands", --- @setting command_permission the permission used to allow command prefixes command_prefix = "!", --- @setting command_prefix prefix used for commands below and to print to all players (if enabled above) commands = { --- @setting commands will trigger only when command prefix is given - ["dev"] = { "chat-bot.not-real-dev" }, - ["blame"] = function(player, _is_command) + ["dev"] = { "exp_chat-auto-reply.reply-dev" }, + ["magic"] = { "exp_chat-auto-reply.reply-magic" }, + ["aids"] = { "exp_chat-auto-reply.reply-aids" }, + ["riot"] = { "exp_chat-auto-reply.reply-riot" }, + ["lenny"] = { "exp_chat-auto-reply.reply-lenny" }, + ["blame"] = function(player) local names = { "Cooldude2606", "arty714", "badgamernl", "mark9064", "aldldl", "Drahc_pro", player.name } for _, next_player in pairs(game.connected_players) do names[#names + 1] = next_player.name end - return { "chat-bot.blame", table.get_random(names) } + return { "exp_chat-auto-reply.reply-blame", table.get_random(names) } end, - ["magic"] = { "chat-bot.magic" }, - ["aids"] = { "chat-bot.aids" }, - ["riot"] = { "chat-bot.riot" }, - ["lenny"] = { "chat-bot.lenny" }, - ["hodor"] = function(_player, _is_command) + ["hodor"] = function() local options = { "?", ".", "!", "!!!" } - return { "chat-bot.hodor", table.get_random(options) } + return { "exp_chat-auto-reply.reply-hodor", table.get_random(options) } end, - ["evolution"] = function(player, _is_command) - return { "chat-bot.current-evolution", string.format("%.2f", game.forces["enemy"].get_evolution_factor(player.surface)) } + ["evolution"] = function(player) + return { "exp_chat-auto-reply.reply-evolution", format_string("%.2f", game.forces["enemy"].get_evolution_factor(player.surface)) } end, - ["makepopcorn"] = function(player, _is_command) - local timeout = math.floor(180 * (math.random() + 0.5)) - send_message_async(true, { "chat-bot.reply", { "chat-bot.get-popcorn-1" } }) - send_message_async:start_after(timeout, true, { "chat-bot.reply", { "chat-bot.get-popcorn-2", player.name } }) + ["makepopcorn"] = function(player) + local timeout = floor(180 * (random() + 0.5)) + send_message_async:start_after(timeout, nil, { locale_reply, { "exp_chat-auto-reply.reply-popcorn-2", player.name } }) + return { locale_reply, { "exp_chat-auto-reply.reply-popcorn-1" } } end, - ["passsomesnaps"] = function(player, _is_command) - local timeout = math.floor(180 * (math.random() + 0.5)) - send_message_async(player, { "chat-bot.reply", { "chat-bot.get-snaps-1" } }) - send_message_async:start_after(timeout, true, { "chat-bot.reply", { "chat-bot.get-snaps-2", player.name } }) - send_message_async:start_after(timeout * (math.random() + 0.5), true, { "chat-bot.reply", { "chat-bot.get-snaps-3", player.name } }) + ["passsomesnaps"] = function(player) + local timeout = floor(180 * (random() + 0.5)) + send_message_async:start_after(timeout, nil, { locale_reply, { "exp_chat-auto-reply.reply-snaps-2", player.name } }) + send_message_async:start_after(timeout * (random() + 0.5), nil, { locale_reply, { "exp_chat-auto-reply.reply-snaps-3", player.name } }) + return { locale_reply, { "exp_chat-auto-reply.reply-snaps-1" } } end, - ["makecocktail"] = function(player, _is_command) - local timeout = math.floor(180 * (math.random() + 0.5)) - send_message_async(true, { "chat-bot.reply", { "chat-bot.get-cocktail-1" } }) - send_message_async:start_after(timeout, true, { "chat-bot.reply", { "chat-bot.get-cocktail-2", player.name } }) - send_message_async:start_after(timeout * (math.random() + 0.5), true, { "chat-bot.reply", { "chat-bot.get-cocktail-3", player.name } }) + ["makecocktail"] = function(player) + local timeout = floor(180 * (random() + 0.5)) + send_message_async:start_after(timeout, nil, { locale_reply, { "exp_chat-auto-reply.reply-cocktail-2", player.name } }) + send_message_async:start_after(timeout * (random() + 0.5), nil, { locale_reply, { "exp_chat-auto-reply.reply-cocktail-3", player.name } }) + return { locale_reply, { "exp_chat-auto-reply.reply-cocktail-1" } } end, - ["makecoffee"] = function(player, _is_command) - local timeout = math.floor(180 * (math.random() + 0.5)) - send_message_async(true, { "chat-bot.reply", { "chat-bot.make-coffee-1" } }) - send_message_async:start_after(timeout, true, { "chat-bot.reply", { "chat-bot.make-coffee-2", player.name } }) + ["makecoffee"] = function(player) + local timeout = floor(180 * (random() + 0.5)) + send_message_async:start_after(timeout, nil, { locale_reply, { "exp_chat-auto-reply.reply-coffee-2", player.name } }) + return { locale_reply, { "exp_chat-auto-reply.reply-coffee-1" } } end, - ["orderpizza"] = function(player, _is_command) - local timeout = math.floor(180 * (math.random() + 0.5)) - send_message_async(true, { "chat-bot.reply", { "chat-bot.order-pizza-1" } }) - send_message_async:start_after(timeout, true, { "chat-bot.reply", { "chat-bot.order-pizza-2", player.name } }) - send_message_async:start_after(timeout * (math.random() + 0.5), true, { "chat-bot.reply", { "chat-bot.order-pizza-3", player.name } }) + ["orderpizza"] = function(player) + local timeout = floor(180 * (random() + 0.5)) + send_message_async:start_after(timeout, nil, { locale_reply, { "exp_chat-auto-reply.reply-pizza-2", player.name } }) + send_message_async:start_after(timeout * (random() + 0.5), nil, { locale_reply, { "exp_chat-auto-reply.reply-pizza-3", player.name } }) + return { locale_reply, { "exp_chat-auto-reply.reply-pizza-1" } } end, - ["maketea"] = function(player, _is_command) - local timeout = math.floor(180 * (math.random() + 0.5)) - send_message_async(true, { "chat-bot.reply", { "chat-bot.make-tea-1" } }) - send_message_async:start_after(timeout, true, { "chat-bot.reply", { "chat-bot.make-tea-2", player.name } }) + ["maketea"] = function(player) + local timeout = floor(180 * (random() + 0.5)) + send_message_async:start_after(timeout, nil, { locale_reply, { "exp_chat-auto-reply.reply-tea-2", player.name } }) + return { locale_reply, { "exp_chat-auto-reply.reply-tea-1" } } end, - ["meadplease"] = function(player, _is_command) - local timeout = math.floor(180 * (math.random() + 0.5)) - send_message_async(true, { "chat-bot.reply", { "chat-bot.get-mead-1" } }) - send_message_async:start_after(timeout, true, { "chat-bot.reply", { "chat-bot.get-mead-2", player.name } }) + ["meadplease"] = function(player) + local timeout = floor(180 * (random() + 0.5)) + send_message_async:start_after(timeout, nil, { locale_reply, { "exp_chat-auto-reply.reply-mead-2", player.name } }) + return { locale_reply, { "exp_chat-auto-reply.reply-mead-1" } } end, - ["passabeer"] = function(player, _is_command) - local timeout = math.floor(180 * (math.random() + 0.5)) - send_message_async(true, { "chat-bot.reply", { "chat-bot.get-beer-1" } }) - send_message_async:start_after(timeout, true, { "chat-bot.reply", { "chat-bot.get-beer-2", player.name } }) + ["passabeer"] = function(player) + local timeout = floor(180 * (random() + 0.5)) + send_message_async:start_after(timeout, nil, { locale_reply, { "exp_chat-auto-reply.reply-beer-2", player.name } }) + return { locale_reply, { "exp_chat-auto-reply.reply-beer-1" } } end, }, } diff --git a/exp_legacy/module/config/compilatron.lua b/exp_legacy/module/config/compilatron.lua index 933b5571..b058c2d4 100644 --- a/exp_legacy/module/config/compilatron.lua +++ b/exp_legacy/module/config/compilatron.lua @@ -3,21 +3,23 @@ return { message_cycle = 60 * 15, --- @setting message_cycle 15 seconds default, how often (in ticks) the messages will cycle - locations = { --- @setting locations defines the spawn locations for all compilatrons - ["Spawn"] = { x = 0, y = 0 }, - }, - messages = { --- @setting messages the messages that each one will say, must be same name as its location + locations = { ["Spawn"] = { - { "info.website" }, - { "info.read-readme" }, - { "info.discord" }, - { "info.softmod" }, - { "info.redmew" }, - { "info.custom-commands" }, - { "info.status" }, - { "info.lhd" }, - { "info.github" }, - { "info.patreon" }, - }, + spawn_position = { x = 0, y = 0 }, + spawn_surface = "nauvis", + entity_name = "small-biter", + messages = { + { "info.website" }, + { "info.read-readme" }, + { "info.discord" }, + { "info.softmod" }, + { "info.redmew" }, + { "info.custom-commands" }, + { "info.status" }, + { "info.lhd" }, + { "info.github" }, + { "info.patreon" }, + }, + } }, } diff --git a/exp_legacy/module/config/death_logger.lua b/exp_legacy/module/config/death_logger.lua index 2a923df2..ca90677a 100644 --- a/exp_legacy/module/config/death_logger.lua +++ b/exp_legacy/module/config/death_logger.lua @@ -4,13 +4,12 @@ -- @config Death-Logger return { - -- WIP_allow_teleport_to_body_command=false, -- allows use of /return-to-body which teleports you to your last death - -- WIP_allow_collect_bodies_command=false, -- allows use of /collect-body which returns all your items to you and removes the body - use_chests_as_bodies = false, --- @setting use_chests_as_bodies weather items should be moved into a chest when a player dies - auto_collect_bodies = true, --- @setting auto_collect_bodies enables items being returned to the spawn point in chests upon corpse expiring + collect_corpses = true, --- @setting collect_corpses enables items being returned to the spawn point in chests upon corpse expiring show_map_markers = true, --- @setting show_map_markers shows markers on the map where bodies are + clean_map_markers = false, include_time_of_death = true, --- @setting include_time_of_death weather to include the time of death on the map marker map_icon = nil, --- @setting map_icon the icon that the map marker shows; nil means no icon; format as a SingleID show_light_at_corpse = true, --- @setting show_light_at_corpse if a light should be rendered at the corpse show_line_to_corpse = true, --- @setting show_line_to_corpse if a line should be rendered from you to your corpse + period_check_map_tags = 60 * 60 * 5, } diff --git a/exp_legacy/module/config/expcore/roles.lua b/exp_legacy/module/config/expcore/roles.lua index d03933b2..618b57f9 100644 --- a/exp_legacy/module/config/expcore/roles.lua +++ b/exp_legacy/module/config/expcore/roles.lua @@ -204,7 +204,7 @@ Roles.new_role("Veteran", "Vet") :set_custom_color{ r = 140, g = 120, b = 200 } :set_parent("Member") :allow{ - "command/chat-bot", + "command/chat-commands", "command/clear-ground-items", "command/clear-blueprints-radius", "command/set-trains-to-automatic", diff --git a/exp_legacy/module/config/logging.lua b/exp_legacy/module/config/logging.lua index eefe48b2..eb66cfbf 100644 --- a/exp_legacy/module/config/logging.lua +++ b/exp_legacy/module/config/logging.lua @@ -15,16 +15,16 @@ return { }, rocket_launch_display_rate = 500, disconnect_reason = { - [defines.disconnect_reason.quit] = " left the game", - [defines.disconnect_reason.dropped] = " was dropped from the game", - [defines.disconnect_reason.reconnect] = " is reconnecting", - [defines.disconnect_reason.wrong_input] = " was having a wrong input", - [defines.disconnect_reason.desync_limit_reached] = " had desync limit reached", - [defines.disconnect_reason.cannot_keep_up] = " cannot keep up", - [defines.disconnect_reason.afk] = " was afk", - [defines.disconnect_reason.kicked] = " was kicked", - [defines.disconnect_reason.kicked_and_deleted] = " was kicked and deleted", - [defines.disconnect_reason.banned] = " was banned", - [defines.disconnect_reason.switching_servers] = " is switching servers", + [defines.disconnect_reason.quit] = "left the game", + [defines.disconnect_reason.dropped] = "was dropped from the game", + [defines.disconnect_reason.reconnect] = "is reconnecting", + [defines.disconnect_reason.wrong_input] = "was having a wrong input", + [defines.disconnect_reason.desync_limit_reached] = "had desync limit reached", + [defines.disconnect_reason.cannot_keep_up] = "cannot keep up", + [defines.disconnect_reason.afk] = "was afk", + [defines.disconnect_reason.kicked] = "was kicked", + [defines.disconnect_reason.kicked_and_deleted] = "was kicked and deleted", + [defines.disconnect_reason.banned] = "was banned", + [defines.disconnect_reason.switching_servers] = "is switching servers", }, } diff --git a/exp_legacy/module/config/nukeprotect.lua b/exp_legacy/module/config/nukeprotect.lua index f80b6a7a..a49c4e12 100644 --- a/exp_legacy/module/config/nukeprotect.lua +++ b/exp_legacy/module/config/nukeprotect.lua @@ -25,6 +25,6 @@ return { }, }, }, - ignore_permisison = "bypass-nukeprotect", -- @setting ignore_permisison The permission that nukeprotect will ignore + ignore_permission = "bypass-nukeprotect", -- @setting ignore_permission The permission that nukeprotect will ignore ignore_admins = true, -- @setting ignore_admins Ignore admins, true by default. Allows usage outside of the roles module } diff --git a/exp_legacy/module/config/spawn_area.lua b/exp_legacy/module/config/spawn_area.lua index 3d6c0d80..7f95462a 100644 --- a/exp_legacy/module/config/spawn_area.lua +++ b/exp_legacy/module/config/spawn_area.lua @@ -15,10 +15,10 @@ return { refill_time = 60 * 60 * 5, --- @setting refill_time The time in ticks between each refill of the turrets, only change if having lag issues offset = { x = 0, y = 0 }, --- @setting offset The position offset to apply to turrets locations = { --- @setting locations The locations of all turrets, this list can change during runtime - { surface = 1, position = { x = -3, y = -3 } }, - { surface = 1, position = { x = 3, y = -3 } }, - { surface = 1, position = { x = -3, y = 3 } }, - { surface = 1, position = { x = 3, y = 3 } }, + { -3, -3 }, + { 3, -3 }, + { -3, 3 }, + { 3, 3 }, }, }, afk_belts = { --- @setting afk_belts Settings relating to adding afk belts to spawn @@ -242,6 +242,7 @@ return { resource_refill_nearby = { enabled = false, range = 128, + refill_time = 36000, resources_name = { "iron-ore", "copper-ore", diff --git a/exp_legacy/module/expcore/roles.lua b/exp_legacy/module/expcore/roles.lua index 513a276e..b84875f2 100644 --- a/exp_legacy/module/expcore/roles.lua +++ b/exp_legacy/module/expcore/roles.lua @@ -802,7 +802,7 @@ local allowed = role:is_allowed('command/kill') ]] function Roles._prototype:is_allowed(action) - local is_root = Roles.config.internal.root.name == self.name + local is_root = Roles.config.internal.root == self.name return self.allowed_actions[action] or self.allow_all_actions or is_root end diff --git a/exp_legacy/module/locale/en/addons.cfg b/exp_legacy/module/locale/en/addons.cfg index 0a868698..0cf56506 100644 --- a/exp_legacy/module/locale/en/addons.cfg +++ b/exp_legacy/module/locale/en/addons.cfg @@ -1,11 +1,3 @@ -[chat-popup] -message=__1__: __2__ -ping=You have been mentioned in chat by __1__. - -[damage-popup] -player-health=__1__ -player-damage=__1__ - [links] discord=https://discord.explosivegaming.nl website=https://www.explosivegaming.nl @@ -37,59 +29,3 @@ ban=You were banned for having too many warnings; visit __1__ to request a ban a script-warning=You are receiving script warnings; if you recive too many you will receive a permanent warning (__1__/__2__) script-warning-removed=A script warning has expired (__1__/__2__) script-warning-limit=__1__ has received a permanent warning from the script. - -[chat-bot] -reply=[Chat Bot] __1__ -disallow=You can't use global chat commands -players-online=There are __1__ players online -players=There have been __1__ players on this map -not-real-dev=Cooldude2606 is a dev for this server and makes the softmod and is not a factorio dev. -softmod=A softmod is a custom scenario that runs on this server,an example is the player list. -blame=Blame __1__ for what just happened! -afk=You're afk? Look at __1__, that player has been afk for: __2__ -current-evolution=Current evolution factor is __1__ -magic=Fear the admin magic (ノ゚∀゚)ノ⌒・*:.。. .。.:*・゜゚・*☆ -aids=≖ ‿ ≖ Fear the aids of a public server ≖ ‿ ≖ -riot=(admins) ┬┴┬┴┤ᵒ_ᵒ)├┬┴┬┴ ‹ ‹\(´ω` )/››‹‹\ (  ´)/››‹‹\ ( ´ω`)/›› (rest of server) -loops=NO LOOPS; LOOPS ARE BAD; JUST NO LOOPS!!!!!; IF YOU MAKE A LOOP.... IT WILL NOT END WELL!!!!!!! -lenny=( ͡° ͜ʖ ͡°) -hodor=Hodor -get-popcorn-1=Heating the oil and waiting for the popping sound... -get-popcorn-2=__1__ your popcorn is finished. Lean backwards and watch the drama unfold. -get-snaps-1=Pouring the glasses and finding the correct song book... -get-snaps-2=Singing a song...🎤🎶 -get-snaps-3=schkål, my friends! -get-cocktail-1= 🍸 Inintiating mind reading unit... 🍸 -get-cocktail-2= 🍸 Mixing favourite ingredients of __1__ 🍸 -get-cocktail-3=🍸 __1__ your cocktail is done.🍸 -make-coffee-1= ☕ Boiling the water and grinding the coffee beans... ☕ -make-coffee-2= ☕ __1__ we ran out of coffe beans! Have some tea instead. ☕ -order-pizza-1= 🍕 Finding nearest pizza supplier... 🍕 -order-pizza-2= 🍕 Figuring out the favourite pizza of __1__ 🍕 -order-pizza-3= 🍕 __1__ your pizza is here! 🍕 -make-tea-1= ☕ Boiling the water... ☕ -make-tea-2= ☕ __1__ your tea is done! ☕ -get-mead-1= Filling the drinking horn -get-mead-2= Skål! -get-beer-1= 🍺 Pouring A Glass 🍺 -get-beer-2= 🍻 Chears Mate 🍻 -verify=Please return to our discord and type r!verify __1__ - -[afk-kick] -message=All players were kicked because everyone was AFK. - -[report-jail] -jail=__1__ was jailed because they were reported too many times. Please wait for a moderator. - -[protection-jail] -jail=__1__ was jailed because they removed too many protected entities. Please wait for a moderator. - -[nukeprotect] -found=You cannot have __1__ in your inventory, so it was placed into the chests at spawn. - -[logging] -add-l=[RES] __1__ at level __2__ has been researched -add-n=[RES] __1__ has been researched - -[deconlog] -decon=__1__ tried to deconstruct from __2__ to __3__ which has __4__ items. diff --git a/exp_legacy/module/locale/en/gui.cfg b/exp_legacy/module/locale/en/gui.cfg index 36ac13a2..4df0378c 100644 --- a/exp_legacy/module/locale/en/gui.cfg +++ b/exp_legacy/module/locale/en/gui.cfg @@ -180,12 +180,6 @@ data-required=Required data-misc=Miscellaneous data-format=__1____2__ -[tree-decon] -main-tooltip=Toggle fast tree decon -enabled=enabled -disabled=disabled -toggle-msg=Fast decon has been __1__ - [bonus] description=Get / Set the amount of bonus you receive. arg-amount=Amount to set your bonus to, 0 will disable bonus. diff --git a/exp_legacy/module/locale/zh-CN/addons.cfg b/exp_legacy/module/locale/zh-CN/addons.cfg index c2f133d2..f586a2ac 100644 --- a/exp_legacy/module/locale/zh-CN/addons.cfg +++ b/exp_legacy/module/locale/zh-CN/addons.cfg @@ -1,11 +1,3 @@ -[chat-popup] -message=__1__: __2__ -ping=__1__ 在信息中提到了你。 - -[damage-popup] -player-health=__1__ -player-damage=__1__ - [links] discord=https://discord.explosivegaming.nl website=https://www.explosivegaming.nl @@ -37,59 +29,3 @@ ban=你已因為被警告太多次而被封禁; 可以到 __1__ 申訴. script-warning=這是系統發出的自動警告 (__1__/__2__) script-warning-removed=系統發出的自動警告已失效 (__1__/__2__) script-warning-limit=__1__ 已被系統發出了一則自動警告。 - -[chat-bot] -reply=[Chat Bot] __1__ -disallow=你沒有權限使用這個指令。 -players-online=現在有 __1__ 人上線。 -players=本地圖現在有 __1__ 人曾上線。 -not-real-dev=Cooldude2606 只是本場境的開發者 -softmod=這裹用了自設情境。 -blame=責怪 __1__ 吧。 -afk=看看 __1__, 他已掛機 __2__ 。 -current-evolution=現在敵人進化度為 __1__ 。 -magic=Fear the admin magic (ノ゚∀゚)ノ⌒・*:.。. .。.:*・゜゚・*☆ -aids=≖ ‿ ≖ Fear the aids of a public server ≖ ‿ ≖ -riot=(admins) ┬┴┬┴┤ᵒ_ᵒ)├┬┴┬┴ ‹ ‹\(´ω` )/››‹‹\ (  ´)/››‹‹\ ( ´ω`)/›› (rest of server) -loops=架設迴旋處最終後果都不好。 -lenny=( ͡° ͜ʖ ͡°) -hodor=Hodor -get-popcorn-1=Heating the oil and waiting for the popping sound... -get-popcorn-2=__1__ your popcorn is finished. Lean backwards and watch the drama unfold. -get-snaps-1=Pouring the glasses and finding the correct song book... -get-snaps-2=Singing a song...🎤🎶 -get-snaps-3=schkål, my friends! -get-cocktail-1= 🍸 Inintiating mind reading unit... 🍸 -get-cocktail-2= 🍸 Mixing favourite ingredients of __1__ 🍸 -get-cocktail-3=🍸 __1__ your cocktail is done.🍸 -make-coffee-1= ☕ Boiling the water and grinding the coffee beans... ☕ -make-coffee-2= ☕ __1__ we ran out of coffe beans! Have some tea instead. ☕ -order-pizza-1= 🍕 Finding nearest pizza supplier... 🍕 -order-pizza-2= 🍕 Figuring out the favourite pizza of __1__ 🍕 -order-pizza-3= 🍕 __1__ your pizza is here! 🍕 -make-tea-1= ☕ Boiling the water... ☕ -make-tea-2= ☕ __1__ your tea is done! ☕ -get-mead-1= Filling the drinking horn -get-mead-2= Skål! -get-beer-1= 🍺 Pouring A Glass 🍺 -get-beer-2= 🍻 Chears Mate 🍻 -verify=Please return to our discord and type r!verify __1__ - -[afk-kick] -message=因地圖中沒有活躍玩家,所以所有人都已被請離。 - -[report-jail] -jail=__1__ 因被多次舉報而被禁止行動。請等候管理員作出下一步處理。 - -[protection-jail] -jail=__1__ 因被多次拆除受保護物體而被禁止行動。請等候管理員作出下一步處理。 - -[nukeprotect] -found=你的用戶組不允許你有 __1__ ,所以該物品已放在出生點的箱子。 - -[logging] -add-l=[RES] __1__ at level __2__ has been researched -add-n=[RES] __1__ has been researched - -[deconlog] -decon=__1__ 試圖拆除在 __2__ 到 __3__ ,有 __4__ 個物品。 diff --git a/exp_legacy/module/locale/zh-CN/gui.cfg b/exp_legacy/module/locale/zh-CN/gui.cfg index 914de6fc..5eeff4ed 100644 --- a/exp_legacy/module/locale/zh-CN/gui.cfg +++ b/exp_legacy/module/locale/zh-CN/gui.cfg @@ -180,12 +180,6 @@ data-required=需要 data-misc=雜項 data-format=__1____2__ -[tree-decon] -main-tooltip=樹木快速拆除選項 -enabled=啟用 -disabled=停用 -toggle-msg=樹木快速拆除已 __1__ - [bonus] description=取得 / 設定 Bonus 量。 arg-amount=Bonus 數量, 0 來停用。 diff --git a/exp_legacy/module/locale/zh-TW/addons.cfg b/exp_legacy/module/locale/zh-TW/addons.cfg index c2f133d2..f586a2ac 100644 --- a/exp_legacy/module/locale/zh-TW/addons.cfg +++ b/exp_legacy/module/locale/zh-TW/addons.cfg @@ -1,11 +1,3 @@ -[chat-popup] -message=__1__: __2__ -ping=__1__ 在信息中提到了你。 - -[damage-popup] -player-health=__1__ -player-damage=__1__ - [links] discord=https://discord.explosivegaming.nl website=https://www.explosivegaming.nl @@ -37,59 +29,3 @@ ban=你已因為被警告太多次而被封禁; 可以到 __1__ 申訴. script-warning=這是系統發出的自動警告 (__1__/__2__) script-warning-removed=系統發出的自動警告已失效 (__1__/__2__) script-warning-limit=__1__ 已被系統發出了一則自動警告。 - -[chat-bot] -reply=[Chat Bot] __1__ -disallow=你沒有權限使用這個指令。 -players-online=現在有 __1__ 人上線。 -players=本地圖現在有 __1__ 人曾上線。 -not-real-dev=Cooldude2606 只是本場境的開發者 -softmod=這裹用了自設情境。 -blame=責怪 __1__ 吧。 -afk=看看 __1__, 他已掛機 __2__ 。 -current-evolution=現在敵人進化度為 __1__ 。 -magic=Fear the admin magic (ノ゚∀゚)ノ⌒・*:.。. .。.:*・゜゚・*☆ -aids=≖ ‿ ≖ Fear the aids of a public server ≖ ‿ ≖ -riot=(admins) ┬┴┬┴┤ᵒ_ᵒ)├┬┴┬┴ ‹ ‹\(´ω` )/››‹‹\ (  ´)/››‹‹\ ( ´ω`)/›› (rest of server) -loops=架設迴旋處最終後果都不好。 -lenny=( ͡° ͜ʖ ͡°) -hodor=Hodor -get-popcorn-1=Heating the oil and waiting for the popping sound... -get-popcorn-2=__1__ your popcorn is finished. Lean backwards and watch the drama unfold. -get-snaps-1=Pouring the glasses and finding the correct song book... -get-snaps-2=Singing a song...🎤🎶 -get-snaps-3=schkål, my friends! -get-cocktail-1= 🍸 Inintiating mind reading unit... 🍸 -get-cocktail-2= 🍸 Mixing favourite ingredients of __1__ 🍸 -get-cocktail-3=🍸 __1__ your cocktail is done.🍸 -make-coffee-1= ☕ Boiling the water and grinding the coffee beans... ☕ -make-coffee-2= ☕ __1__ we ran out of coffe beans! Have some tea instead. ☕ -order-pizza-1= 🍕 Finding nearest pizza supplier... 🍕 -order-pizza-2= 🍕 Figuring out the favourite pizza of __1__ 🍕 -order-pizza-3= 🍕 __1__ your pizza is here! 🍕 -make-tea-1= ☕ Boiling the water... ☕ -make-tea-2= ☕ __1__ your tea is done! ☕ -get-mead-1= Filling the drinking horn -get-mead-2= Skål! -get-beer-1= 🍺 Pouring A Glass 🍺 -get-beer-2= 🍻 Chears Mate 🍻 -verify=Please return to our discord and type r!verify __1__ - -[afk-kick] -message=因地圖中沒有活躍玩家,所以所有人都已被請離。 - -[report-jail] -jail=__1__ 因被多次舉報而被禁止行動。請等候管理員作出下一步處理。 - -[protection-jail] -jail=__1__ 因被多次拆除受保護物體而被禁止行動。請等候管理員作出下一步處理。 - -[nukeprotect] -found=你的用戶組不允許你有 __1__ ,所以該物品已放在出生點的箱子。 - -[logging] -add-l=[RES] __1__ at level __2__ has been researched -add-n=[RES] __1__ has been researched - -[deconlog] -decon=__1__ 試圖拆除在 __2__ 到 __3__ ,有 __4__ 個物品。 diff --git a/exp_legacy/module/locale/zh-TW/gui.cfg b/exp_legacy/module/locale/zh-TW/gui.cfg index 914de6fc..5eeff4ed 100644 --- a/exp_legacy/module/locale/zh-TW/gui.cfg +++ b/exp_legacy/module/locale/zh-TW/gui.cfg @@ -180,12 +180,6 @@ data-required=需要 data-misc=雜項 data-format=__1____2__ -[tree-decon] -main-tooltip=樹木快速拆除選項 -enabled=啟用 -disabled=停用 -toggle-msg=樹木快速拆除已 __1__ - [bonus] description=取得 / 設定 Bonus 量。 arg-amount=Bonus 數量, 0 來停用。 diff --git a/exp_legacy/module/modules/addons/advanced-start.lua b/exp_legacy/module/modules/addons/advanced-start.lua deleted file mode 100644 index f544658e..00000000 --- a/exp_legacy/module/modules/addons/advanced-start.lua +++ /dev/null @@ -1,48 +0,0 @@ ---- Adds a better method of player starting items based on production levels. --- @addon Advanced-Start - -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local config = require("modules.exp_legacy.config.advanced_start") --- @dep config.advanced_start -local items = config.items - -Event.add(defines.events.on_player_created, function(event) - local player = game.players[event.player_index] - -- game init settings - if event.player_index == 1 then - player.force.friendly_fire = config.friendly_fire - game.map_settings.enemy_expansion.enabled = config.enemy_expansion - local r = config.chart_radius - local p = player.physical_position - player.force.chart(player.physical_surface, { { p.x - r, p.y - r }, { p.x + r, p.y + r } }) - end - -- spawn items - for item, callback in pairs(items) do - if type(callback) == "function" then - local stats = player.force.get_item_production_statistics(player.physical_surface) - local made = stats.get_input_count(item) - local success, count = pcall(callback, made, stats.get_input_count, player) - count = math.floor(count) - if success and count > 0 then - player.insert{ name = item, count = count } - end - else - player.insert{ name = item, count = callback } - end - end -end) - -Event.on_init(function() - if remote.interfaces["freeplay"] then - remote.call("freeplay", "set_created_items", {}) - --remote.call("freeplay", "set_respawn_items", {}) - remote.call("freeplay", "set_chart_distance", 0) - remote.call("freeplay", "set_disable_crashsite", true) - remote.call("freeplay", "set_skip_intro", config.skip_intro) - end - if remote.interfaces["silo_script"] then - remote.call("silo_script", "set_no_victory", config.skip_victory) - end - if remote.interfaces["space_finish_script"] then - remote.call("space_finish_script", "set_no_victory", config.skip_victory) - end -end) diff --git a/exp_legacy/module/modules/addons/afk-kick.lua b/exp_legacy/module/modules/addons/afk-kick.lua deleted file mode 100644 index ed87e3a4..00000000 --- a/exp_legacy/module/modules/addons/afk-kick.lua +++ /dev/null @@ -1,66 +0,0 @@ ---- Kicks players when all players on the server are afk --- @addon afk-kick - -local Async = require("modules/exp_util/async") -local Storage = require("modules/exp_util/storage") -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local config = require("modules.exp_legacy.config.afk_kick") --- @dep config.afk_kick - ---- Optional roles require -local Roles -if config.active_role then - Roles = require("modules.exp_legacy.expcore.roles") -end - ---- Globals -local primitives = { last_active = 0 } -Storage.register(primitives, function(tbl) - primitives = tbl -end) - ---- Kicks an afk player, used to add a delay so the gui has time to appear -local kick_player_async = - Async.register(function(player) - if game.tick - primitives.last_active < config.kick_time then return end -- Safety Catch - game.kick_player(player, "AFK while no active players on the server") - end) - ---- Check for an active player every update_time number of ticks -Event.on_nth_tick(config.update_time, function() - -- Check for active players - for _, player in ipairs(game.connected_players) do - if player.afk_time < config.afk_time - or config.admin_as_active and player.admin - or config.trust_as_active and player.online_time > config.trust_time - or config.active_role and (Roles.get_player_highest_role(player).index <= Roles.get_role_from_any(config.active_role).index) then - -- Active player was found - primitives.last_active = game.tick - return - end - end - - -- No active player was found, check if players should be kicked - if game.tick - primitives.last_active < config.kick_time then return end - - -- Kick time exceeded, kick all players - for _, player in ipairs(game.connected_players) do - -- Add a frame to say why the player was kicked - local res = player.display_resolution - local uis = player.display_scale - player.gui.screen.add{ - type = "frame", - name = "afk-kick", - caption = { "afk-kick.message" }, - }.location = { x = res.width * (0.5 - 0.11 * uis), y = res.height * (0.5 - 0.14 * uis) } - - -- Kick the player, some delay needed because network delay - kick_player_async:start_after(10, player) - end -end) - ---- Remove the screen gui if it is present -Event.add(defines.events.on_player_joined_game, function(event) - local player = game.players[event.player_index] - local frame = player.gui.screen["afk-kick"] - if frame and frame.valid then frame.destroy() end -end) diff --git a/exp_legacy/module/modules/addons/chat-popups.lua b/exp_legacy/module/modules/addons/chat-popups.lua deleted file mode 100644 index ef771697..00000000 --- a/exp_legacy/module/modules/addons/chat-popups.lua +++ /dev/null @@ -1,41 +0,0 @@ ---- Creates flying text entities when a player sends a message in chat; --- also displays a ping above users who are named in the message --- @addon Chat-Popups - -local FlyingText = require("modules/exp_util/flying_text") -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local config = require("modules.exp_legacy.config.popup_messages") --- @dep config.popup_messages - -Event.add(defines.events.on_console_chat, function(event) - if not event.player_index or event.player_index < 1 then return end - local player = game.players[event.player_index] - - -- Some basic sanity checks - if not player then return end - if not event.message then return end - - -- Sends the message as text above them - if config.show_player_messages then - FlyingText.create_as_player{ - target_player = player, - text = { "chat-popup.message", player.name, event.message }, - } - end - - if not config.show_player_mentions then return end - - -- Makes lower and removes white space from the message - local search_string = event.message:lower():gsub("%s+", "") - - -- Loops over online players to see if they name is included - for _, mentioned_player in pairs(game.connected_players) do - if mentioned_player.index ~= player.index then - if search_string:find(mentioned_player.name:lower(), 1, true) then - FlyingText.create_as_player{ - target_player = mentioned_player, - text = { "chat-popup.ping", player.name }, - } - end - end - end -end) diff --git a/exp_legacy/module/modules/addons/chat-reply.lua b/exp_legacy/module/modules/addons/chat-reply.lua deleted file mode 100644 index d0ab85cb..00000000 --- a/exp_legacy/module/modules/addons/chat-reply.lua +++ /dev/null @@ -1,50 +0,0 @@ ---- Adds auto replies to chat messages; as well as chat commands --- @addon Chat-Reply - -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles -local config = require("modules.exp_legacy.config.chat_reply") --- @dep config.chat_reply - -Event.add(defines.events.on_console_chat, function(event) - local player_index = event.player_index - if not player_index or player_index < 1 then return end - local player = game.players[player_index] - local message = event.message:lower():gsub("%s+", "") - local allowed = true - if config.command_admin_only and not player.admin then allowed = false end - if config.command_permission and not Roles.player_allowed(player, config.command_permission) then allowed = false end - - local prefix = config.command_prefix - for key_word, reply in pairs(config.messages) do - if message:find(key_word) then - local is_command = message:find(prefix .. key_word) - if type(reply) == "function" then - reply = reply(player, is_command) - end - - if is_command and allowed then - game.print{ "chat-bot.reply", reply } - elseif is_command then - player.print{ "chat-bot.disallow" } - elseif not allowed then - player.print{ "chat-bot.reply", reply } - end - end - end - - if not allowed then return end - - for key_word, reply in pairs(config.commands) do - if message:find(prefix .. key_word) then - if type(reply) == "function" then - local msg = reply(player, true) - - if reply then - game.print{ "chat-bot.reply", msg } - end - else - game.print{ "chat-bot.reply", reply } - end - end - end -end) diff --git a/exp_legacy/module/modules/addons/compilatron.lua b/exp_legacy/module/modules/addons/compilatron.lua deleted file mode 100644 index 0dcc551b..00000000 --- a/exp_legacy/module/modules/addons/compilatron.lua +++ /dev/null @@ -1,114 +0,0 @@ ---- Adds a compilatron that walks around the spawn area; adapted from redmew code --- @addon Compilatron - -local Async = require("modules/exp_util/async") -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local Storage = require("modules/exp_util/storage") -local config = require("modules.exp_legacy.config.compilatron") --- @dep config.compilatron -local messages = config.messages -local locations = config.locations - -local Public = { - compilatrons = {}, - current_messages = {}, -} - -Storage.register({ - compilatrons = Public.compilatrons, - current_messages = Public.current_messages, -}, function(tbl) - Public.compilatrons = tbl.compilatrons - Public.current_messages = tbl.current_messages -end) - -local speech_bubble_async = - Async.register(function(data) - --- @cast data { ent: LuaEntity, name: string, msg_number: number } - if not data.ent.valid then return end - - local message = - data.ent.surface.create_entity{ - name = "compi-speech-bubble", - text = messages[data.name][data.msg_number], - source = data.ent, - position = { 0, 0 }, - } - - Public.current_messages[data.name] = { - message = message, - msg_number = data.msg_number, - } - end) - ---- This will move the messages onto the next message in the loop -local function circle_messages() - for name, ent in pairs(Public.compilatrons) do - if not ent.valid then - Public.spawn_compilatron(game.players[1].surface, name) - end - local current_message = Public.current_messages[name] - local msg_number - local message - if current_message ~= nil then - message = current_message.message - if message ~= nil then - message.destroy() - end - msg_number = current_message.msg_number - msg_number = (msg_number < #messages[name]) and msg_number + 1 or 1 - else - msg_number = 1 - end - -- this calls the callback above to re-spawn the message after some time - speech_bubble_async:start_after(300, { ent = ent, name = name, msg_number = msg_number }) - end -end - -Event.on_nth_tick(config.message_cycle, circle_messages) - ---- This will add a compilatron to the global and start his message cycle --- @tparam LuaEntity entity the compilatron entity that moves around --- @tparam string name the name of the location that the compilatron is at -function Public.add_compilatron(entity, name) - if not entity and not entity.valid then - return - end - - if name == nil then - return - end - - Public.compilatrons[name] = entity - local message = entity.surface.create_entity{ - name = "compi-speech-bubble", - text = messages[name][1], - position = { 0, 0 }, - source = entity, - } - - Public.current_messages[name] = { message = message, msg_number = 1 } -end - ---- This spawns a new compilatron on a surface with the given location tag (not a position) --- @tparam LuaSurface surface the surface to spawn the compilatron on --- @tparam string location the location tag that is in the config file -function Public.spawn_compilatron(surface, location) - local position = locations[location] - local pos = surface.find_non_colliding_position("small-biter", position, 1.5, 0.5) - if pos then - local compi = surface.create_entity{ name = "small-biter", position = pos, force = game.forces.neutral } - Public.add_compilatron(compi, location) - end -end - --- When the first player is created this will create all compilatrons that are resisted in the config -Event.add(defines.events.on_player_created, function(event) - if event.player_index ~= 1 then return end - local player = game.players[event.player_index] - - for location in pairs(locations) do - Public.spawn_compilatron(player.surface, location) - end -end) - -return Public diff --git a/exp_legacy/module/modules/addons/damage-popups.lua b/exp_legacy/module/modules/addons/damage-popups.lua deleted file mode 100644 index 4201900d..00000000 --- a/exp_legacy/module/modules/addons/damage-popups.lua +++ /dev/null @@ -1,40 +0,0 @@ ---- Displays the amount of dmg that is done by players to entities; --- also shows player health when a player is attacked --- @addon Damage-Popups - -local FlyingText = require("modules/exp_util/flying_text") -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local config = require("modules.exp_legacy.config.popup_messages") --- @dep config.popup_messages - -Event.add(defines.events.on_entity_damaged, function(event) - local entity = event.entity - local cause = event.cause - local damage = math.floor(event.original_damage_amount) - local health = math.floor(entity.health) - local health_percentage = entity.get_health_ratio() - local text_colour = { r = 1 - health_percentage, g = health_percentage, b = 0 } - - -- Gets the location of the text - local size = entity.get_radius() - if size < 1 then size = 1 end - local r = (math.random() - 0.5) * size * config.damage_location_variance - local p = entity.position - local position = { x = p.x + r, y = p.y - size } - - -- Sets the message - local message - if entity.name == "character" and config.show_player_health then - message = { "damage-popup.player-health", health } - elseif entity.name ~= "character" and cause and cause.name == "character" and config.show_player_damage then - message = { "damage-popup.player-damage", damage } - end - - -- Outputs the message as floating text - if message then - FlyingText.create{ - text = message, - position = position, - color = text_colour, - } - end -end) diff --git a/exp_legacy/module/modules/addons/death-logger.lua b/exp_legacy/module/modules/addons/death-logger.lua deleted file mode 100644 index a1b9531a..00000000 --- a/exp_legacy/module/modules/addons/death-logger.lua +++ /dev/null @@ -1,165 +0,0 @@ ---- Makes markers on the map where places have died and reclaims items if not recovered --- @addon Death-Logger - -local ExpUtil = require("modules/exp_util") -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local Storage = require("modules/exp_util/storage") -local config = require("modules.exp_legacy.config.death_logger") --- @dep config.death_logger - --- Max amount of ticks a corpse can be alive -local corpse_lifetime = 60 * 60 * 15 - -local deaths = { - archive = {}, -- deaths moved here after body is gone - -- {player_name='Cooldude2606', time_of_death='15H 15M', position={x=0, y=0}, corpse=LuaEntity, tag=LuaCustomChartTag} -} -Storage.register(deaths, function(tbl) - deaths = tbl -end) - -local map_tag_time_format = ExpUtil.format_time_factory{ format = "short", hours = true, minutes = true } - ---- Creates a new death marker and saves it to the given death -local function create_map_tag(death) - local player = game.players[death.player_name] - local message = player.name .. " died" - if config.include_time_of_death then - local time = map_tag_time_format(death.time_of_death) - message = message .. " at " .. time - end - death.tag = player.force.add_chart_tag(player.physical_surface, { - position = death.position, - icon = config.map_icon, - text = message, - }) -end - ---- Checks that all map tags are present and valid --- adds missing ones, deletes expired ones -local function check_map_tags() - for index, death in ipairs(deaths) do - local map_tag = death.tag - local corpse = death.corpse - -- Check the corpse is valid - if corpse and corpse.valid then - -- Corpse is valid check the map tag - if not map_tag or not map_tag.valid then - -- Map tag is not valid make a new one - create_map_tag(death) - end - else - -- Corpse is not valid so remove the map tag - if map_tag and map_tag.valid then - map_tag.destroy() - end - -- Move the death to the archive - death.corpse = nil - death.tag = nil - table.insert(deaths.archive, death) - table.remove(deaths, index) - end - end -end - --- when a player dies a new death is added to the records and a map marker is made ---- @param event EventData.on_player_died -Event.add(defines.events.on_player_died, function(event) - local player = game.players[event.player_index] - local corpse = player.surface.find_entity("character-corpse", player.physical_position) - if not corpse or not corpse.valid then return end - if config.use_chests_as_bodies then - local inventory = assert(corpse.get_inventory(defines.inventory.character_corpse)) - local chest = ExpUtil.transfer_inventory_to_surface{ - inventory = inventory, - surface = corpse.surface, - position = corpse.position, - name = "iron-chest", - allow_creation = true, - } - - corpse.destroy() - corpse = chest - end - local death = { - player_name = player.name, - time_of_death = event.tick, - position = player.physical_position, - corpse = corpse, - } - if config.show_map_markers then - create_map_tag(death) - end - table.insert(deaths, death) - - -- Draw a light attached to the corpse with the player color - if config.show_light_at_corpse then - rendering.draw_light{ - sprite = "utility/light_medium", - color = player.color, - target = corpse, - force = player.force, - surface = player.surface, - } - end -end) - --- Draw lines to the player corpse -if config.show_line_to_corpse then - Event.add(defines.events.on_player_respawned, function(event) - local player = game.players[event.player_index] - - -- New deaths are added at the end of the deaths array, this is why - -- we are itterating over the array in reverse. This saves on the amount - -- of itterations we do. - for index = #deaths, 1, -1 do - local death = deaths[index] - - -- If the corpse has already expired break out of the loop because - -- all the deaths that will follow will be expired. - if game.tick - death.time_of_death > corpse_lifetime then break end - - -- Check if the death body is from the player - -- Check if the corpse entity is still valid - if death.player_name == player.name and death.corpse and death.corpse.valid then - local line_color = player.color - line_color.a = .3 - rendering.draw_line{ - color = line_color, - from = player.character, - to = death.corpse, - players = { event.player_index }, - width = 2, - dash_length = 1, - gap_length = 1, - surface = player.surface, - draw_on_ground = true, - } - end - end - end) -end - --- every 5 min all bodies are checked for valid map tags -if config.show_map_markers then - local check_period = 60 * 60 * 5 -- five minutes - Event.on_nth_tick(check_period, function() - check_map_tags() - end) -end - -if config.auto_collect_bodies then - --- @param event EventData.on_character_corpse_expired - Event.add(defines.events.on_character_corpse_expired, function(event) - local corpse = event.corpse - local inventory = assert(corpse.get_inventory(defines.inventory.character_corpse)) - ExpUtil.transfer_inventory_to_surface{ - inventory = inventory, - surface = corpse.surface, - name = "iron-chest", - allow_creation = true, - } - end) -end - --- this is so other modules can access the logs -return deaths diff --git a/exp_legacy/module/modules/addons/deconlog.lua b/exp_legacy/module/modules/addons/deconlog.lua deleted file mode 100644 index f5d9bad0..00000000 --- a/exp_legacy/module/modules/addons/deconlog.lua +++ /dev/null @@ -1,120 +0,0 @@ ---- Log certain actions into a file when events are triggered --- @addon Deconlog - -local ExpUtil = require("modules/exp_util") -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles -local format_number = require("util").format_number --- @dep util -local config = require("modules.exp_legacy.config.deconlog") --- @dep config.deconlog - -local write_file = helpers.write_file - -local filepath = "log/decon.log" -local seconds_time_format = ExpUtil.format_time_factory{ format = "short", hours = true, minutes = true, seconds = true } - -local function add_log(data) - write_file(filepath, data .. "\n", true, 0) -- write data -end - -local function get_secs() - return seconds_time_format(game.tick) -end - -local function pos_to_string(pos) - return tostring(pos.x) .. "," .. tostring(pos.y) -end - -local function pos_to_gps_string(pos, surface_name) - return "[gps=" .. string.format("%.1f", pos.x) .. "," .. string.format("%.1f", pos.y) .. "," .. surface_name .. "]" -end - ---- Print a message to all players who match the value of admin -local function print_to_players(admin, message) - for _, player in ipairs(game.connected_players) do - if player.admin == admin then - player.print(message) - end - end -end - -Event.on_init(function() - write_file(filepath, "\n", false, 0) -- write data -end) - -if config.decon_area then - Event.add(defines.events.on_player_deconstructed_area, function(e) - if e.alt then - return - end - - local player = game.players[e.player_index] - - if Roles.player_has_flag(player, "deconlog-bypass") then - return - end - - local items = e.surface.find_entities_filtered{ area = e.area, force = player.force } - - if #items > 250 then - print_to_players(true, { - "deconlog.decon", - player.name, - pos_to_gps_string(e.area.left_top, e.surface.name), - pos_to_gps_string(e.area.right_bottom, e.surface.name), - format_number(#items, false), - }) - end - - add_log(get_secs() .. "," .. player.name .. ",decon_area," .. e.surface.name .. "," .. pos_to_string(e.area.left_top) .. "," .. pos_to_string(e.area.right_bottom)) - end) -end - -if config.built_entity then - Event.add(defines.events.on_built_entity, function(e) - if not e.player_index then return end - local player = game.players[e.player_index] - if Roles.player_has_flag(player, "deconlog-bypass") then - return - end - local ent = e.entity - add_log(get_secs() .. "," .. player.name .. ",built_entity," .. ent.name .. "," .. pos_to_string(ent.position) .. "," .. tostring(ent.direction) .. "," .. tostring(ent.orientation)) - end) -end - -if config.mined_entity then - Event.add(defines.events.on_player_mined_entity, function(e) - local player = game.players[e.player_index] - if Roles.player_has_flag(player, "deconlog-bypass") then - return - end - local ent = e.entity - add_log(get_secs() .. "," .. player.name .. ",mined_entity," .. ent.name .. "," .. pos_to_string(ent.position) .. "," .. tostring(ent.direction) .. "," .. tostring(ent.orientation)) - end) -end - -if config.fired_rocket or config.fired_explosive_rocket or config.fired_nuke then - Event.add(defines.events.on_player_ammo_inventory_changed, function(e) - local player = game.players[e.player_index] - if Roles.player_has_flag(player, "deconlog-bypass") then - return - end - if player.character then - local ammo_inv = player.get_inventory(defines.inventory.character_ammo) --- @cast ammo_inv -nil - local item = ammo_inv[player.character.selected_gun_index] - local action_name - if not item or not item.valid or not item.valid_for_read then - return - end - if config.fired_rocket and item.name == "rocket" then - action_name = ",shot-rocket," - elseif config.fired_explosive_rocket and item.name == "explosive-rocket" then - action_name = ",shot-explosive-rocket," - elseif config.fired_nuke and item.name == "atomic-bomb" then - action_name = ",shot-nuke," - else - return - end - add_log(get_secs() .. "," .. player.name .. action_name .. pos_to_string(player.physical_position) .. "," .. pos_to_string(player.shooting_state.position)) - end - end) -end diff --git a/exp_legacy/module/modules/addons/discord-alerts.lua b/exp_legacy/module/modules/addons/discord-alerts.lua deleted file mode 100644 index 9491c455..00000000 --- a/exp_legacy/module/modules/addons/discord-alerts.lua +++ /dev/null @@ -1,305 +0,0 @@ ---- Sends alert messages to our discord server when certain events are triggered --- @addon Discord-Alerts - -local ExpUtil = require("modules/exp_util") -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local Colors = require("modules/exp_util/include/color") -local config = require("modules.exp_legacy.config.discord_alerts") --- @dep config.discord_alerts -local write_json = ExpUtil.write_json - -local playtime_format = ExpUtil.format_time_factory{ format = "short", hours = true, minutes = true, seconds = true } -local emit_event_time_format = ExpUtil.format_time_factory{ format = "short", hours = true, minutes = true } - -local function append_playtime(player_name) - if not config.show_playtime then - return player_name - end - - local player = game.get_player(player_name) - - if not player then - return player_name - end - - return player.name .. " (" .. playtime_format(player.online_time) .. ")" -end - -local function get_player_name(event) - local player = game.players[event.player_index] - return player.name, event.by_player_name -end - -local function to_hex(color) - local hex_digits = "0123456789ABCDEF" - local function hex(bit) - local major, minor = math.modf(bit / 16) - major, minor = major + 1, minor * 16 + 1 - return hex_digits:sub(major, major) .. hex_digits:sub(minor, minor) - end - - return "0x" .. hex(color.r) .. hex(color.g) .. hex(color.b) -end - -local function emit_event(args) - local title = args.title or "" - local color = args.color or "0x0" --- @type string | Color - local description = args.description or "" - - if type(color) == "table" then - color = to_hex(color) - end - - local tick = args.tick or game.tick - local tick_formatted = emit_event_time_format(tick) - - local players_online = 0 - local admins_online = 0 - - for _, player in pairs(game.connected_players) do - players_online = players_online + 1 - - if player.admin then - admins_online = admins_online + 1 - end - end - - local done = { title = true, color = true, description = true } - local fields = { { - name = "Server Details", - value = string.format("Server: ${serverName} Time: %s\nTotal: %d Online: %d Admins: %d", tick_formatted, #game.players, players_online, admins_online), - } } - - for key, value in pairs(args) do - if not done[key] then - done[key] = true - local field = { - name = key, - value = value, - inline = false, - } - - local new_value, inline = value:gsub("", "", 1) - if inline > 0 then - field.value = new_value - field.inline = true - end - - table.insert(fields, field) - end - end - - write_json("ext/discord.out", { - title = title, - description = description, - color = color, - fields = fields, - }) -end - ---- Repeated protected entity mining -if config.entity_protection then - local EntityProtection = require("modules.exp_legacy.modules.control.protection") --- @dep modules.control.protection - Event.add(EntityProtection.events.on_repeat_violation, function(event) - local player_name = get_player_name(event) - emit_event{ - title = "Entity Protection", - description = "A player removed protected entities", - color = Colors.yellow, - ["Player"] = "" .. append_playtime(player_name), - ["Entity"] = "" .. event.entity.name, - ["Location"] = "X " .. event.entity.position.x .. " Y " .. event.entity.position.y, - } - end) -end - ---- Reports added and removed -if config.player_reports then - local Reports = require("modules.exp_legacy.modules.control.reports") --- @dep modules.control.reports - Event.add(Reports.events.on_player_reported, function(event) - local player_name, by_player_name = get_player_name(event) - emit_event{ - title = "Report", - description = "A player was reported", - color = Colors.yellow, - ["Player"] = "" .. append_playtime(player_name), - ["By"] = "" .. append_playtime(by_player_name), - ["Report Count"] = "" .. Reports.count_reports(player_name), - ["Reason"] = event.reason, - } - end) - Event.add(Reports.events.on_report_removed, function(event) - if event.batch ~= 1 then return end - local player_name = get_player_name(event) - emit_event{ - title = "Reports Removed", - description = "A player has a report removed", - color = Colors.green, - ["Player"] = "" .. player_name, - ["By"] = "" .. event.removed_by_name, - ["Report Count"] = "" .. event.batch_count, - } - end) -end - ---- Warnings added and removed -if config.player_warnings then - local Warnings = require("modules.exp_legacy.modules.control.warnings") --- @dep modules.control.warnings - Event.add(Warnings.events.on_warning_added, function(event) - local player_name, by_player_name = get_player_name(event) - local player = game.get_player(player_name) - emit_event{ - title = "Warning", - description = "A player has been given a warning", - color = Colors.yellow, - ["Player"] = "" .. player_name, - ["By"] = "" .. by_player_name, - ["Warning Count"] = "" .. Warnings.count_warnings(player), - ["Reason"] = event.reason, - } - end) - Event.add(Warnings.events.on_warning_removed, function(event) - if event.batch ~= 1 then return end - local player_name = get_player_name(event) - emit_event{ - title = "Warnings Removed", - description = "A player has a warning removed", - color = Colors.green, - ["Player"] = "" .. player_name, - ["By"] = "" .. event.removed_by_name, - ["Warning Count"] = "" .. event.batch_count, - } - end) -end - ---- When a player is jailed or unjailed -if config.player_jail then - local Jail = require("modules.exp_legacy.modules.control.jail") - Event.add(Jail.events.on_player_jailed, function(event) - local player_name, by_player_name = get_player_name(event) - emit_event{ - title = "Jail", - description = "A player has been jailed", - color = Colors.yellow, - ["Player"] = "" .. player_name, - ["By"] = "" .. by_player_name, - ["Reason"] = event.reason, - } - end) - Event.add(Jail.events.on_player_unjailed, function(event) - local player_name, by_player_name = get_player_name(event) - emit_event{ - title = "Unjail", - description = "A player has been unjailed", - color = Colors.green, - ["Player"] = "" .. player_name, - ["By"] = "" .. by_player_name, - } - end) -end - ---- Ban and unban -if config.player_bans then - Event.add(defines.events.on_player_banned, function(event) - if event.by_player then - local by_player = game.players[event.by_player] - emit_event{ - title = "Banned", - description = "A player has been banned", - color = Colors.red, - ["Player"] = "" .. event.player_name, - ["By"] = "" .. by_player.name, - ["Reason"] = event.reason, - } - end - end) - Event.add(defines.events.on_player_unbanned, function(event) - if event.by_player then - local by_player = game.players[event.by_player] - emit_event{ - title = "Un-Banned", - description = "A player has been un-banned", - color = Colors.green, - ["Player"] = "" .. event.player_name, - ["By"] = "" .. by_player.name, - } - end - end) -end - ---- Mute and unmute -if config.player_mutes then - Event.add(defines.events.on_player_muted, function(event) - local player_name = get_player_name(event) - emit_event{ - title = "Muted", - description = "A player has been muted", - color = Colors.yellow, - ["Player"] = "" .. player_name, - } - end) - Event.add(defines.events.on_player_unmuted, function(event) - local player_name = get_player_name(event) - emit_event{ - title = "Un-Muted", - description = "A player has been un-muted", - color = Colors.green, - ["Player"] = "" .. player_name, - } - end) -end - ---- Kick -if config.player_kicks then - Event.add(defines.events.on_player_kicked, function(event) - if event.by_player then - local player_name = get_player_name(event) - local by_player = game.players[event.by_player] - emit_event{ - title = "Kick", - description = "A player has been kicked", - color = Colors.orange, - ["Player"] = "" .. player_name, - ["By"] = "" .. by_player.name, - ["Reason"] = event.reason, - } - end - end) -end - ---- Promote and demote -if config.player_promotes then - Event.add(defines.events.on_player_promoted, function(event) - local player_name = get_player_name(event) - emit_event{ - title = "Promote", - description = "A player has been promoted", - color = Colors.green, - ["Player"] = "" .. player_name, - } - end) - Event.add(defines.events.on_player_demoted, function(event) - local player_name = get_player_name(event) - emit_event{ - title = "Demote", - description = "A player has been demoted", - color = Colors.yellow, - ["Player"] = "" .. player_name, - } - end) -end - ---- Other commands -Event.add(defines.events.on_console_command, function(event) - if event.player_index then - local player_name = get_player_name(event) - if config[event.command] then - emit_event{ - title = event.command:gsub("^%l", string.upper), - description = "/" .. event.command .. " was used", - color = Colors.grey, - ["By"] = "" .. player_name, - ["Details"] = event.parameters ~= "" and event.parameters or nil, - } - end - end -end) diff --git a/exp_legacy/module/modules/addons/fagc.lua b/exp_legacy/module/modules/addons/fagc.lua deleted file mode 100644 index da33cbd4..00000000 --- a/exp_legacy/module/modules/addons/fagc.lua +++ /dev/null @@ -1,21 +0,0 @@ ---- Allows the FAGC clientside bot to receive information about bans and unbans and propagate that information to other servers --- @addon FAGC - -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event - -local write_file = helpers.write_file - --- Clear the file on startup to minimize its size -Event.on_init(function() - write_file("fagc-actions.txt", "", false, 0) -end) - -Event.add(defines.events.on_player_banned, function(e) - local text = "ban;" .. e.player_name .. ";" .. (e.by_player or "") .. ";" .. (e.reason or "") .. "\n" - write_file("fagc-actions.txt", text, true, 0) -end) - -Event.add(defines.events.on_player_unbanned, function(e) - local text = "unban;" .. e.player_name .. ";" .. (e.by_player or "") .. ";" .. (e.reason or "") .. "\n" - write_file("fagc-actions.txt", text, true, 0) -end) diff --git a/exp_legacy/module/modules/addons/inserter.lua b/exp_legacy/module/modules/addons/inserter.lua deleted file mode 100644 index a9380051..00000000 --- a/exp_legacy/module/modules/addons/inserter.lua +++ /dev/null @@ -1,23 +0,0 @@ -local Event = require("modules/exp_legacy/utils/event") - -local controllers_with_inventory = { - [defines.controllers.character] = true, - [defines.controllers.god] = true, - [defines.controllers.editor] = true, -} - -Event.add(defines.events.on_player_mined_entity, function(event) - if (not event.entity.valid) or (event.entity.type ~= "inserter") or event.entity.drop_target then - return - end - - local item_entity = event.entity.surface.find_entity("item-on-ground", event.entity.drop_position) - - if item_entity then - local player = game.players[event.player_index] - - if controllers_with_inventory[player.controller_type] then - player.mine_entity(item_entity) - end - end -end) diff --git a/exp_legacy/module/modules/addons/inventory-clear.lua b/exp_legacy/module/modules/addons/inventory-clear.lua deleted file mode 100644 index 60c11e90..00000000 --- a/exp_legacy/module/modules/addons/inventory-clear.lua +++ /dev/null @@ -1,19 +0,0 @@ ---- Will move players items to spawn when they are banned or kicked, option to clear on leave --- @addon Inventory-Clear - -local ExpUtil = require("modules/exp_util") -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local events = require("modules.exp_legacy.config.inventory_clear") --- @dep config.inventory_clear - -local function clear_items(event) - local player = game.players[event.player_index] - local inventory = assert(player.get_main_inventory()) - ExpUtil.transfer_inventory_to_surface{ - inventory = inventory, - surface = game.surfaces[1], - name = "iron-chest", - allow_creation = true, - } -end - -for _, event_name in ipairs(events) do Event.add(event_name, clear_items) end diff --git a/exp_legacy/module/modules/addons/logging.lua b/exp_legacy/module/modules/addons/logging.lua deleted file mode 100644 index cf940d15..00000000 --- a/exp_legacy/module/modules/addons/logging.lua +++ /dev/null @@ -1,69 +0,0 @@ ---[[-- Addon Logging - @addon Logging -]] - -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local config = require("modules.exp_legacy.config.logging") --- @dep config.logging -local config_res = require("modules.exp_legacy.config.research") --- @dep config.research - -local write_file = helpers.write_file - -local function add_log(data) - write_file(config.file_name, data, true, 0) - write_file(config.file_name, "\n", true, 0) -end - -Event.add(defines.events.on_cargo_pod_finished_ascending, function(event) - if event and event.launched_by_rocket then - local force = event.cargo_pod.force - if force.rockets_launched >= config.rocket_launch_display_rate and force.rockets_launched % config.rocket_launch_display_rate == 0 then - add_log("[ROCKET] " .. force.rockets_launched .. " rockets launched") - elseif config.rocket_launch_display[force.rockets_launched] then - add_log("[ROCKET] " .. force.rockets_launched .. " rockets launched") - end - end -end) - -Event.add(defines.events.on_pre_player_died, function(event) - if event and event.player_index then - if event.cause then - if event.cause.type and event.cause.type == "character" and event.cause.player and event.cause.player.index then - add_log("[DEATH] " .. game.players[event.player_index].name .. " died because of " .. (game.players[event.cause.player.index].name or "unknown reason")) - else - add_log("[DEATH] " .. game.players[event.player_index].name .. " died because of " .. (event.cause.name or "unknown reason")) - end - else - add_log("[DEATH] " .. game.players[event.player_index].name .. " died because of unknown reason") - end - end -end) - -Event.add(defines.events.on_research_finished, function(event) - if event and event.research then - if event.by_script then - return - end - - if (event.research.level and config_res.inf_res[config_res.mod_set][event.research.name]) and (event.research.level >= config_res.inf_res[config_res.mod_set][event.research.name]) then - add_log{ "logging.add-l", event.research.prototype.localised_name, event.research.level - 1 } - else - add_log{ "logging.add-n", event.research.prototype.localised_name } - end - end -end) - -Event.add(defines.events.on_player_joined_game, function(event) - if event and event.player_index then - add_log("[JOIN] " .. game.players[event.player_index].name .. " joined the game") - end -end) - -Event.add(defines.events.on_player_left_game, function(event) - if event and event.player_index then - if event.reason then - add_log("[LEAVE] " .. game.players[event.player_index].name .. config.disconnect_reason[event.reason]) - else - add_log("[LEAVE] " .. game.players[event.player_index].name .. config.disconnect_reason[defines.disconnect_reason.quit]) - end - end -end) diff --git a/exp_legacy/module/modules/addons/miner.lua b/exp_legacy/module/modules/addons/miner.lua deleted file mode 100644 index 7dd8f29f..00000000 --- a/exp_legacy/module/modules/addons/miner.lua +++ /dev/null @@ -1,179 +0,0 @@ -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event_core -local Storage = require("modules/exp_util/storage") -local config = require("modules.exp_legacy.config.miner") --- @dep config.miner - -local miner_data = {} -Storage.register(miner_data, function(tbl) - miner_data = tbl -end) - -miner_data.queue = {} - -local function drop_target(entity) - if entity.drop_target then - return entity.drop_target - else - local entities = entity.surface.find_entities_filtered{ position = entity.drop_position } - - if #entities > 0 then - return entities[1] - else - return nil - end - end -end - -local function check_entity(entity) - if entity.to_be_deconstructed(entity.force) then - -- if it is already waiting to be deconstruct - return true - end - - local egcn = entity.get_wire_connectors() - - if egcn then - for k, _ in pairs(egcn) do - if k == defines.wire_connector_id.circuit_red or k == defines.wire_connector_id.circuit_green then - -- connected to circuit network - return true - end - end - end - - if not entity.minable then - -- if it is not minable - return true - end - - if not entity.prototype.selectable_in_game then - -- if it can select - return true - end - - if entity.has_flag("not-deconstructable") then - -- if it can deconstruct - return true - end - - return false -end - -local function chest_check(entity) - local target = drop_target(entity) - - if target == nil then - return - end - - if check_entity(entity) then - return - end - - if target.type ~= "logistic-container" and target.type ~= "container" then - -- not a chest - return - end - - local radius = 2 - local entities = target.surface.find_entities_filtered{ area = { { target.position.x - radius, target.position.y - radius }, { target.position.x + radius, target.position.y + radius } }, type = { "mining-drill", "inserter" } } - - for _, e in pairs(entities) do - if drop_target(e) == target then - if not e.to_be_deconstructed(entity.force) and e ~= entity then - return - end - end - end - - if check_entity(target) then - table.insert(miner_data.queue, { t = game.tick + 10, e = target }) - end -end - -local function miner_check(entity) - local ep = entity.position - local es = entity.surface - local ef = entity.force - local er = entity.prototype.mining_drill_radius - - for _, r in pairs(entity.surface.find_entities_filtered{ area = { { x = ep.x - er, y = ep.y - er }, { x = ep.x + er, y = ep.y + er } }, type = "resource" }) do - if r.amount > 0 then - return - end - end - - if check_entity(entity) then - return - end - - local pipe_build = {} - - if config.fluid and entity.fluidbox and #entity.fluidbox > 0 then - table.insert(pipe_build, { x = 0, y = 0 }) - local rh = math.ceil(er / 2) - 1 - local r = er + 1 - local entities = es.find_entities_filtered{ area = { { ep.x - r, ep.y - r }, { ep.x + r, ep.y + r } }, type = { "mining-drill", "pipe", "pipe-to-ground" } } - local entities_t = es.find_entities_filtered{ area = { { ep.x - r, ep.y - r }, { ep.x + r, ep.y + r } }, ghost_type = { "pipe", "pipe-to-ground" } } - table.insert_array(entities, entities_t) - - for _, e in pairs(entities) do - if (e.position.x > ep.x) and (e.position.y == ep.y) then - for h = 1, rh do - table.insert(pipe_build, { x = h, y = 0 }) - end - elseif (e.position.x < ep.x) and (e.position.y == ep.y) then - for h = 1, rh do - table.insert(pipe_build, { x = -h, y = 0 }) - end - elseif (e.position.x == ep.x) and (e.position.y > ep.y) then - for h = 1, rh do - table.insert(pipe_build, { x = 0, y = h }) - end - elseif (e.position.x == ep.x) and (e.position.y < ep.y) then - for h = 1, rh do - table.insert(pipe_build, { x = 0, y = -h }) - end - end - end - end - - if config.chest then - chest_check(entity) - end - - table.insert(miner_data.queue, { t = game.tick + 5, e = entity }) - - for _, pos in ipairs(pipe_build) do - es.create_entity{ name = "entity-ghost", position = { x = ep.x + pos.x, y = ep.y + pos.y }, force = ef, inner_name = "pipe" } - end -end - -Event.add(defines.events.on_resource_depleted, function(event) - if event.entity.prototype.infinite_resource then - return - end - - local resource = event.entity - local drills = resource.surface.find_entities_filtered{ area = { { event.entity.position.x - 1, event.entity.position.y - 1 }, { event.entity.position.x + 1, event.entity.position.y + 1 } }, type = "mining-drill" } - - for _, entity in pairs(drills) do - local radius = entity.prototype.mining_drill_radius - local dx = math.abs(entity.position.x - resource.position.x) - local dy = math.abs(entity.position.y - resource.position.y) - if dx <= radius and dy <= radius then - miner_check(entity) - end - end -end) - -Event.on_nth_tick(10, function(event) - for i = #miner_data.queue, 1, -1 do - local q = miner_data.queue[i] - if not q.e or not q.e.valid then - table.remove(miner_data.queue, i) - elseif event.tick >= q.t then - q.e.order_deconstruction(q.e.force) - table.remove(miner_data.queue, i) - end - end -end) diff --git a/exp_legacy/module/modules/addons/nukeprotect.lua b/exp_legacy/module/modules/addons/nukeprotect.lua deleted file mode 100644 index 59238290..00000000 --- a/exp_legacy/module/modules/addons/nukeprotect.lua +++ /dev/null @@ -1,45 +0,0 @@ ---- Disable new players from having certain items in their inventory, most commonly nukes --- @addon Nukeprotect - -local ExpUtil = require("modules/exp_util") -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles -local config = require("modules.exp_legacy.config.nukeprotect") --- @dep config.nukeprotect - ---- Check all items in the given inventory ----@param player LuaPlayer ----@param type defines.inventory -local function check_items(player, type) - -- if the player has perms to be ignored, then they should be - if config.ignore_permisison and Roles.player_allowed(player, config.ignore_permisison) then return end - -- if the players - if config.ignore_admins and player.admin then return end - - local items = {} --- @type LuaItemStack[] - local inventory = assert(player.get_inventory(type)) - for i = 1, #inventory do - local item = inventory[i] - if item.valid and item.valid_for_read and config[tostring(type)][item.name] then - player.print{ "nukeprotect.found", { "item-name." .. item.name } } - items[#items + 1] = item - end - end - - ExpUtil.move_items_to_surface{ - items = items, - surface = game.planets.nauvis.surface, - allow_creation = true, - name = "iron-chest", - } -end - -for _, inventory in ipairs(config.inventories) do - if #inventory.items > 0 then - Event.add(inventory.event, function(event) - local player = game.players[event.player_index] - if player and player.valid then - check_items(player, inventory.inventory) - end - end) - end -end diff --git a/exp_legacy/module/modules/addons/pollution-grading.lua b/exp_legacy/module/modules/addons/pollution-grading.lua deleted file mode 100644 index 3d76d459..00000000 --- a/exp_legacy/module/modules/addons/pollution-grading.lua +++ /dev/null @@ -1,16 +0,0 @@ ---- Makes polution look much nice of the map, ie not one big red mess --- @addon Pollution-Grading - -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local config = require("modules.exp_legacy.config.pollution_grading") --- @dep config.pollution_grading - -local delay = config.update_delay * 3600 -- convert from minutes to ticks -Event.on_nth_tick(delay, function() - local surface = game.surfaces[1] - local true_max = surface.get_pollution(config.reference_point) - local max = true_max * config.max_scalar - local min = max * config.min_scalar - local settings = game.map_settings.pollution - settings.expected_max_per_chunk = max - settings.min_to_show_per_chunk = min -end) diff --git a/exp_legacy/module/modules/addons/protection-jail.lua b/exp_legacy/module/modules/addons/protection-jail.lua deleted file mode 100644 index 079fd239..00000000 --- a/exp_legacy/module/modules/addons/protection-jail.lua +++ /dev/null @@ -1,41 +0,0 @@ ---- When a player triggers protection multiple times they are automatically jailed --- @addon protection-jail - -local ExpUtil = require("modules/exp_util") -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local Storage = require("modules/exp_util/storage") --- @dep utils.global -local Jail = require("modules.exp_legacy.modules.control.jail") --- @dep modules.control.jail -local Protection = require("modules.exp_legacy.modules.control.protection") --- @dep modules.control.protection -local format_player_name = ExpUtil.format_player_name_locale - ---- Stores how many times the repeat violation was triggered -local repeat_count = {} -Storage.register(repeat_count, function(tbl) - repeat_count = tbl -end) - ---- When a protection is triggered increment their counter and jail if needed -Event.add(Protection.events.on_repeat_violation, function(event) - local player = game.players[event.player_index] - - -- Increment the counter - if repeat_count[player.index] then - repeat_count[player.index] = repeat_count[player.index] + 1 - else - repeat_count[player.index] = 1 - end - - -- Jail if needed - if repeat_count[player.index] < 3 then - return - end - - local player_name_color = format_player_name(player) - Jail.jail_player(player, "", "Removed too many protected entities, please wait for a moderator.") - game.print{ "protection-jail.jail", player_name_color } -end) - ---- Clear the counter when they leave the game (stops a build up of data) -Event.add(defines.events.on_player_left_game, function(event) - repeat_count[event.player_index] = nil -end) diff --git a/exp_legacy/module/modules/addons/report-jail.lua b/exp_legacy/module/modules/addons/report-jail.lua deleted file mode 100644 index c86f6838..00000000 --- a/exp_legacy/module/modules/addons/report-jail.lua +++ /dev/null @@ -1,29 +0,0 @@ ---- When a player is reported, the player is automatically jailed if the combined playtime of the reporters exceeds the reported player --- @addon report-jail - -local ExpUtil = require("modules/exp_util") -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local Jail = require("modules.exp_legacy.modules.control.jail") --- @dep modules.control.jail -local Reports = require("modules.exp_legacy.modules.control.reports") --- @dep modules.control.reports -local format_player_name = ExpUtil.format_player_name_locale - ---- Returns the playtime of the reporter. Used when calculating the total playtime of all reporters -local function reporter_playtime(_, by_player_name, _) - local player = game.get_player(by_player_name) - if player == nil then - return 0 - end - return player.online_time -end - -Event.add(Reports.events.on_player_reported, function(event) - local player = game.players[event.player_index] - local total_playtime = Reports.count_reports(player, reporter_playtime) - - -- player less than 30 min - if (Reports.count_reports(player) > 1) and (total_playtime > math.max(player.online_time * 2, 108000)) then - local player_name_color = format_player_name(player) - Jail.jail_player(player, "", "Reported by too many players, please wait for a moderator.") - game.print{ "report-jail.jail", player_name_color } - end -end) diff --git a/exp_legacy/module/modules/addons/spawn-area.lua b/exp_legacy/module/modules/addons/spawn-area.lua deleted file mode 100644 index 8caeae56..00000000 --- a/exp_legacy/module/modules/addons/spawn-area.lua +++ /dev/null @@ -1,247 +0,0 @@ ---- Adds a custom spawn area with chests and afk turrets --- @addon Spawn-Area - -local Storage = require("modules/exp_util/storage") -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local config = require("modules.exp_legacy.config.spawn_area") --- @dep config.spawn_area - -local turrets = config.turrets.locations -Storage.register(turrets, function(tbl) - turrets = tbl -end) - --- Apply an offset to a LuaPosition -local function apply_offset(position, offset) - return { x = position.x + (offset.x or offset[1]), y = position.y + (offset.y or offset[2]) } -end - --- Apply the offset to the turrets default position -for _, turret in ipairs(turrets) do - turret.position = apply_offset(turret.position, config.turrets.offset) -end - --- Get or create the force used for entities in spawn -local function get_spawn_force() - local force = game.forces["spawn"] - - if force and force.valid then - return force - end - - force = game.create_force("spawn") - force.set_cease_fire("player", true) - -- force.set_friend('player', true) - game.forces["player"].set_cease_fire("spawn", true) - -- game.forces['player'].set_friend('spawn', true) - - return force -end - --- Protects an entity --- and sets its force to the spawn force -local function protect_entity(entity, set_force) - if entity and entity.valid then - entity.destructible = false - entity.minable = false - entity.rotatable = false - entity.operable = false - - if set_force then - entity.force = get_spawn_force() - end - end -end - --- Will spawn all infinite ammo turrets and keep them refilled -local function spawn_turrets() - for _, turret_pos in pairs(turrets) do - local surface = game.surfaces[turret_pos.surface] - local pos = turret_pos.position - local turret = surface.find_entity("gun-turret", pos) - - -- Makes a new turret if it is not found - if not turret or not turret.valid then - turret = surface.create_entity{ name = "gun-turret", position = pos, force = "spawn" } - if not turret then return end - protect_entity(turret) - end - - -- Adds ammo to the turret - local inv = turret.get_inventory(defines.inventory.turret_ammo) - if inv and inv.can_insert{ name = config.turrets.ammo_type, count = 10 } then - inv.insert{ name = config.turrets.ammo_type, count = 10 } - end - end -end - --- Makes a 2x2 afk belt at the locations in the config -local function spawn_belts(surface, position) - position = apply_offset(position, config.afk_belts.offset) - local belt_type = config.afk_belts.belt_type - -- 2 4 0 6 - local belt_details = { { -0.5, -0.5, defines.direction.east }, { 0.5, -0.5, defines.direction.south }, { -0.5, 0.5, defines.direction.north }, { 0.5, 0.5, defines.direction.west } } -- x, y, dir - - - for _, belt_set in pairs(config.afk_belts.locations) do - local set_position = apply_offset(position, belt_set) - for _, belt in pairs(belt_details) do - local pos = apply_offset(set_position, belt) - local belt_entity = surface.create_entity{ name = belt_type, position = pos, force = "neutral", direction = belt[3] } - - if config.afk_belts.protected then - protect_entity(belt_entity) - end - end - end -end - --- Generates extra tiles in a set pattern as defined in the config -local function spawn_pattern(surface, position) - position = apply_offset(position, config.pattern.offset) - local tiles_to_make = {} - local pattern_tile = config.pattern.pattern_tile - - for _, tile in pairs(config.pattern.locations) do - table.insert(tiles_to_make, { name = pattern_tile, position = apply_offset(position, tile) }) - end - - surface.set_tiles(tiles_to_make) -end - --- Generates extra water as defined in the config -local function spawn_water(surface, position) - position = apply_offset(position, config.water.offset) - local tiles_to_make = {} - local water_tile = config.water.water_tile - for _, tile in pairs(config.water.locations) do - table.insert(tiles_to_make, { name = water_tile, position = apply_offset(position, tile) }) - end - - surface.set_tiles(tiles_to_make) -end - --- Generates the entities that are in the config -local function spawn_entities(surface, position) - position = apply_offset(position, config.entities.offset) - for _, entity in pairs(config.entities.locations) do - local pos = apply_offset(position, { x = entity[2], y = entity[3] }) - entity = surface.create_entity{ name = entity[1], position = pos, force = "neutral" } - - if config.entities.protected then - protect_entity(entity) - end - - entity.operable = config.entities.operable - end -end - --- Generates an area with no water or entities, no water area is larger -local function spawn_area(surface, position) - local dr = config.spawn_area.deconstruction_radius - local tr = config.spawn_area.tile_radius - local tr2 = tr ^ 2 - local decon_tile = config.spawn_area.deconstruction_tile - - local fr = config.spawn_area.landfill_radius - local fr2 = fr ^ 2 - --- @diagnostic disable-next-line Incorrect Api Type: https://forums.factorio.com/viewtopic.php?f=233&t=109145&p=593761&hilit=get_tile#p593761 - local fill_tile = surface.get_tile(position).name - - -- Make sure a non water tile is used for each tile - --- @diagnostic disable-next-line Incorrect Api Type: https://forums.factorio.com/viewtopic.php?f=233&t=109145&p=593761&hilit=get_tile#p593761 - if surface.get_tile(position).collides_with("player") then fill_tile = "landfill" end - if decon_tile == nil then decon_tile = fill_tile end - - local tiles_to_make = {} - for x = -fr, fr do -- loop over x - local x2 = (x + 0.5) ^ 2 - for y = -fr, fr do -- loop over y - local y2 = (y + 0.5) ^ 2 - local dst = x2 + y2 - local pos = { x = position.x + x, y = position.y + y } - if dst < tr2 then - -- If it is inside the decon radius always set the tile - table.insert(tiles_to_make, { name = decon_tile, position = pos }) - --- @diagnostic disable-next-line Incorrect Api Type: https://forums.factorio.com/viewtopic.php?f=233&t=109145&p=593761&hilit=get_tile#p593761 - elseif dst < fr2 and surface.get_tile(pos).collides_with("player") then - -- If it is inside the fill radius only set the tile if it is water - table.insert(tiles_to_make, { name = fill_tile, position = pos }) - end - end - end - - -- Remove entities then set the tiles - local entities_to_remove = surface.find_entities_filtered{ position = position, radius = dr, name = "character", invert = true } - for _, entity in pairs(entities_to_remove) do - entity.destroy() - end - - surface.set_tiles(tiles_to_make) -end - -local function spawn_resource_tiles(surface) - for _, v in ipairs(config.resource_tiles.resources) do - if v.enabled then - for x = v.offset[1], v.offset[1] + v.size[1] do - for y = v.offset[2], v.offset[2] + v.size[2] do - surface.create_entity{ name = v.name, amount = v.amount, position = { x, y } } - end - end - end - end -end - -local function spawn_resource_patches(surface) - for _, v in ipairs(config.resource_patches.resources) do - if v.enabled then - for i = 1, v.num_patches do - surface.create_entity{ name = v.name, amount = v.amount, position = { v.offset[1] + v.offset_next[1] * (i - 1), v.offset[2] + v.offset_next[2] * (i - 1) } } - end - end - end -end - --- Only add a event handler if the turrets are enabled -if config.turrets.enabled then - Event.on_nth_tick(config.turrets.refill_time, function() - if game.tick < 10 then return end - if game.tick < (config.turrets.refill_time + 10) then - get_spawn_force() - end - spawn_turrets() - end) -end - -if config.resource_refill_nearby.enabled then - -- could have a flag in global that early returns if true, and reset it on_tick - Event.on_nth_tick(36000, function() - if game.tick < 10 then - return - end - - for _, ore in pairs(game.players[1].surface.find_entities_filtered{ position = game.players[1].force.get_spawn_position(game.players[1].surface), radius = config.resource_refill_nearby.range, name = config.resource_refill_nearby.resources_name }) do - ore.amount = ore.amount + math.random(config.resource_refill_nearby.amount[1], config.resource_refill_nearby.amount[2]) - end - end) -end - --- When the first player joins create the spawn area -Event.add(defines.events.on_player_created, function(event) - if event.player_index ~= 1 then return end - local player = game.players[event.player_index] - local p = { x = 0, y = 0 } - local s = player.physical_surface - get_spawn_force() - spawn_area(s, p) - if config.pattern.enabled then spawn_pattern(s, p) end - if config.water.enabled then spawn_water(s, p) end - if config.afk_belts.enabled then spawn_belts(s, p) end - if config.turrets.enabled then spawn_turrets() end - if config.entities.enabled then spawn_entities(s, p) end - if config.resource_tiles.enabled then spawn_resource_tiles(s) end - if config.resource_patches.enabled then spawn_resource_patches(s) end - player.teleport(p, s) -end) - --- Way to access global table -return turrets diff --git a/exp_legacy/module/modules/addons/station-auto-name.lua b/exp_legacy/module/modules/addons/station-auto-name.lua deleted file mode 100644 index dbc5ccce..00000000 --- a/exp_legacy/module/modules/addons/station-auto-name.lua +++ /dev/null @@ -1,93 +0,0 @@ ---- LuaPlayerBuiltEntityEventFilters ---- Events.set_event_filter(defines.events.on_built_entity, {{filter = "name", name = "fast-inserter"}}) -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local config = require("modules.exp_legacy.config.station_auto_name") --- @dep config.chat_reply - --- Credit to Cooldude2606 for using his lua magic to make this function. -local directions = { - ["W"] = -0.875, - ["NW"] = -0.625, - ["N"] = -0.375, - ["NE"] = -0.125, - ["E"] = 0.125, - ["SE"] = 0.375, - ["S"] = 0.625, - ["SW"] = 0.875, -} - ---- @param entity LuaEntity ---- @return string -local function get_direction(entity) - local angle = math.atan2(entity.position.y, entity.position.x) / math.pi - for direction, required_angle in pairs(directions) do - if angle < required_angle then - return direction - end - end - - return "W" -end - -local custom_string = " *" -local custom_string_len = #custom_string - ---- @param event EventData.on_built_entity | EventData.on_robot_built_entity -local function station_name_changer(event) - local entity = event.entity - local name = entity.name - if name == "entity-ghost" then - if entity.ghost_name ~= "train-stop" then return end - local backer_name = entity.backer_name - if backer_name ~= "" then - entity.backer_name = backer_name .. custom_string - end - elseif name == "train-stop" then -- only do the event if its a train stop - local backer_name = entity.backer_name or "" - if backer_name:sub(-custom_string_len) == custom_string then - entity.backer_name = backer_name:sub(1, -custom_string_len - 1) - return - end - - local bounding_box = entity.bounding_box - -- expanded box for recourse search: - local bounding2 = { { bounding_box.left_top.x - 100, bounding_box.left_top.y - 100 }, { bounding_box.right_bottom.x + 100, bounding_box.right_bottom.y + 100 } } - -- gets all resources in bounding_box2: - local resources = game.surfaces[1].find_entities_filtered{ area = bounding2, type = "resource" } - if #resources > 0 then -- save cpu time if their are no resources in bounding_box2 - local closest_distance - local px, py = bounding_box.left_top.x, bounding_box.left_top.y - local recourse_closed - - -- Check which recourse is closest - for i, item in ipairs(resources) do - local dx, dy = px - item.bounding_box.left_top.x, py - item.bounding_box.left_top.y - local distance = (dx * dx) + (dy * dy) - if not closest_distance or distance < closest_distance then - recourse_closed = item - closest_distance = distance - end - end - - local item_name = recourse_closed.name - if item_name then -- prevent errors if something went wrong - local item_name2 = item_name:gsub("^%l", string.upper):gsub("-", " ") -- removing the - and making first letter capital - - local item_type = "item" - if item_name == "crude-oil" then - item_type = "fluid" - end - - entity.backer_name = config.station_name:gsub("__icon__", "[img=" .. item_type .. "." .. item_name .. "]") - :gsub("__item_name__", item_name2) - :gsub("__backer_name__", entity.backer_name) - :gsub("__direction__", get_direction(entity)) - :gsub("__x__", math.floor(entity.position.x)) - :gsub("__y__", math.floor(entity.position.y)) - end - end - end -end - --- Add handler to robot and player build entities -Event.add(defines.events.on_built_entity, station_name_changer) -Event.add(defines.events.on_robot_built_entity, station_name_changer) diff --git a/exp_legacy/module/modules/addons/tree-decon.lua b/exp_legacy/module/modules/addons/tree-decon.lua deleted file mode 100644 index c6d33d9f..00000000 --- a/exp_legacy/module/modules/addons/tree-decon.lua +++ /dev/null @@ -1,140 +0,0 @@ ---- Makes trees which are marked for decon "decay" quickly to allow faster building --- @addon Tree-Decon - -local Storage = require("modules/exp_util/storage") -local Gui = require("modules/exp_gui") - -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles -local PlayerData = require("modules.exp_legacy.expcore.player_data") --- @dep expcore.player_data - --- Storage queue used to store trees that need to be removed, also cache for player roles -local cache = {} -local tree_queue = { _head = 0 } -Storage.register({ tree_queue, cache }, function(tbl) - tree_queue = tbl[1] - cache = tbl[2] -end) - -local function get_permission(player_index) - if cache[player_index] == nil then - local player = game.players[player_index] - if Roles.player_allowed(player, "fast-tree-decon") then - cache[player_index] = "fast" - elseif Roles.player_allowed(player, "standard-decon") then - cache[player_index] = "standard" - else - cache[player_index] = player.force - end - end - - return cache[player_index] -end - --- Left menu button to toggle between fast decon and normal decon marking -local HasEnabledDecon = PlayerData.Settings:combine("HasEnabledDecon") -HasEnabledDecon:set_default(false) - -Gui.toolbar.create_button{ - name = "toggle-tree-decon", - sprite = "entity/tree-01", - tooltip = { "tree-decon.main-tooltip" }, - auto_toggle = true, - visible = function(player, _) - return Roles.player_allowed(player, "fast-tree-decon") - end -}:on_click(function(def, player, element) - local state = Gui.toolbar.get_button_toggled_state(def, player) - HasEnabledDecon:set(player, state) - player.print{ "tree-decon.toggle-msg", state and { "tree-decon.enabled" } or { "tree-decon.disabled" } } -end) - --- Add trees to queue when marked, only allows simple entities and for players with role permission -Event.add(defines.events.on_marked_for_deconstruction, function(event) - -- Check which type of decon a player is allowed - local index = event.player_index - if not index then return end - - -- Check what should happen to this entity - local entity = event.entity - if not entity or not entity.valid then return end - - -- Not allowed to decon this entity - local last_user = entity.last_user - local allow = get_permission(index) - if last_user and allow ~= "standard" and allow ~= "fast" then - entity.cancel_deconstruction(allow) - return - end - - -- Allowed to decon this entity, but not fast - if allow ~= "fast" then return end - - local player = game.get_player(index) - if not HasEnabledDecon:get(player) then return end - - -- Allowed fast decon on this entity, just trees - local head = tree_queue._head + 1 - if not last_user and entity.type ~= "cliff" then - tree_queue[head] = entity - tree_queue._head = head - end -end) - --- Remove trees at random till the queue is empty -Event.add(defines.events.on_tick, function() - local head = tree_queue._head - if head == 0 then return end - - local max_remove = math.floor(head / 100) + 1 - local remove_count = math.random(0, max_remove) - while remove_count > 0 and head > 0 do - local remove_index = math.random(1, head) - local entity = tree_queue[remove_index] - tree_queue[remove_index] = tree_queue[head] - head = head - 1 - if entity and entity.valid then - remove_count = remove_count - 1 - entity.destroy() - end - end - - tree_queue._head = head -end) - --- Clear the cache -Event.on_nth_tick(300, function() - for key, _ in pairs(cache) do - cache[key] = nil - end -end) - --- Clear trees when hit with a car ---- @param event EventData.on_entity_damaged -Event.add(defines.events.on_entity_damaged, function(event) - if not (event.damage_type.name == "impact" and event.force) then - return - end - - if not (event.entity.type == "tree" or event.entity.type == "simple-entity") then - return - end - - if (not event.cause) or (event.cause.type ~= "car") then - return - end - - local driver = event.cause.get_driver() - if not driver then return end - if driver.object_name ~= "LuaPlayer" then - driver = driver.player - if not driver then return end - end - - local allow = get_permission(driver.index) - if allow == "fast" and HasEnabledDecon:get(driver) then - event.entity.destroy() - else - event.entity.order_deconstruction(event.force, driver) - end -end) diff --git a/exp_scenario/module/commands/_types.lua b/exp_scenario/module/commands/_types.lua index 2b042835..0acfeb82 100644 --- a/exp_scenario/module/commands/_types.lua +++ b/exp_scenario/module/commands/_types.lua @@ -31,7 +31,8 @@ types.lower_role = --- @cast result any TODO role is not a defined type local player_highest = highest_role(player) - if player_highest.index >= result.index then + local is_root = Roles.config.internal.root == player_highest.name + if not is_root and player_highest.index >= result.index then return invalid{ "exp-commands-parse_role.lower-role" } else return valid(result) @@ -47,7 +48,8 @@ types.lower_role_player = local other_highest = highest_role(result) local player_highest = highest_role(player) - if player_highest.index >= other_highest.index then + local is_root = Roles.config.internal.root == player_highest.name + if not is_root and player_highest.index >= other_highest.index then return invalid{ "exp-commands-parse_role.lower-role-player" } else return valid(result) @@ -63,7 +65,8 @@ types.lower_role_player_online = local other_highest = highest_role(result) local player_highest = highest_role(player) - if player_highest.index >= other_highest.index then + local is_root = Roles.config.internal.root == player_highest.name + if not is_root and player_highest.index >= other_highest.index then return invalid{ "exp-commands-parse_role.lower-role-player" } else return valid(result) @@ -79,7 +82,8 @@ types.lower_role_player_alive = local other_highest = highest_role(result) local player_highest = highest_role(player) - if player_highest.index >= other_highest.index then + local is_root = Roles.config.internal.root == player_highest.name + if not is_root and player_highest.index >= other_highest.index then return invalid{ "exp-commands-parse_role.lower-role-player" } else return valid(result) diff --git a/exp_scenario/module/control.lua b/exp_scenario/module/control.lua index 8c24e0f6..62427829 100644 --- a/exp_scenario/module/control.lua +++ b/exp_scenario/module/control.lua @@ -43,8 +43,29 @@ require("modules/exp_scenario/commands/warnings") require("modules/exp_scenario/commands/waterfill") --- Control +add(require("modules/exp_scenario/control/afk_kick")) add(require("modules/exp_scenario/control/bonus")) +add(require("modules/exp_scenario/control/chat_auto_reply")) +add(require("modules/exp_scenario/control/chat_popup")) +add(require("modules/exp_scenario/control/custom_start")) +add(require("modules/exp_scenario/control/damage_popups")) +add(require("modules/exp_scenario/control/death_markers")) +add(require("modules/exp_scenario/control/deconstruction_log")) +add(require("modules/exp_scenario/control/degrading_tiles")) +add(require("modules/exp_scenario/control/discord_alerts")) +add(require("modules/exp_scenario/control/extra_logging")) +add(require("modules/exp_scenario/control/fast_deconstruction")) +add(require("modules/exp_scenario/control/help_bubbles")) +add(require("modules/exp_scenario/control/inserter_pickup")) +add(require("modules/exp_scenario/control/inventory_clear")) +add(require("modules/exp_scenario/control/mine_depletion")) +add(require("modules/exp_scenario/control/nuke_protection")) +add(require("modules/exp_scenario/control/pollution_grading")) +add(require("modules/exp_scenario/control/protection_jail")) +add(require("modules/exp_scenario/control/report_jail")) add(require("modules/exp_scenario/control/research")) +add(require("modules/exp_scenario/control/spawn_area")) +add(require("modules/exp_scenario/control/station_auto_name")) --- Guis add(require("modules/exp_scenario/gui/autofill")) diff --git a/exp_scenario/module/control/afk_kick.lua b/exp_scenario/module/control/afk_kick.lua new file mode 100644 index 00000000..24f9b378 --- /dev/null +++ b/exp_scenario/module/control/afk_kick.lua @@ -0,0 +1,84 @@ +--[[-- Control -- AFK Kick +Kicks players when all players on the server are afk +]] + +local Async = require("modules/exp_util/async") +local Storage = require("modules/exp_util/storage") +local config = require("modules.exp_legacy.config.afk_kick") + +--- @type { last_active: number } +local script_data = { last_active = 0 } +Storage.register(script_data, function(tbl) + script_data = tbl +end) + +--- Kicks an afk player, used to add a delay so the gui has time to appear +local afk_kick_player_async = + Async.register(function(player) + if game.tick - script_data.last_active < config.kick_time then return end + game.kick_player(player, "AFK while no active players on the server") + end) + +--- Check if there is an active player +local function has_active_player() + for _, player in ipairs(game.connected_players) do + if player.afk_time < config.afk_time + or config.admin_as_active and player.admin + or config.trust_as_active and player.online_time > config.trust_time + or config.custom_active_check and config.custom_active_check(player) then + script_data.last_active = game.tick + return true + end + end + + return false +end + +--- Check for an active player every update_time number of ticks +local function check_afk_players() + -- Check for active players + if has_active_player() then return end + + -- Check if players should be kicked + if game.tick - script_data.last_active < config.kick_time then return end + + -- Kick time exceeded, kick all players + for _, player in ipairs(game.connected_players) do + -- Add a frame to say why the player was kicked + local frame = player.gui.screen.add{ + type = "frame", + name = "afk-kick", + caption = { "exp_afk-kick.kick-message" }, + } + + local uis = player.display_scale + local res = player.display_resolution + frame.location = { + x = res.width * (0.5 - 0.11 * uis), + y = res.height * (0.5 - 0.14 * uis), + } + + -- Kick the player, some delay needed allow the gui to show + afk_kick_player_async:start_after(60, player) + end +end + +--- Remove the screen gui if it is present +--- @param event EventData.on_player_joined_game +local function on_player_joined_game(event) + local player = assert(game.get_player(event.player_index)) + local frame = player.gui.screen["afk-kick"] + if frame and frame.valid then frame.destroy() end +end + +local e = defines.events + +return { + events = { + [e.on_player_joined_game] = on_player_joined_game, + }, + on_nth_tick = { + [config.update_time] = check_afk_players, + }, + has_active_player = has_active_player, +} diff --git a/exp_scenario/module/control/chat_auto_reply.lua b/exp_scenario/module/control/chat_auto_reply.lua new file mode 100644 index 00000000..8d5ccc8f --- /dev/null +++ b/exp_scenario/module/control/chat_auto_reply.lua @@ -0,0 +1,63 @@ +--[[-- Control - Chat Auto Reply +Adds auto replies to chat messages, as well as chat commands +]] + +local Roles = require("modules.exp_legacy.expcore.roles") +local config = require("modules.exp_legacy.config.chat_reply") +local prefix = config.command_prefix +local prefix_len = string.len(prefix) + +local find = string.find +local sub = string.sub + +--- Check if a message has any trigger words +--- @param event EventData.on_console_chat +local function on_console_chat(event) + if not event.player_index then return end + local player = assert(game.get_player(event.player_index)) + local message = event.message:lower():gsub("%s+", "") + + -- Check if the player can chat commands + local commands_allowed = true + if config.command_admin_only and not player.admin then commands_allowed = false end + if config.command_permission and not Roles.player_allowed(player, config.command_permission) then commands_allowed = false end + + -- Check if a key word appears in the message + for key_word, reply in pairs(config.messages) do + local start_pos = find(message, key_word) + if start_pos then + local is_command = sub(message, start_pos - prefix_len - 1, start_pos - 1) == prefix + if type(reply) == "function" then + reply = reply(player, is_command) + end + + if is_command and commands_allowed then + game.print{ "exp_chat-auto-reply.chat-reply", reply } + elseif is_command then + player.print{ "exp_chat-auto-reply.chat-disallowed" } + elseif not commands_allowed then + player.print{ "exp_chat-auto-reply.chat-reply", reply } + end + end + end + + if not commands_allowed then return end + + -- Check if a command appears in the message + for key_word, reply in pairs(config.commands) do + if find(message, prefix .. key_word) then + if type(reply) == "function" then + reply = reply(player, true) + end + game.print{ "exp_chat-auto-reply.chat-reply", reply } + end + end +end + +local e = defines.events + +return { + events = { + [e.on_console_chat] = on_console_chat, + } +} diff --git a/exp_scenario/module/control/chat_popup.lua b/exp_scenario/module/control/chat_popup.lua new file mode 100644 index 00000000..a858acf3 --- /dev/null +++ b/exp_scenario/module/control/chat_popup.lua @@ -0,0 +1,48 @@ +--[[-- Control - Chat Popup +Creates flying text entities when a player sends a message in chat +]] + +local FlyingText = require("modules/exp_util/flying_text") +local config = require("modules.exp_legacy.config.popup_messages") + +local lower = string.lower +local find = string.find + +--- Create a chat bubble when a player types a message +--- @param event EventData.on_console_chat +local function on_console_chat(event) + if not event.player_index then return end + local player = assert(game.get_player(event.player_index)) + local name = player.name + + -- Sends the message as text above them + if config.show_player_messages then + FlyingText.create_as_player{ + target_player = player, + text = { "exp_chat-popup.flying-text-message", name, event.message }, + } + end + + if not config.show_player_mentions then return end + + -- Loops over online players to see if they name is included + local search_string = lower(event.message) + for _, mentioned_player in ipairs(game.connected_players) do + if mentioned_player.index ~= player.index then + if find(search_string, lower(mentioned_player.name), 1, true) then + FlyingText.create_as_player{ + target_player = mentioned_player, + text = { "exp_chat-popup.flying-text-ping", name }, + } + end + end + end +end + +local e = defines.events + +return { + events = { + [e.on_console_chat] = on_console_chat, + } +} diff --git a/exp_scenario/module/control/custom_start.lua b/exp_scenario/module/control/custom_start.lua new file mode 100644 index 00000000..1c4e64ff --- /dev/null +++ b/exp_scenario/module/control/custom_start.lua @@ -0,0 +1,64 @@ +--[[-- Control - Custom Start +Changes the starting script and the items given on first join depending on factory production levels +]] + +local config = require("modules.exp_legacy.config.advanced_start") +local floor = math.floor + +--- Give a player their starting items +--- @param player LuaPlayer +local function give_starting_items(player) + local get_prod_stats = player.force.get_item_production_statistics(player.physical_surface) + local get_input_count = get_prod_stats.get_input_count + local insert_param = { name = "", count = 0 } + local insert = player.insert + for item_name, insert_amount in pairs(config.items) do + insert_param.name = item_name + if type(insert_amount) == "function" then + local count = insert_amount(get_input_count(item_name), get_input_count, player) + if count >= 1 then + insert_param.count = floor(count) + insert(insert_param) + end + elseif insert_amount >= 1 then + insert_param.count = floor(insert_amount) + insert(insert_param) + end + end +end + +--- Calls remote interfaces to configure the base game scenarios +local function on_init() + game.forces.player.friendly_fire = config.friendly_fire + game.map_settings.enemy_expansion.enabled = config.enemy_expansion + if remote.interfaces["freeplay"] then + remote.call("freeplay", "set_created_items", {}) + remote.call("freeplay", "set_disable_crashsite", config.disable_crashsite) + remote.call("freeplay", "set_skip_intro", config.skip_intro) + remote.call("freeplay", "set_chart_distance", config.chart_radius) + end + if remote.interfaces["silo_script"] then + remote.call("silo_script", "set_no_victory", config.skip_victory) + end + if remote.interfaces["space_finish_script"] then + remote.call("space_finish_script", "set_no_victory", config.skip_victory) + end +end + +--- Give a player starting items when they first join +--- @param event EventData.on_player_created +local function on_player_created(event) + -- We can't trust on_init to work for clusterio + if event.player_index == 1 then on_init() end + give_starting_items(assert(game.get_player(event.player_index))) +end + +local e = defines.events + +return { + on_init = on_init, + events = { + [e.on_player_created] = on_player_created, + }, + give_starting_items = give_starting_items, +} diff --git a/exp_scenario/module/control/damage_popups.lua b/exp_scenario/module/control/damage_popups.lua new file mode 100644 index 00000000..54dfc0e1 --- /dev/null +++ b/exp_scenario/module/control/damage_popups.lua @@ -0,0 +1,50 @@ +--[[-- Control - Damage PopUps +Displays the amount of dmg that is done by players to entities; +also shows player health when a player is attacked +]] + +local FlyingText = require("modules/exp_util/flying_text") +local config = require("modules.exp_legacy.config.popup_messages") + +local random = math.random +local floor = math.floor +local max = math.max + +--- Called when entity entity is damaged including the player character +--- @param event EventData.on_entity_damaged +local function on_entity_damaged(event) + local message + local cause = event.cause + local entity = event.entity + + -- Check which message to display + if config.show_player_health and entity.name == "character" then + message = { "exp_damage-popup.flying-text-health", floor(entity.health) } + elseif config.show_player_damage and entity.name ~= "character" and cause and cause.name == "character" then + message = { "exp_damage-popup.flying-text-damage", floor(event.original_damage_amount) } + end + + -- Outputs the message as floating text + if message then + local entity_radius = max(1, entity.get_radius()) + local offset = (random() - 0.5) * entity_radius * config.damage_location_variance + local position = { x = entity.position.x + offset, y = entity.position.y - entity_radius } + + local health_percentage = entity.get_health_ratio() + local color = { r = 1 - health_percentage, g = health_percentage, b = 0 } + + FlyingText.create{ + text = message, + position = position, + color = color, + } + end +end + +local e = defines.events + +return { + events = { + [e.on_entity_damaged] = on_entity_damaged, + }, +} diff --git a/exp_scenario/module/control/death_markers.lua b/exp_scenario/module/control/death_markers.lua new file mode 100644 index 00000000..efb25694 --- /dev/null +++ b/exp_scenario/module/control/death_markers.lua @@ -0,0 +1,157 @@ +--[[-- Control - Death Markers +Makes markers on the map where places have died and reclaims items if not recovered +]] + +local ExpUtil = require("modules/exp_util") +local Storage = require("modules/exp_util/storage") +local config = require("modules.exp_legacy.config.death_logger") + +local map_tag_time_format = ExpUtil.format_time_factory{ format = "short", hours = true, minutes = true } + +--- @class CorpseData +--- @field player LuaPlayer +--- @field corpse LuaEntity +--- @field tag LuaCustomChartTag? +--- @field created_at number + +--- @type table +local character_corpses = {} +Storage.register(character_corpses, function(tbl) + character_corpses = tbl +end) + +--- Creates a new death marker and saves it to the given death +--- @param corpse_data CorpseData +local function create_map_tag(corpse_data) + local player = corpse_data.player + local message = player.name .. " died" + + if config.include_time_of_death then + local time = map_tag_time_format(corpse_data.created_at) + message = message .. " at " .. time + end + + corpse_data.tag = player.force.add_chart_tag(corpse_data.corpse.surface, { + position = corpse_data.corpse.position, + icon = config.map_icon, + text = message, + }) +end + +--- Checks that all map tags are present and valid, creating any that are missing +local function check_map_tags() + for _, corpse_data in pairs(character_corpses) do + if not corpse_data.tag or not corpse_data.tag.valid then + create_map_tag(corpse_data) + end + end +end + +-- when a player dies a new death is added to the records and a map marker is made +--- @param event EventData.on_player_died +local function on_player_died(event) + local player = assert(game.get_player(event.player_index)) + local corpse = player.surface.find_entity("character-corpse", player.physical_position) + if not corpse or not corpse.valid then return end + + local corpse_data = { + player = player, + corpse = corpse, + created_at = event.tick, + } + + local registration_number = script.register_on_object_destroyed(corpse) + character_corpses[registration_number] = corpse_data + + -- Create a map marker + if config.show_map_markers then + create_map_tag(corpse_data) + end + + -- Draw a light attached to the corpse with the player color + if config.show_light_at_corpse then + rendering.draw_light{ + sprite = "utility/light_medium", + surface = player.surface, + color = player.color, + force = player.force, + target = corpse, + } + end +end + +--- Called to remove stale corpse data +--- @param event EventData.on_object_destroyed +local function on_object_destroyed(event) + local corpse_data = character_corpses[event.registration_number] + character_corpses[event.registration_number] = nil + if not corpse_data then + return + end + + local tag = corpse_data.tag + if tag and config.clean_map_markers then + tag.destroy() + end +end + +--- Draw lines to the player corpse +--- @param event EventData.on_player_respawned +local function on_player_respawned(event) + local index = event.player_index + local player = assert(game.get_player(index)) + for _, corpse_data in pairs(character_corpses) do + if corpse_data.player.index == index then + local line_color = player.color + line_color.a = .3 + rendering.draw_line{ + color = line_color, + from = player.character, + to = corpse_data.corpse, + surface = player.surface, + players = { index }, + draw_on_ground = true, + dash_length = 1, + gap_length = 1, + width = 2, + } + end + end +end + +--- Collect all items from expired character corpses +--- @param event EventData.on_character_corpse_expired +local function on_character_corpse_expired(event) + local corpse = event.corpse + local inventory = assert(corpse.get_inventory(defines.inventory.character_corpse)) + ExpUtil.transfer_inventory_to_surface{ + inventory = inventory, + surface = corpse.surface, + name = "iron-chest", + allow_creation = true, + } +end + +local on_nth_tick = {} +if config.show_map_markers then + on_nth_tick[config.period_check_map_tags] = check_map_tags +end + +local e = defines.events +local events = { + [e.on_player_died] = on_player_died, + [e.on_object_destroyed] = on_object_destroyed, +} + +if config.show_line_to_corpse then + events[e.on_player_respawned] = on_player_respawned +end + +if config.collect_corpses then + events[e.on_character_corpse_expired] = on_character_corpse_expired +end + +return { + on_nth_tick = on_nth_tick, + events = events, +} diff --git a/exp_scenario/module/control/deconstruction_log.lua b/exp_scenario/module/control/deconstruction_log.lua new file mode 100644 index 00000000..087030ab --- /dev/null +++ b/exp_scenario/module/control/deconstruction_log.lua @@ -0,0 +1,181 @@ +--[[-- Control - Deconstruction Log +Log certain actions into a file when events are triggered +]] + +local ExpUtil = require("modules/exp_util") +local Roles = require("modules.exp_legacy.expcore.roles") +local config = require("modules.exp_legacy.config.deconlog") + +local seconds_time_format = ExpUtil.format_time_factory{ format = "short", hours = true, minutes = true, seconds = true } +local format_number = require("util").format_number +local write_file = helpers.write_file +local format_string = string.format +local concat = table.concat + +local filepath = "log/deconstruction.log" + +--- Clear the log file +local function clear_log() + helpers.remove_path(filepath) +end + +--- Add a new line to the log +--- @param player LuaPlayer +--- @param action string +--- @param ... string +local function add_log_line(player, action, ...) + local text = concat({ + seconds_time_format(game.tick), + player.name, + action, + ... + }, ",") + + write_file(filepath, text .. "\n", true, 0) +end + +--- Convert a position to a string +--- @param pos MapPosition +--- @return string +local function format_position(pos) + return format_string("%.1f,%.1f", pos.x, pos.y) +end + +--- Convert an area to a string +--- @param area BoundingBox +--- @return string +local function format_area(area) + return format_string("%.1f,%.1f,%.1f,%.1f", area.left_top.x, area.left_top.y, area.right_bottom.x, area.right_bottom.y) +end + +--- Convert an entity to a string +--- @param entity LuaEntity +--- @return string +local function format_entity(entity) + return format_string("%s,%.1f,%.1f,%s,%s", entity.name, entity.position.x, entity.position.y, entity.direction, entity.orientation) +end + +--- Concert a position into a gps tag +--- @param pos MapPosition +--- @param surface_name string +--- @return string +local function format_position_gps(pos, surface_name) + return format_string("[gps=%.1f,%.1f,%s]", pos.x, pos.y, surface_name) +end + +--- Print a message to all players who match the value of admin +--- @param message LocalisedString +local function admin_print(message) + for _, player in ipairs(game.connected_players) do + if player.admin then + player.print(message) + end + end +end + +--- Check if a log should be created for a player +--- @param event { player_index: number } +--- @return LuaPlayer? +local function get_log_player(event) + local player = assert(game.get_player(event.player_index)) + + if Roles.player_has_flag(player, "deconlog-bypass") then + return nil + end + + return player +end + +--- Log when an area is deconstructed +--- @param event EventData.on_player_deconstructed_area +local function on_player_deconstructed_area(event) + local player = get_log_player(event) + if not player then return end + + --- Don't log when a player clears a deconstruction + if event.alt then + return + end + + local area = event.area + local surface_name = event.surface.name + local items = event.surface.find_entities_filtered{ area = area, force = player.force } + + if #items > 250 then + admin_print{ + "exp_deconstruction-log.chat-admin", + player.name, + format_position_gps(area.left_top, surface_name), + format_position_gps(area.right_bottom, surface_name), + format_number(#items, false), + } + end + + add_log_line(player, "deconstructed_area", surface_name, format_area(area)) +end + +--- Log when an entity is built +--- @param event EventData.on_built_entity +local function on_built_entity(event) + local player = get_log_player(event) + if not player then return end + add_log_line(player, "built_entity", format_entity(event.entity)) +end + +--- Log when an entity is mined +--- @param event EventData.on_player_mined_entity +local function on_player_mined_entity(event) + local player = get_log_player(event) + if not player then return end + add_log_line(player, "mined_entity", format_entity(event.entity)) +end + +--- Log when rocket is fired +--- @param event EventData.on_player_ammo_inventory_changed +local function on_player_ammo_inventory_changed(event) + local player = get_log_player(event) + if not player or not player.character then return end + + local character_ammo = assert(player.get_inventory(defines.inventory.character_ammo)) + local item = character_ammo[player.character.selected_gun_index] + if not item or not item.valid or not item.valid_for_read then + return + end + + local action_name = "shot-" .. item.name + if not config.fired_rocket and action_name == "shot-rocket" then + return + elseif not config.fired_explosive_rocket and action_name == "shot-explosive-rocket" then + return + elseif not config.fired_nuke and action_name == "shot-atomic-bomb" then + return + end + + add_log_line(player, action_name, format_position(player.physical_position), format_position(player.shooting_state.position)) +end + + +local e = defines.events +local events = { + [e.on_multiplayer_init] = clear_log, +} + +if config.decon_area then + events[e.on_player_deconstructed_area] = on_player_deconstructed_area +end + +if config.built_entity then + events[e.on_built_entity] = on_built_entity +end + +if config.mined_entity then + events[e.on_player_mined_entity] = on_player_mined_entity +end + +if config.fired_rocket or config.fired_explosive_rocket or config.fired_nuke then + events[e.on_player_ammo_inventory_changed] = on_player_ammo_inventory_changed +end + +return { + events = events, +} diff --git a/exp_legacy/module/modules/addons/scorched-earth.lua b/exp_scenario/module/control/degrading_tiles.lua similarity index 50% rename from exp_legacy/module/modules/addons/scorched-earth.lua rename to exp_scenario/module/control/degrading_tiles.lua index f71881f6..ad16dbfd 100644 --- a/exp_legacy/module/modules/addons/scorched-earth.lua +++ b/exp_scenario/module/control/degrading_tiles.lua @@ -1,10 +1,12 @@ ---- When a player walks around the tiles under them will degrade over time, the same is true when entites are built --- @addon Scorched-Earth +--[[-- Control - Degrading Tiles +When a player walks around the tiles under them will degrade over time, the same is true when entites are built +]] -local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event -local config = require("modules.exp_legacy.config.scorched_earth") --- @dep config.scorched_earth +local config = require("modules.exp_legacy.config.scorched_earth") --- Loops over the config and finds the wile which has the highest value for strength +local random = math.random + +--- Get the max tile strength local max_strength = 0 for _, strength in pairs(config.strengths) do if strength > max_strength then @@ -12,8 +14,10 @@ for _, strength in pairs(config.strengths) do end end --- Will degrade a tile down to the next tile when called -local function degrade(surface, position) +--- Replace a tile with the next tile in the degrade chain +--- @param surface LuaSurface +--- @param position MapPosition +local function degrade_tile(surface, position) --- @diagnostic disable-next-line Incorrect Api Type: https://forums.factorio.com/viewtopic.php?f=233&t=109145&p=593761&hilit=get_tile#p593761 local tile = surface.get_tile(position) local tile_name = tile.name @@ -22,49 +26,50 @@ local function degrade(surface, position) surface.set_tiles{ { name = degrade_tile_name, position = position } } end --- Same as degrade but will degrade all tiles that are under an entity +--- Replace all titles under an entity with the next tile in the degrade chain +--- @param entity LuaEntity local function degrade_entity(entity) - local surface = entity.surface - local position = entity.position - local tiles = {} if not config.entities[entity.name] then return end - local box = entity.prototype.collision_box - local lt = box.left_top - local rb = box.right_bottom - for x = lt.x, rb.x do -- x loop - local px = position.x + x - for y = lt.y, rb.y do -- y loop - local p = { x = px, y = position.y + y } - local tile = surface.get_tile(p) + + local tiles = {} + local surface = entity.surface + local left_top = entity.bounding_box.left_top + local right_bottom = entity.bounding_box.right_bottom + for x = left_top.x, right_bottom.x do + for y = left_top.y, right_bottom.y do + local tile = surface.get_tile(x, y) local tile_name = tile.name local degrade_tile_name = config.degrade_order[tile_name] - if not degrade_tile_name then return end - table.insert(tiles, { name = degrade_tile_name, position = p }) + if degrade_tile_name then + tiles[#tiles + 1] = { name = degrade_tile_name, position = { x, y } } + end end end surface.set_tiles(tiles) end --- Turns the strength of a tile into a probability (0 = impossible, 1 = certain) +--- Covert strength of a tile into a probability to degrade (0 = impossible, 1 = certain) +--- @param strength number +--- @return number local function get_probability(strength) - local v1 = strength / max_strength - local dif = 1 - v1 - local v2 = dif / 2 - return (1 - v1 + v2) / config.weakness_value + return 1.5 * (1 - (strength / max_strength)) / config.weakness_value end --- Gets the mean of the strengths around a tile to give the strength at that position +--- Gets the average tile strengths around position +--- @param surface LuaSurface +--- @param position MapPosition +--- @return number? local function get_tile_strength(surface, position) --- @diagnostic disable-next-line Incorrect Api Type: https://forums.factorio.com/viewtopic.php?f=233&t=109145&p=593761&hilit=get_tile#p593761 local tile = surface.get_tile(position) local tile_name = tile.name local strength = config.strengths[tile_name] if not strength then return end - for x = -1, 1 do -- x loop - local px = position.x + x - for y = -1, 1 do -- y loop - local check_tile = surface.get_tile(px, position.y + y) + + for x = position.x - 1, position.x + 1 do + for y = position.y - 1, position.y + 1 do + local check_tile = surface.get_tile(x, y) local check_tile_name = check_tile.name local check_strength = config.strengths[check_tile_name] or 0 strength = strength + check_strength @@ -74,28 +79,40 @@ local function get_tile_strength(surface, position) return strength / 9 end --- When the player changes position the tile will have a chance to downgrade, debug check is here -Event.add(defines.events.on_player_changed_position, function(event) +--- When the player changes position the tile will have a chance to downgrade +--- @param event EventData.on_player_changed_position +local function on_player_changed_position(event) local player = game.players[event.player_index] if player.controller_type ~= defines.controllers.character then return end + local surface = player.physical_surface local position = player.physical_position local strength = get_tile_strength(surface, position) if not strength then return end - if get_probability(strength) > math.random() then - degrade(surface, position) - end -end) --- When an entity is build there is a much higher chance that the tiles will degrade + if get_probability(strength) > random() then + degrade_tile(surface, position) + end +end + +--- When an entity is build there is a much higher chance that the tiles will degrade +--- @param event EventData.on_built_entity | EventData.on_robot_built_entity local function on_built_entity(event) local entity = event.entity local strength = get_tile_strength(entity.surface, entity.position) if not strength then return end - if get_probability(strength) * config.weakness_value > math.random() then + + if get_probability(strength) * config.weakness_value > random() then degrade_entity(entity) end end -Event.add(defines.events.on_built_entity, on_built_entity) -Event.add(defines.events.on_robot_built_entity, on_built_entity) +local e = defines.events + +return { + events = { + [e.on_player_changed_position] = on_player_changed_position, + [e.on_robot_built_entity] = on_built_entity, + [e.on_built_entity] = on_built_entity, + }, +} diff --git a/exp_scenario/module/control/discord_alerts.lua b/exp_scenario/module/control/discord_alerts.lua new file mode 100644 index 00000000..38978e1c --- /dev/null +++ b/exp_scenario/module/control/discord_alerts.lua @@ -0,0 +1,332 @@ +--[[-- Control - Discord Alerts +Sends alert messages to our discord server when certain events are triggered +]] + +local ExpUtil = require("modules/exp_util") +local Colors = require("modules/exp_util/include/color") +local config = require("modules.exp_legacy.config.discord_alerts") + +local format_string = string.format +local write_json = ExpUtil.write_json +local playtime_format = ExpUtil.format_time_factory{ format = "short", hours = true, minutes = true, seconds = true } +local emit_event_time_format = ExpUtil.format_time_factory{ format = "short", hours = true, minutes = true } + +local e = defines.events +local events = {} + +--- Append the play time to a players name +--- @param player_name string +--- @return string +local function append_playtime(player_name) + if not config.show_playtime then + return player_name + end + + local player = game.get_player(player_name) + if not player then + return player_name + end + + return format_string("%s (%s)", player.name, playtime_format(player.online_time)) +end + +--- Get the player name from an event +--- @param event { player_index: number, by_player_name: string? } +--- @return string, string +local function get_player_name(event) + local player = game.players[event.player_index] + return player.name, event.by_player_name +end + +--- Convert a colour value into hex +--- @param color Color.0 +--- @return string +local function to_hex(color) + local hex_digits = "0123456789ABCDEF" + local function hex(bit) + local major, minor = math.modf(bit / 16) + major, minor = major + 1, minor * 16 + 1 + return hex_digits:sub(major, major) .. hex_digits:sub(minor, minor) + end + + return format_string("0x%s%s%s", hex(color.r), hex(color.g), hex(color.b)) +end + +--- Emit the requires json to file for the given event arguments +--- @param opts { title: string?, color: (Color.0 | string)?, description: string?, tick: number?, fields: { name: string, value: string, inline: boolean? }[] } +local function emit_event(opts) + local admins_online = 0 + local players_online = 0 + for _, player in pairs(game.connected_players) do + players_online = players_online + 1 + if player.admin then + admins_online = admins_online + 1 + end + end + + local tick_formatted = emit_event_time_format(opts.tick or game.tick) + table.insert(opts.fields, 1, { + name = "Server Details", + value = format_string("Server: ${serverName} Time: %s\nTotal: %d Online: %d Admins: %d", tick_formatted, #game.players, players_online, admins_online), + }) + + local color = opts.color + write_json("ext/discord.out", { + title = opts.title or "", + description = opts.description or "", + color = type(color) == "table" and to_hex(color) or color or "0x0", + fields = opts.fields, + }) +end + +--- Repeated protected entity mining +if config.entity_protection then + local EntityProtection = require("modules.exp_legacy.modules.control.protection") + events[EntityProtection.events.on_repeat_violation] = function(event) + local player_name = get_player_name(event) + emit_event{ + title = "Entity Protection", + description = "A player removed protected entities", + color = Colors.yellow, + fields = { + { name = "Player", inline = true, value = append_playtime(player_name) }, + { name = "Entity", inline = true, value = event.entity.name }, + { name = "Location", value = format_string("X %.1f Y %.1f", event.entity.position.x, event.entity.position.y) }, + }, + } + end +end + +--- Reports added and removed +if config.player_reports then + local Reports = require("modules.exp_legacy.modules.control.reports") + events[Reports.events.on_player_reported] = function(event) + local player_name, by_player_name = get_player_name(event) + local player = assert(game.get_player(player_name)) + emit_event{ + title = "Report", + description = "A player was reported", + color = Colors.yellow, + fields = { + { name = "Player", inline = true, value = append_playtime(player_name) }, + { name = "By", inline = true, value = append_playtime(by_player_name) }, + { name = "Report Count", inline = true, value = Reports.count_reports(player) }, + { name = "Reason", value = event.reason }, + }, + } + end + events[Reports.events.on_report_removed] = function(event) + if event.batch ~= 1 then return end + local player_name = get_player_name(event) + emit_event{ + title = "Reports Removed", + description = "A player has a report removed", + color = Colors.green, + fields = { + { name = "Player", inline = true, value = append_playtime(player_name) }, + { name = "By", inline = true, value = append_playtime(event.removed_by_name) }, + { name = "Report Count", inline = true, value = tostring(event.batch_count) }, + }, + } + end +end + +--- Warnings added and removed +if config.player_warnings then + local Warnings = require("modules.exp_legacy.modules.control.warnings") + events[Warnings.events.on_warning_added] = function(event) + local player_name, by_player_name = get_player_name(event) + local player = assert(game.get_player(player_name)) + emit_event{ + title = "Warning", + description = "A player has been given a warning", + color = Colors.yellow, + fields = { + { name = "Player", inline = true, value = append_playtime(player_name) }, + { name = "By", inline = true, value = append_playtime(by_player_name) }, + { name = "Report Count", inline = true, value = Warnings.count_warnings(player) }, + { name = "Reason", value = event.reason }, + }, + } + end + events[Warnings.events.on_warning_removed] = function(event) + if event.batch ~= 1 then return end + local player_name = get_player_name(event) + emit_event{ + title = "Warnings Removed", + description = "A player has a warning removed", + color = Colors.green, + fields = { + { name = "Player", inline = true, value = append_playtime(player_name) }, + { name = "By", inline = true, value = append_playtime(event.removed_by_name) }, + { name = "Report Count", inline = true, value = tostring(event.batch_count) }, + }, + } + end +end + +--- When a player is jailed or unjailed +if config.player_jail then + local Jail = require("modules.exp_legacy.modules.control.jail") + events[Jail.events.on_player_jailed] = function(event) + local player_name, by_player_name = get_player_name(event) + emit_event{ + title = "Jail", + description = "A player has been jailed", + color = Colors.yellow, + fields = { + { name = "Player", inline = true, value = append_playtime(player_name) }, + { name = "By", inline = true, value = append_playtime(by_player_name) }, + { name = "Reason", value = event.reason }, + }, + } + end + events[Jail.events.on_player_unjailed] = function(event) + local player_name, by_player_name = get_player_name(event) + emit_event{ + title = "Unjail", + description = "A player has been unjailed", + color = Colors.green, + fields = { + { name = "Player", inline = true, value = append_playtime(player_name) }, + { name = "By", inline = true, value = append_playtime(by_player_name) }, + }, + } + end +end + +--- Ban and unban +if config.player_bans then + --- @param event EventData.on_player_banned + events[e.on_player_banned] = function(event) + if event.by_player then + local by_player = game.players[event.by_player] + emit_event{ + title = "Banned", + description = "A player has been banned", + color = Colors.red, + fields = { + { name = "Player", inline = true, value = append_playtime(event.player_name) }, + { name = "By", inline = true, value = append_playtime(by_player.name) }, + { name = "Reason", value = event.reason }, + }, + } + end + end + --- @param event EventData.on_player_unbanned + events[e.on_player_unbanned] = function(event) + if event.by_player then + local by_player = game.players[event.by_player] + emit_event{ + title = "Un-Banned", + description = "A player has been un-banned", + color = Colors.green, + fields = { + { name = "Player", inline = true, value = append_playtime(event.player_name) }, + { name = "By", inline = true, value = append_playtime(by_player.name) }, + { name = "Reason", value = event.reason }, + }, + } + end + end +end + +--- Mute and unmute +if config.player_mutes then + --- @param event EventData.on_player_muted + events[e.on_player_muted] = function(event) + local player_name = get_player_name(event) + emit_event{ + title = "Muted", + description = "A player has been muted", + color = Colors.yellow, + fields = { + { name = "Player", inline = true, value = append_playtime(player_name) }, + }, + } + end + --- @param event EventData.on_player_unmuted + events[e.on_player_unmuted] = function(event) + local player_name = get_player_name(event) + emit_event{ + title = "Un-Muted", + description = "A player has been un-muted", + color = Colors.green, + fields = { + { name = "Player", inline = true, value = append_playtime(player_name) }, + }, + } + end +end + +--- Kick +if config.player_kicks then + --- @param event EventData.on_player_kicked + events[e.on_player_kicked] = function(event) + if event.by_player then + local player_name = get_player_name(event) + local by_player = game.players[event.by_player] + emit_event{ + title = "Kick", + description = "A player has been kicked", + color = Colors.orange, + fields = { + { name = "Player", inline = true, value = append_playtime(player_name) }, + { name = "By", inline = true, value = append_playtime(by_player.name) }, + { name = "Reason", value = event.reason }, + }, + } + end + end +end + +--- Promote and demote +if config.player_promotes then + --- @param event EventData.on_player_promoted + events[e.on_player_promoted] = function(event) + local player_name = get_player_name(event) + emit_event{ + title = "Promote", + description = "A player has been promoted", + color = Colors.green, + fields = { + { name = "Player", inline = true, value = append_playtime(player_name) }, + }, + } + end + --- @param event EventData.on_player_demoted + events[e.on_player_demoted] = function(event) + local player_name = get_player_name(event) + emit_event{ + title = "Demote", + description = "A player has been demoted", + color = Colors.yellow, + fields = { + { name = "Player", inline = true, value = append_playtime(player_name) }, + }, + } + end +end + + +--- @param event EventData.on_console_command +events[e.on_console_command] = function(event) + if event.player_index then + local player_name = get_player_name(event) + if config[event.command] then + emit_event{ + title = event.command:gsub("^%l", string.upper), + description = "/" .. event.command .. " was used", + color = Colors.grey, + fields = { + { name = "By", inline = true, value = append_playtime(player_name) }, + { name = "Details", value = event.parameters ~= "" and event.parameters or "" }, + }, + } + end + end +end + +return { + events = events, +} diff --git a/exp_scenario/module/control/extra_logging.lua b/exp_scenario/module/control/extra_logging.lua new file mode 100644 index 00000000..aaacd3d1 --- /dev/null +++ b/exp_scenario/module/control/extra_logging.lua @@ -0,0 +1,83 @@ +--[[-- Addon Logging +Log some extra events to a separate file +]] + +local config = require("modules.exp_legacy.config.logging") +local config_res = require("modules.exp_legacy.config.research") + +local concat = table.concat +local write_file = helpers.write_file + +--- Add a line to the log file +--- @param ... string +local function add_log_line(...) + write_file(config.file_name, concat({ ... }, " ") .. "\n", true, 0) +end + +--- Add a line to the log file +--- @param line LocalisedString +local function add_log_line_locale(line) + write_file(config.file_name, line, true, 0) +end + +--- @param event EventData.on_cargo_pod_finished_ascending +local function on_cargo_pod_finished_ascending(event) + if event.launched_by_rocket then + local force = event.cargo_pod.force + if force.rockets_launched >= config.rocket_launch_display_rate and force.rockets_launched % config.rocket_launch_display_rate == 0 then + add_log_line("[ROCKET]", force.rockets_launched, "rockets launched") + elseif config.rocket_launch_display[force.rockets_launched] then + add_log_line("[ROCKET]", force.rockets_launched, "rockets launched") + end + end +end + +--- @param event EventData.on_pre_player_died +local function on_pre_player_died(event) + local player = assert(game.get_player(event.player_index)) + local cause = event.cause + if cause then + local by_player = event.cause.player + add_log_line("[DEATH]", player.name, "died because of", by_player and by_player.name or event.cause.name) + else + add_log_line("[DEATH]", player.name, "died because of unknown reason") + end +end + +--- @param event EventData.on_research_finished +local function on_research_finished(event) + if event.by_script then + return + end + + local inf_research_level = config_res.inf_res[config_res.mod_set][event.research.name] + if inf_research_level and event.research.level >= inf_research_level then + add_log_line_locale{ "", "[RES] ", event.research.prototype.localised_name, " at level ", event.research.level - 1, "has been researched\n" } + else + add_log_line_locale{ "", "[RES] ", event.research.prototype.localised_name, "has been researched\n" } + end +end + +--- @param event EventData.on_player_joined_game +local function on_player_joined_game(event) + local player = assert(game.get_player(event.player_index)) + add_log_line("[JOIN]", player.name, "joined the game") +end + +--- @param event EventData.on_player_left_game +local function on_player_left_game(event) + local player = assert(game.get_player(event.player_index)) + add_log_line("[LEAVE]", game.players[event.player_index].name, config.disconnect_reason[event.reason]) +end + +local e = defines.events + +return { + events = { + [e.on_cargo_pod_finished_ascending] = on_cargo_pod_finished_ascending, + [e.on_pre_player_died] = on_pre_player_died, + [e.on_research_finished] = on_research_finished, + [e.on_player_joined_game] = on_player_joined_game, + [e.on_player_left_game] = on_player_left_game, + } +} diff --git a/exp_scenario/module/control/fast_deconstruction.lua b/exp_scenario/module/control/fast_deconstruction.lua new file mode 100644 index 00000000..4481f9b4 --- /dev/null +++ b/exp_scenario/module/control/fast_deconstruction.lua @@ -0,0 +1,181 @@ +--[[-- Control - Fast Deconstruction +Makes trees which are marked for decon "decay" quickly to allow faster building +]] + +local Gui = require("modules/exp_gui") +local Async = require("modules/exp_util/async") +local Roles = require("modules.exp_legacy.expcore.roles") + +local PlayerData = require("modules.exp_legacy.expcore.player_data") +local HasEnabledDecon = PlayerData.Settings:combine("HasEnabledDecon") +HasEnabledDecon:set_default(false) + +local random = math.random +local floor = math.floor +local min = math.min + +--- @class TreeDeconCache +--- @field tick number The tick this cache is valid +--- @field player_index number +--- @field player LuaPlayer +--- @field force LuaForce +--- @field trees LuaEntity[] +--- @field tree_count number +--- @field permission "fast" | "allow" | "disallow" +--- @field task Async.AsyncReturn + +local cache --- @type TreeDeconCache? + +local remove_trees_async = + Async.register(function(task_data) + --- @cast task_data TreeDeconCache + if task_data.tree_count == 0 then + return Async.status.complete() + end + + local head = task_data.tree_count + local trees = task_data.trees + + local max_remove = floor(head / 100) + 1 + local remove_count = min(random(0, max_remove), head) + for i = 1, remove_count do + local index = random(1, head) + local entity = trees[index] + trees[index] = trees[head] + head = head - 1 + + if entity and entity.valid then + entity.destroy() + end + end + + task_data.tree_count = head + return Async.status.continue(task_data) + end) + +--- Check the permission the player has +--- @param player LuaPlayer +--- @return "fast" | "allow" | "disallow" +local function get_permission(player) + if Roles.player_allowed(player, "fast-tree-decon") then + return HasEnabledDecon:get(player) and "fast" or "allow" + elseif Roles.player_allowed(player, "standard-decon") then + return "allow" + else + return "disallow" + end +end + +--- Return or build the cache for a player +--- @param player_index number +--- @return TreeDeconCache +local function get_player_cache(player_index) + -- Return the current cache if it is valid + if cache and cache.tick == game.tick and cache.player_index == player_index then + return cache + end + + -- Create a new cache if the previous on is in use + if not cache or cache.task and not cache.task.completed then + cache = {} --[[@as any]] + end + + local player = assert(game.get_player(player_index)) + cache.tick = game.tick + cache.player_index = player_index + cache.player = player + cache.force = player.force --[[ @as LuaForce ]] + cache.tree_count = 0 + cache.trees = {} + cache.permission = get_permission(player) + cache.task = remove_trees_async:start_soon(cache) + + return cache +end + +-- Left menu button to toggle between fast decon and normal decon marking +Gui.toolbar.create_button{ + name = "toggle-tree-decon", + sprite = "entity/tree-01", + tooltip = { "exp_fast-decon.tooltip-main" }, + auto_toggle = true, + visible = function(player, _) + return Roles.player_allowed(player, "fast-tree-decon") + end +}:on_click(function(def, player, element) + local state = Gui.toolbar.get_button_toggled_state(def, player) + HasEnabledDecon:set(player, state) + player.print{ "exp_fast-decon.chat-toggle", state and { "exp_fast-decon.chat-enabled" } or { "exp_fast-decon.chat-disabled" } } +end) + +-- Add trees to queue when marked, only allows simple entities and for players with role permission +--- @param event EventData.on_marked_for_deconstruction +local function on_marked_for_deconstruction(event) + -- Check player and entity are valid + local entity = event.entity + local player_index = event.player_index + if not player_index or not entity.valid then + return + end + + -- If it has a last user then either do nothing or cancel decon + local last_user = entity.last_user + local player_cache = get_player_cache(player_index) + if last_user then + if player_cache.permission == "disallow" then + entity.cancel_deconstruction(player_cache.force) + end + return + end + + -- Allow fast decon on no last user and not cliff + if player_cache.permission == "fast" and entity.type ~= "cliff" then + local head = player_cache.tree_count + 1 + player_cache.tree_count = head + player_cache.trees[head] = entity + end +end + +--- Clear trees when hit with a car +--- @param event EventData.on_entity_damaged +local function on_entity_damaged(event) + -- Check it was an impact from a force + if not (event.damage_type.name == "impact" and event.force) then + return + end + + -- Check the entity hit was a tree or rock + if not (event.entity.type == "tree" or event.entity.type == "simple-entity") then + return + end + + -- Check the case was a car + if (not event.cause) or (event.cause.type ~= "car") then + return + end + + -- Get a valid player as the driver + local driver = event.cause.get_driver() + if not driver then return end + if driver.object_name ~= "LuaPlayer" then + driver = driver.player + if not driver then return end + end + + -- Mark the entity to be removed + local allow = get_player_cache(driver.index) + if allow == "fast" and HasEnabledDecon:get(driver) then + event.entity.destroy() + else + event.entity.order_deconstruction(event.force, driver) + end +end + +local e = defines.events + +return { + events = { + [e.on_entity_damaged] = on_entity_damaged, + [e.on_marked_for_deconstruction] = on_marked_for_deconstruction, + } +} diff --git a/exp_scenario/module/control/help_bubbles.lua b/exp_scenario/module/control/help_bubbles.lua new file mode 100644 index 00000000..6740b93a --- /dev/null +++ b/exp_scenario/module/control/help_bubbles.lua @@ -0,0 +1,100 @@ +--[[-- Control - Help Bubbles +Adds friendly biters that walk around and give helpful messages +]] + +local Async = require("modules/exp_util/async") +local Storage = require("modules/exp_util/storage") +local config = require("modules.exp_legacy.config.compilatron") + +--- @type table +local persistent_locations = {} +Storage.register(persistent_locations, function(tbl) + persistent_locations = tbl +end) + +--- @class speech_bubble_task_param +--- @field entity LuaEntity +--- @field messages LocalisedString[] +--- @field previous_message LuaEntity? +--- @field current_message_index number + +--- Used by create_entity within speech_bubble_async +local speech_bubble_param = { + name = "compi-speech-bubble", + position = { 0, 0 }, +} + +--- Cycle between a set category of messages above an entity +local speech_bubble_task = + Async.register(function(task) + --- @cast task speech_bubble_task_param + local entity = task.entity + if not entity.valid then + return Async.status.complete() + end + + local index = task.current_message_index + if index > #task.messages then + index = 1 + end + + local previous_message = task.previous_message + if previous_message and previous_message.valid then + previous_message.destroy() + end + + speech_bubble_param.source = entity + speech_bubble_param.text = task.messages[index] + task.previous_message = entity.surface.create_entity(speech_bubble_param) + + task.current_message_index = index + 1 + return Async.status.delay(config.message_cycle, task) + end) + +--- Register an entity to start spawning speech bubbles +--- @param entity LuaEntity the entity which will have messages spawn from it +--- @param messages LocalisedString[] the messages which should be shown +--- @param starting_index number? the message index to start at, default 1 +--- @return Async.AsyncReturn +local function register_entity(entity, messages, starting_index) + return speech_bubble_task{ + entity = entity, + messages = messages, + current_message_index = starting_index or 1, + } +end + +--- Check all persistent locations from the config are active +local function check_persistent_locations() + for name, location in pairs(config.locations) do + local task = persistent_locations[name] + if task and not task.completed then + goto continue + end + + local surface = game.get_surface(location.spawn_surface) + if not surface then + goto continue + end + + local position = surface.find_non_colliding_position(location.entity_name, location.spawn_position, 1.5, 0.5) + if not position then + goto continue + end + + local entity = surface.create_entity{ name = location.entity_name, position = position, force = game.forces.neutral } + if not entity then + goto continue + end + + persistent_locations[name] = register_entity(entity, location.messages) + ::continue:: + end +end + +return { + on_nth_tick = { + [config.message_cycle] = check_persistent_locations, + }, + register_entity = register_entity, +} diff --git a/exp_scenario/module/control/inserter_pickup.lua b/exp_scenario/module/control/inserter_pickup.lua new file mode 100644 index 00000000..d7847a00 --- /dev/null +++ b/exp_scenario/module/control/inserter_pickup.lua @@ -0,0 +1,34 @@ +--[[-- Control Insert Pickup +Automatically pick up the items in the inserts hand when you mine it +]] + +local controllers_with_inventory = { + [defines.controllers.character] = true, + [defines.controllers.god] = true, + [defines.controllers.editor] = true, +} + +--- @param event EventData.on_player_mined_entity +local function on_player_mined_entity(event) + local entity = event.entity + if not entity.valid or entity.type ~= "inserter" or entity.drop_target then + return + end + + local item_entity = entity.surface.find_entity("item-on-ground", entity.drop_position) + + if item_entity then + local player = assert(game.get_player(event.player_index)) + if controllers_with_inventory[player.controller_type] then + player.mine_entity(item_entity) + end + end +end + +local e = defines.events + +return { + events = { + [e.on_player_mined_entity] = on_player_mined_entity + } +} diff --git a/exp_scenario/module/control/inventory_clear.lua b/exp_scenario/module/control/inventory_clear.lua new file mode 100644 index 00000000..2dc63e31 --- /dev/null +++ b/exp_scenario/module/control/inventory_clear.lua @@ -0,0 +1,27 @@ +--[[-- Control - Inventory Clear +Will move players items to spawn when they are banned or kicked, option to clear on leave +]] + +local ExpUtil = require("modules/exp_util") +local events = require("modules.exp_legacy.config.inventory_clear") + +--- @param event { player_index: number } +local function clear_items(event) + local player = assert(game.get_player(event.player_index)) + local inventory = assert(player.get_main_inventory()) + ExpUtil.transfer_inventory_to_surface{ + inventory = inventory, + surface = game.planets.nauvis.surface, + name = "iron-chest", + allow_creation = true, + } +end + +local event_handlers = {} +for _, event_name in ipairs(events) do + event_handlers[event_name] = clear_items +end + +return { + events = event_handlers, +} diff --git a/exp_scenario/module/control/mine_depletion.lua b/exp_scenario/module/control/mine_depletion.lua new file mode 100644 index 00000000..2764ebcf --- /dev/null +++ b/exp_scenario/module/control/mine_depletion.lua @@ -0,0 +1,231 @@ +--[[-- Control - Mine Depletion +Marks mining drills for deconstruction when resources deplete +]] + +local Async = require("modules/exp_util/async") +local config = require("modules.exp_legacy.config.miner") + +local floor = math.floor + +--- Orders the deconstruction of an entity by its own force +local order_deconstruction_async = + Async.register(function(entity) + --- @cast entity LuaEntity + entity.order_deconstruction(entity.force) + end) + +--- Reliability get the drop target of an entity +--- @param entity LuaEntity +--- @return LuaEntity? +local function get_drop_chest(entity) + -- First check the direct drop target + local target = entity.drop_target + if target and (target.type == "container" or target.type == "logistic-container" or target.type == "infinity-container") then + return target + end + + -- Then check all entities at the drop position + local entities = entity.surface.find_entities_filtered{ + position = entity.drop_position, + type = { "container", "logistic-container", "infinity-container" }, + } + + return #entities > 0 and entities[1] or nil +end + +--- Check if an entity should has checked performed +--- @param entity LuaEntity +--- @return boolean +local function prevent_deconstruction(entity) + -- Already waiting to be deconstructed + if not entity.valid or entity.to_be_deconstructed() then + return true + end + + -- Not minable, selectable, or deconstructive + if not entity.minable or not entity.prototype.selectable_in_game or entity.has_flag("not-deconstructable") then + return true + end + + -- Is connected to the circuit network + local red_write_connection = entity.get_wire_connector(defines.wire_connector_id.circuit_red, false) + local green_write_connection = entity.get_wire_connector(defines.wire_connector_id.circuit_green, false) + if red_write_connection and red_write_connection.connection_count > 0 + or green_write_connection and green_write_connection.connection_count > 0 then + return true + end + + return false +end + +--- Check if an output chest should be deconstructed +--- @param entity LuaEntity +local function try_deconstruct_output_chest(entity) + -- Get a valid chest as the target + local target = get_drop_chest(entity) + if not target or prevent_deconstruction(target) then + return + end + + -- Get all adjacent mining drills and inserters + local entities = target.surface.find_entities_filtered{ + type = { "mining-drill", "inserter" }, + to_be_deconstructed = false, + area = { + { target.position.x - 1, target.position.y - 1 }, + { target.position.x + 1, target.position.y + 1 } + }, + } + + -- Check if any other entity is using this chest + for _, other in ipairs(entities) do + if other ~= entity and get_drop_chest(other) == target then + return + end + end + + -- Deconstruct the chest + order_deconstruction_async:start_after(10, target) +end + +--- Check if a miner should be deconstructed +--- @param entity LuaEntity +local function try_deconstruct_miner(entity) + -- Check if the miner should be deconstructed + if prevent_deconstruction(entity) then + return + end + + -- Check if there are any resources remaining for the miner + local surface = entity.surface + local resources = surface.find_entities_filtered{ + type = "resource", + area = entity.mining_area, + } + + for _, resource in ipairs(resources) do + if resource.amount > 0 then + return + end + end + + -- Deconstruct the miner + order_deconstruction_async:start_after(10, entity) + + -- Try deconstruct the output chest + if config.chest then + try_deconstruct_output_chest(entity) + end + + -- Skip pipe build if not required + if not config.fluid or #entity.fluidbox == 0 then + return + end + + -- Build pipes if the miner used fluid + local position = entity.position + local create_entity_position = { x = position.x, y = position.y } + local create_entity_param = { name = "entity-ghost", inner_name = "pipe", force = entity.force, position = create_entity_position } + local create_entity = surface.create_entity + create_entity(create_entity_param) + + -- Find all the entities to connect to + local bounding_box = entity.bounding_box + local search_area = { + { bounding_box.left_top.x - 1, bounding_box.left_top.y - 1 }, + { bounding_box.right_bottom.x + 1, bounding_box.right_bottom.y + 1 }, + } + + local entities = surface.find_entities_filtered{ area = search_area, type = { "mining-drill", "pipe", "pipe-to-ground", "infinity-pipe" } } + local ghosts = surface.find_entities_filtered{ area = search_area, ghost_type = { "pipe", "pipe-to-ground", "infinity-pipe" } } + table.insert_array(entities, ghosts) + + -- Check which directions to add pipes in + local pos_x, pos_y, neg_x, neg_y = false, false, false, false + for _, other in ipairs(entities) do + if (other.position.x > position.x) and (other.position.y == position.y) then + pos_x = true + elseif (other.position.x < position.x) and (other.position.y == position.y) then + neg_x = true + elseif (other.position.x == position.x) and (other.position.y > position.y) then + pos_y = true + elseif (other.position.x == position.x) and (other.position.y < position.y) then + neg_y = true + end + end + + -- Build the pipes + if pos_x then + create_entity_position.y = floor(position.y) + for x = position.x + 1, bounding_box.right_bottom.x do + create_entity_position.x = x + create_entity(create_entity_param) + end + end + if neg_x then + create_entity_position.y = floor(position.y) + for x = floor(bounding_box.left_top.x), floor(position.x - 1) do + create_entity_position.x = x + create_entity(create_entity_param) + end + end + if pos_y then + create_entity_position.x = floor(position.x) + for y = floor(position.y + 1), floor(bounding_box.right_bottom.y) do + create_entity_position.y = y + create_entity(create_entity_param) + end + end + if neg_y then + create_entity_position.x = floor(position.x) + for y = floor(bounding_box.left_top.y), floor(position.y - 1) do + create_entity_position.y = y + create_entity(create_entity_param) + end + end +end + +--- Get the max mining radius +local max_mining_radius = 0 +for _, proto in pairs(prototypes.get_entity_filtered{ { filter = "type", type = "mining-drill" } }) do + if proto.mining_drill_radius > max_mining_radius then + max_mining_radius = proto.mining_drill_radius + end +end + +--- Try deconstruct a miner when its resources deplete +--- @param event EventData.on_resource_depleted +local function on_resource_depleted(event) + local resource = event.entity + if resource.prototype.infinite_resource then + return + end + + -- Find all mining drills within the area + local position = resource.position + local drills = resource.surface.find_entities_filtered{ + type = "mining-drill", + area = { + { position.x - max_mining_radius, position.y - max_mining_radius }, + { position.x + max_mining_radius, position.y + max_mining_radius }, + }, + } + + -- Check which could have reached this resource + for _, drill in pairs(drills) do + local radius = drill.prototype.mining_drill_radius + local dx = math.abs(drill.position.x - resource.position.x) + local dy = math.abs(drill.position.y - resource.position.y) + if dx <= radius and dy <= radius then + try_deconstruct_miner(drill) + end + end +end + +local e = defines.events + +return { + events = { + [e.on_resource_depleted] = on_resource_depleted, + }, +} diff --git a/exp_scenario/module/control/nuke_protection.lua b/exp_scenario/module/control/nuke_protection.lua new file mode 100644 index 00000000..cfff046b --- /dev/null +++ b/exp_scenario/module/control/nuke_protection.lua @@ -0,0 +1,56 @@ +--[[-- Control - Nuke Protection +Disable new players from having certain items in their inventory, most commonly nukes +]] + +local ExpUtil = require("modules/exp_util") +local Roles = require("modules.exp_legacy.expcore.roles") +local config = require("modules.exp_legacy.config.nukeprotect") + +--- Check all items in the given inventory +--- @param player LuaPlayer +--- @param type defines.inventory +--- @param banned_items string[] +local function check_items(player, type, banned_items) + -- If the player has perms to be ignored, then they should be + if config.ignore_permission and Roles.player_allowed(player, config.ignore_permission) then return end + if config.ignore_admins and player.admin then return end + + local items = {} --- @type LuaItemStack[] + local inventory = assert(player.get_inventory(type)) + -- Check what items the player has + for i = 1, #inventory do + local item = inventory[i] + if item.valid_for_read and banned_items[item.name] then + player.print{ "exp_nuke-protection.chat-found", item.prototype.localised_name } + items[#items + 1] = item + end + end + + -- Move any items they aren't allowed + ExpUtil.move_items_to_surface{ + items = items, + surface = game.planets.nauvis.surface, + allow_creation = true, + name = "iron-chest", + } +end + +--- Add event handlers for the different inventories +local events = {} +for index, inventory in ipairs(config.inventories) do + if next(inventory.items) then + local assert_msg = "invalid event, no player index, index: " .. index + --- @param event { player_index: number } + events[inventory.event] = function(event) + local player_index = assert(event.player_index, assert_msg) + local player = assert(game.get_player(player_index)) + if player and player.valid then + check_items(player, inventory.inventory, inventory.items) + end + end + end +end + +return { + events = events, +} diff --git a/exp_scenario/module/control/pollution_grading.lua b/exp_scenario/module/control/pollution_grading.lua new file mode 100644 index 00000000..8bd8f876 --- /dev/null +++ b/exp_scenario/module/control/pollution_grading.lua @@ -0,0 +1,27 @@ +--[[-- Control - Pollution Grading +Makes pollution look much nice of the map, ie not one big red mess +]] + +local config = require("modules.exp_legacy.config.pollution_grading") + +local function check_surfaces() + local max_reference = 0 + for _, surface in pairs(game.surfaces) do + local reference = surface.get_pollution(config.reference_point) + if reference > max_reference then + max_reference = reference + end + end + + local max = max_reference * config.max_scalar + local min = max * config.min_scalar + local settings = game.map_settings.pollution + settings.expected_max_per_chunk = max + settings.min_to_show_per_chunk = min +end + +return { + on_nth_tick = { + [config.update_delay * 3600] = check_surfaces, + } +} diff --git a/exp_scenario/module/control/protection_jail.lua b/exp_scenario/module/control/protection_jail.lua new file mode 100644 index 00000000..c39e649b --- /dev/null +++ b/exp_scenario/module/control/protection_jail.lua @@ -0,0 +1,47 @@ +--[[-- Control - Projection Jail +When a player triggers protection multiple times they are automatically jailed +]] + +local ExpUtil = require("modules/exp_util") +local Storage = require("modules/exp_util/storage") +local Jail = require("modules.exp_legacy.modules.control.jail") +local Protection = require("modules.exp_legacy.modules.control.protection") + +local format_player_name = ExpUtil.format_player_name_locale + +--- Stores how many times the repeat violation was triggered +--- @type table +local repeat_count = {} +Storage.register(repeat_count, function(tbl) + repeat_count = tbl +end) + +--- When a protection is triggered increment their counter and jail if needed +local function on_repeat_violation(event) + local player = assert(game.get_player(event.player_index)) + + -- Increment the counter + local count = (repeat_count[player.index] or 0) + 1 + repeat_count[player.index] = count + + -- Jail if needed + if count >= 3 then + Jail.jail_player(player, "", "Removed too many protected entities, please wait for a moderator.") + game.print{ "exp_protection-jail.chat-jailed", format_player_name(player) } + end +end + +--- Clear the counter when they leave the game (stops a build up of data) +--- @param event EventData.on_player_left_game +local function on_player_left_game(event) + repeat_count[event.player_index] = nil +end + +local e = defines.events + +return { + events = { + [Protection.events.on_repeat_violation] = on_repeat_violation, + [e.on_player_left_game] = on_player_left_game, + } +} diff --git a/exp_scenario/module/control/report_jail.lua b/exp_scenario/module/control/report_jail.lua new file mode 100644 index 00000000..e4bbdd72 --- /dev/null +++ b/exp_scenario/module/control/report_jail.lua @@ -0,0 +1,38 @@ +--[[-- Control - Report Jail +When a player is reported, the player is automatically jailed if the combined playtime of the reporters exceeds the reported player +]] + +local ExpUtil = require("modules/exp_util") +local Jail = require("modules.exp_legacy.modules.control.jail") +local Reports = require("modules.exp_legacy.modules.control.reports") + +local max = math.max +local format_player_name = ExpUtil.format_player_name_locale + +--- Returns the playtime of the reporter. Used when calculating the total playtime of all reporters +--- @param player LuaPlayer +--- @param by_player_name string +--- @param reason string +--- @return number +local function reporter_playtime(player, by_player_name, reason) + local by_player = game.get_player(by_player_name) + return by_player and by_player.online_time or 0 +end + +--- Check if the player has too many reports against them (based on playtime) +local function on_player_reported(event) + local player = assert(game.get_player(event.player_index)) + local total_playtime = Reports.count_reports(player, reporter_playtime) + + -- Total time greater than the players own time, or 30 minutes, which ever is greater + if Reports.count_reports(player) > 1 and total_playtime > max(player.online_time * 2, 108000) then + Jail.jail_player(player, "", "Reported by too many players, please wait for a moderator.") + game.print{ "exp_report-jail.chat-jailed", format_player_name(player) } + end +end + +return { + events = { + [Reports.events.on_player_reported] = on_player_reported, + } +} diff --git a/exp_scenario/module/control/spawn_area.lua b/exp_scenario/module/control/spawn_area.lua new file mode 100644 index 00000000..8c1626b7 --- /dev/null +++ b/exp_scenario/module/control/spawn_area.lua @@ -0,0 +1,292 @@ +--[[-- Control - Spawn Area +Adds a custom spawn area with chests and afk turrets +]] + +local config = require("modules.exp_legacy.config.spawn_area") + +--- Apply an offset to a LuaPosition +--- @param position MapPosition +--- @param offset MapPosition +--- @return MapPosition.0 +local function apply_offset(position, offset) + return { + x = (position.x or position[1]) + (offset.x or offset[1]), + y = (position.y or position[2]) + (offset.y or offset[2]) + } +end + +--- Apply offset to an array of positions +--- @param positions table +--- @param offset MapPosition +--- @param x_index number +--- @param y_index number +local function apply_offset_to_array(positions, offset, x_index, y_index) + local x = (offset.x or offset[1]) + local y = (offset.y or offset[2]) + for _, position in ipairs(positions) do + position[x_index] = position[x_index] + x + position[y_index] = position[y_index] + y + end +end + +-- Apply the offsets to all config values +apply_offset_to_array(config.turrets.locations, config.turrets.offset, 1, 2) +apply_offset_to_array(config.afk_belts.locations, config.afk_belts.offset, 1, 2) +apply_offset_to_array(config.water.locations, config.water.offset, 1, 2) +apply_offset_to_array(config.water.locations, config.water.offset, 1, 2) +apply_offset_to_array(config.entities.locations, config.entities.offset, 2, 3) + +--- Get or create the force used for entities in spawn +--- @return LuaForce +local function get_spawn_force() + local force = game.forces["spawn"] + if force and force.valid then + return force + end + + force = game.create_force("spawn") + force.set_cease_fire("player", true) + game.forces["player"].set_cease_fire("spawn", true) + + return force +end + +--- Protects an entity from player interaction +--- @param entity LuaEntity +local function protect_entity(entity) + if entity and entity.valid then + entity.destructible = false + entity.minable = false + entity.rotatable = false + entity.operable = false + end +end + +--- Will spawn all infinite ammo turrets and keep them refilled +local function update_turrets() + local force = get_spawn_force() + for _, position in pairs(config.turrets.locations) do + -- Get or create a valid turret + local surface = assert(game.get_surface("nauvis")) + local turret = surface.find_entity("gun-turret", position) + if not turret or not turret.valid then + turret = surface.create_entity{ name = "gun-turret", position = position, force = force } + if not turret then + goto continue + end + protect_entity(turret) + end + + -- Adds ammo to the turret + local inv = turret.get_inventory(defines.inventory.turret_ammo) + if inv and inv.can_insert{ name = config.turrets.ammo_type, count = 10 } then + inv.insert{ name = config.turrets.ammo_type, count = 10 } + end + + ::continue:: + end +end + +--- Details required to create a 2x2 belt circle +local belt_details = { + { -0.5, -0.5, defines.direction.east }, + { 0.5, -0.5, defines.direction.south }, + { -0.5, 0.5, defines.direction.north }, + { 0.5, 0.5, defines.direction.west }, +} + +--- Makes a 2x2 afk belt at the locations in the config +--- @param surface LuaSurface +--- @param offset MapPosition +local function create_belts(surface, offset) + local belt_type = config.afk_belts.belt_type + + for _, position in pairs(config.afk_belts.locations) do + position = apply_offset(position, offset) + for _, belt in pairs(belt_details) do + local pos = apply_offset(position, belt) + local entity = surface.create_entity{ name = belt_type, position = pos, force = "neutral", direction = belt[3] } + if entity and config.afk_belts.protected then + protect_entity(entity) + end + end + end +end + +-- Generates extra tiles in a set pattern as defined in the config +--- @param surface LuaSurface +--- @param offset MapPosition +local function create_pattern_tiles(surface, offset) + local tiles_to_make = {} + local pattern_tile = config.pattern.pattern_tile + + for index, position in pairs(config.pattern.locations) do + tiles_to_make[index] = { name = pattern_tile, position = apply_offset(position, offset) } + end + + surface.set_tiles(tiles_to_make) +end + +-- Generates extra water as defined in the config +--- @param surface LuaSurface +--- @param offset MapPosition +local function create_water_tiles(surface, offset) + local tiles_to_make = {} + local water_tile = config.water.water_tile + + for _, position in pairs(config.water.locations) do + table.insert(tiles_to_make, { name = water_tile, position = apply_offset(position, offset) }) + end + + surface.set_tiles(tiles_to_make) +end + +--- Generates the entities that are in the config +--- @param surface LuaSurface +--- @param offset MapPosition +local function create_entities(surface, offset) + for _, entity_details in pairs(config.entities.locations) do + local pos = apply_offset({ entity_details[2], entity_details[3] }, offset) + local entity = surface.create_entity{ name = entity_details[1], position = pos, force = "neutral" } + + if entity and config.entities.protected then + protect_entity(entity) + end + + entity.operable = config.entities.operable + end +end + +--- Generates an area with no water or entities, no water area is larger +--- @param surface LuaSurface +--- @param offset MapPosition +local function clear_spawn_area(surface, offset) + local get_tile = surface.get_tile + + -- Make sure a non water tile is used for filling + --- @diagnostic disable-next-line Incorrect Api Type: https://forums.factorio.com/viewtopic.php?f=233&t=109145&p=593761&hilit=get_tile#p593761 + local starting_tile = get_tile(offset) + local fill_tile = starting_tile.collides_with("player") and "landfill" or starting_tile.name + local fill_radius = config.spawn_area.landfill_radius + local fill_radius_sqr = fill_radius ^ 2 + + -- Select the deconstruction tile + local decon_radius = config.spawn_area.deconstruction_radius + local decon_tile = config.spawn_area.deconstruction_tile or fill_tile + + local tiles_to_make = {} + local tile_radius_sqr = config.spawn_area.tile_radius ^ 2 + for x = -fill_radius, fill_radius do -- loop over x + local x_sqr = (x + 0.5) ^ 2 + for y = -fill_radius, fill_radius do -- loop over y + local y_sqr = (y + 0.5) ^ 2 + local dst = x_sqr + y_sqr + local pos = apply_offset({ x, y }, offset) + if dst < tile_radius_sqr then + -- If it is inside the decon radius always set the tile + tiles_to_make[#tiles_to_make + 1] = { name = decon_tile, position = pos } + --- @diagnostic disable-next-line Incorrect Api Type: https://forums.factorio.com/viewtopic.php?f=233&t=109145&p=593761&hilit=get_tile#p593761 + elseif dst < fill_radius_sqr and get_tile(pos).collides_with("player") then + -- If it is inside the fill radius only set the tile if it is water + tiles_to_make[#tiles_to_make + 1] = { name = fill_tile, position = pos } + end + end + end + + -- Remove entities then set the tiles + local entities_to_remove = surface.find_entities_filtered{ position = offset, radius = decon_radius, name = "character", invert = true } + for _, entity in pairs(entities_to_remove) do + entity.destroy() + end + + surface.set_tiles(tiles_to_make) +end + +--- Spawn the resource tiles +--- @param surface LuaSurface +--- @param offset MapPosition +local function create_resources_tiles(surface, offset) + for _, resource in ipairs(config.resource_tiles.resources) do + if resource.enabled then + local pos = apply_offset(resource.offset, offset) + for x = pos.x, pos.x + resource.size[1] do + for y = pos.y, pos.y + resource.size[2] do + surface.create_entity{ name = resource.name, amount = resource.amount, position = { x, y } } + end + end + end + end +end + +--- Spawn the resource entities +--- @param surface LuaSurface +--- @param offset MapPosition +local function create_resource_patches(surface, offset) + for _, resource in ipairs(config.resource_patches.resources) do + if resource.enabled then + local pos = apply_offset(resource.offset, offset) + for i = 1, resource.num_patches do + surface.create_entity{ name = resource.name, amount = resource.amount, position = { pos.x + resource.offset_next[1] * (i - 1), pos.y + resource.offset_next[2] * (i - 1) } } + end + end + end +end + +local on_nth_tick = {} + +if config.turrets.enabled then + --- Refill the ammo in the spawn turrets + on_nth_tick[config.turrets.refill_time] = function() + if game.tick < 10 then return end + update_turrets() + end +end + +if config.resource_refill_nearby.enabled then + --- + on_nth_tick[config.resource_refill_nearby.refill_time] = function() + if game.tick < 10 then return end + + local force = game.forces.player + local surface = assert(game.get_surface("nauvis")) + local entities = surface.find_entities_filtered{ + position = force.get_spawn_position(surface), + radius = config.resource_refill_nearby.range, + name = config.resource_refill_nearby.resources_name + } + + for _, ore in ipairs(entities) do + ore.amount = ore.amount + math.random(config.resource_refill_nearby.amount[1], config.resource_refill_nearby.amount[2]) + end + end +end + +--- When the first player joins create the spawn area +--- @param event EventData.on_player_created +local function on_player_created(event) + if event.player_index ~= 1 then return end + local player = assert(game.get_player(event.player_index)) + local surface = player.physical_surface + local offset = { x = 0, y = 0 } + clear_spawn_area(surface, offset) + + if config.pattern.enabled then create_pattern_tiles(surface, offset) end + if config.water.enabled then create_water_tiles(surface, offset) end + if config.afk_belts.enabled then create_belts(surface, offset) end + if config.entities.enabled then create_entities(surface, offset) end + if config.resource_tiles.enabled then create_resources_tiles(surface, offset) end + if config.resource_patches.enabled then create_resource_patches(surface, offset) end + if config.turrets.enabled then update_turrets() end + + player.force.set_spawn_position(offset, surface) + player.teleport(offset, surface) +end + +local e = defines.events + +return { + on_nth_tick = on_nth_tick, + events = { + [e.on_player_created] = on_player_created, + } +} diff --git a/exp_scenario/module/control/station_auto_name.lua b/exp_scenario/module/control/station_auto_name.lua new file mode 100644 index 00000000..fb9b8905 --- /dev/null +++ b/exp_scenario/module/control/station_auto_name.lua @@ -0,0 +1,104 @@ +--[[-- Control - Station Auto Name +Automatically name stations when they are placed based on closest resource and direction from spawn +]] + +local config = require("modules.exp_legacy.config.station_auto_name") + +local get_direction do + local directions = { + ["W"] = -0.875, + ["NW"] = -0.625, + ["N"] = -0.375, + ["NE"] = -0.125, + ["E"] = 0.125, + ["SE"] = 0.375, + ["S"] = 0.625, + ["SW"] = 0.875, + } + + --- Get the direction of a position from the centre of the surface + --- @param position MapPosition + --- @return string + function get_direction(position) + local angle = math.atan2(position.y, position.x) / math.pi + for direction, required_angle in pairs(directions) do + if angle < required_angle then + return direction + end + end + + return "W" + end +end + +-- Custom strings are used to detect backer names from ghosts +local custom_string = " *" +local custom_string_len = #custom_string + +--- Change the name of a station when it is placed +--- @param event EventData.on_built_entity | EventData.on_robot_built_entity +local function rename_station(event) + local entity = event.entity + local name = entity.name + if name == "entity-ghost" and entity.ghost_name == "train-stop" then + local backer_name = entity.backer_name + if backer_name ~= "" then + entity.backer_name = backer_name .. custom_string + end + + elseif name == "train-stop" then + -- Restore the backer name + local backer_name = entity.backer_name or "" + if backer_name:sub(-custom_string_len) == custom_string then + entity.backer_name = backer_name:sub(1, -custom_string_len - 1) + return + end + + -- Find the closest resource + local icon = "" + local item_name = "" + local bounding_box = entity.bounding_box + local resources = entity.surface.find_entities_filtered{ position = entity.position, radius = 250, type = "resource" } + if #resources > 0 then + local closest_recourse --- @type LuaEntity? + local closest_distance = 250 * 250 -- search radius + 1 + local px, py = bounding_box.left_top.x, bounding_box.left_top.y + + -- Check which recourse is closest + for _, resource in ipairs(resources) do + local dx = px - resource.bounding_box.left_top.x + local dy = py - resource.bounding_box.left_top.y + local distance = (dx * dx) + (dy * dy) + if distance < closest_distance then + closest_distance = distance + closest_recourse = resource + end + end + + -- Set the item name and icon + if closest_recourse then + item_name = closest_recourse.name:gsub("^%l", string.upper):gsub("-", " ") -- remove dashes and making first letter capital + local product = closest_recourse.prototype.mineable_properties.products[1] + icon = string.format("[img=%s.%s]", product.type, product.name) + end + end + + -- Rename the station + entity.backer_name = config.station_name + :gsub("__icon__", icon) + :gsub("__item_name__", item_name) + :gsub("__backer_name__", entity.backer_name) + :gsub("__direction__", get_direction(entity.position)) + :gsub("__x__", math.floor(entity.position.x)) + :gsub("__y__", math.floor(entity.position.y)) + end +end + +local e = defines.events + +return { + events = { + [e.on_built_entity] = rename_station, + [e.on_robot_built_entity] = rename_station, + } +} diff --git a/exp_scenario/module/gui/autofill.lua b/exp_scenario/module/gui/autofill.lua index 72da3016..c973a256 100644 --- a/exp_scenario/module/gui/autofill.lua +++ b/exp_scenario/module/gui/autofill.lua @@ -284,7 +284,7 @@ Elements.container = Gui.define("autofill/container") }) -- Setup the player data, this is used by section and item category so needs to be done here - local player = assert(game.get_player(parent.player_index)) + local player = Gui.get_player(parent) --- @type table local player_data = def.data[player] or table.deep_copy(config.default_entities) def.data[player] = player_data diff --git a/exp_scenario/module/gui/module_inserter.lua b/exp_scenario/module/gui/module_inserter.lua index 9cb2bfd4..a39bd391 100644 --- a/exp_scenario/module/gui/module_inserter.lua +++ b/exp_scenario/module/gui/module_inserter.lua @@ -484,7 +484,7 @@ local function on_entity_settings_pasted(event) planner.set_mapper(1, "to", mapper) -- Apply the planner - local player = assert(game.get_player(event.player_index)) + local player = Gui.get_player(event) player.surface.upgrade_area{ area = destination.bounding_box, item = planner, diff --git a/exp_scenario/module/gui/player_bonus.lua b/exp_scenario/module/gui/player_bonus.lua index 34abc22e..82595d71 100644 --- a/exp_scenario/module/gui/player_bonus.lua +++ b/exp_scenario/module/gui/player_bonus.lua @@ -463,7 +463,7 @@ Gui.toolbar.create_button{ --- Recalculate and apply the bonus for a player local function recalculate_bonus(event) - local player = assert(game.get_player(event.player_index)) + local player = Gui.get_player(event) if event.name == Roles.events.on_role_assigned or event.name == Roles.events.on_role_unassigned then -- If the player's roles changed then we will need to recalculate their limit Elements.bonus_used._clear_points_limit_cache(player) diff --git a/exp_scenario/module/locale/en.cfg b/exp_scenario/module/locale/en.cfg index 90bc0736..5c3e2b51 100644 --- a/exp_scenario/module/locale/en.cfg +++ b/exp_scenario/module/locale/en.cfg @@ -375,3 +375,69 @@ caption-set-location=Set type-player=Player type-static=Static type-loop=Loop + +[exp_afk-kick] +kick-message=All players were kicked because everyone was AFK. + +[exp_chat-auto-reply] +chat-reply=[Compilatron] __1__ +chat-disallowed=You can't use global chat commands +reply-online=There are __1__ players online +reply-players=There have been __1__ players on this map +reply-dev=Cooldude2606 is a dev for this server and makes the scenario and is not a factorio dev. +reply-softmod=A softmod is a custom scenario that runs on this server, an example is the player list. +reply-clusterio=Clusterio is a factorio server hosting platform allowing internet enabled mods. +reply-blame=Blame __1__ for what just happened! +reply-afk=You're afk? Look at __1__, that player has been afk for: __2__ +reply-evolution=Current evolution factor is __1__ +reply-magic=Fear the admin magic (ノ゚∀゚)ノ⌒・*:.。. .。.:*・゜゚・*☆ +reply-aids=≖ ‿ ≖ Fear the aids of a public server ≖ ‿ ≖ +reply-riot=(admins) ┬┴┬┴┤ᵒ_ᵒ)├┬┴┬┴ ‹ ‹\(´ω` )/››‹‹\ (  ´)/››‹‹\ ( ´ω`)/›› (rest of server) +reply-loops=NO LOOPS; LOOPS ARE BAD; JUST NO LOOPS!!!!!; IF YOU MAKE A LOOP.... IT WILL NOT END WELL!!!!!!! +reply-lenny=( ͡° ͜ʖ ͡°) +reply-hodor=Hodor +reply-popcorn-1=Heating the oil and waiting for the popping sound... +reply-popcorn-2=__1__ your popcorn is finished. Lean backwards and watch the drama unfold. +reply-snaps-1=Pouring the glasses and finding the correct song book... +reply-snaps-2=Singing a song...🎤🎶 +reply-snaps-3=schkål, my friends! +reply-cocktail-1= 🍸 Inintiating mind reading unit... 🍸 +reply-cocktail-2= 🍸 Mixing favourite ingredients of __1__ 🍸 +reply-cocktail-3=🍸 __1__ your cocktail is done.🍸 +reply-coffee-1= ☕ Boiling the water and grinding the coffee beans... ☕ +reply-coffee-2= ☕ __1__ we ran out of coffe beans! Have some tea instead. ☕ +reply-pizza-1= 🍕 Finding nearest pizza supplier... 🍕 +reply-pizza-2= 🍕 Figuring out the favourite pizza of __1__ 🍕 +reply-pizza-3= 🍕 __1__ your pizza is here! 🍕 +reply-tea-1= ☕ Boiling the water... ☕ +reply-tea-2= ☕ __1__ your tea is done! ☕ +reply-mead-1= Filling the drinking horn +reply-mead-2= Skål! +reply-beer-1= 🍺 Pouring A Glass 🍺 +reply-beer-2= 🍻 Chears Mate 🍻 + +[exp_chat-popup] +flying-text-message=__1__: __2__ +flying-text-ping=You have been mentioned in chat by __1__. + +[exp_damage-popup] +flying-text-health=__1__ +flying-text-damage=__1__ + +[exp_deconstruction-log] +chat-admin=__1__ tried to deconstruct from __2__ to __3__ which has __4__ items. + +[exp_fast-decon] +tooltip-main=Toggle fast tree decon +chat-enabled=enabled +chat-disabled=disabled +chat-toggle=Fast decon has been __1__ + +[exp_nuke-protection] +chat-found=You cannot have __1__ in your inventory, so it was placed into the chests at spawn. + +[exp_protection-jail] +chat-jailed=__1__ was jailed because they removed too many protected entities. Please wait for a moderator. + +[exp_report-jail] +chat-jailed=__1__ was jailed because they were reported too many times. Please wait for a moderator. \ No newline at end of file diff --git a/exp_scenario/module/locale/zh-CN.cfg b/exp_scenario/module/locale/zh-CN.cfg index 2005a62e..8768808a 100644 --- a/exp_scenario/module/locale/zh-CN.cfg +++ b/exp_scenario/module/locale/zh-CN.cfg @@ -375,3 +375,69 @@ caption-set-location=設 type-player=用戶 type-static=靜態 type-loop=循環 + +[exp_afk-kick] +kick-message=因地圖中沒有活躍玩家,所以所有人都已被請離。 + +[exp_chat-auto-reply] +chat-reply=[Compilatron] __1__ +chat-disallowed=你沒有權限使用這個指令。 +reply-online=現在有 __1__ 人上線。 +reply-players=本地圖現在有 __1__ 人曾上線。 +reply-dev=Cooldude2606 只是本場境的開發者 +reply-softmod=這裹用了自設情境。 +reply-clusterio=Clusterio is a factorio server hosting platform allowing internet enabled mods. +reply-blame=責怪 __1__ 吧。 +reply-afk=看看 __1__, 他已掛機 __2__ 。 +reply-evolution=現在敵人進化度為 __1__ 。 +reply-magic=Fear the admin magic (ノ゚∀゚)ノ⌒・*:.。. .。.:*・゜゚・*☆ +reply-aids=≖ ‿ ≖ Fear the aids of a public server ≖ ‿ ≖ +reply-riot=(admins) ┬┴┬┴┤ᵒ_ᵒ)├┬┴┬┴ ‹ ‹\(´ω` )/››‹‹\ (  ´)/››‹‹\ ( ´ω`)/›› (rest of server) +reply-loops=架設迴旋處最終後果都不好。 +reply-lenny=( ͡° ͜ʖ ͡°) +reply-hodor=Hodor +reply-popcorn-1=Heating the oil and waiting for the popping sound... +reply-popcorn-2=__1__ your popcorn is finished. Lean backwards and watch the drama unfold. +reply-snaps-1=Pouring the glasses and finding the correct song book... +reply-snaps-2=Singing a song...🎤🎶 +reply-snaps-3=schkål, my friends! +reply-cocktail-1= 🍸 Inintiating mind reading unit... 🍸 +reply-cocktail-2= 🍸 Mixing favourite ingredients of __1__ 🍸 +reply-cocktail-3=🍸 __1__ your cocktail is done.🍸 +reply-coffee-1= ☕ Boiling the water and grinding the coffee beans... ☕ +reply-coffee-2= ☕ __1__ we ran out of coffe beans! Have some tea instead. ☕ +reply-pizza-1= 🍕 Finding nearest pizza supplier... 🍕 +reply-pizza-2= 🍕 Figuring out the favourite pizza of __1__ 🍕 +reply-pizza-3= 🍕 __1__ your pizza is here! 🍕 +reply-tea-1= ☕ Boiling the water... ☕ +reply-tea-2= ☕ __1__ your tea is done! ☕ +reply-mead-1= Filling the drinking horn +reply-mead-2= Skål! +reply-beer-1= 🍺 Pouring A Glass 🍺 +reply-beer-2= 🍻 Chears Mate 🍻 + +[exp_chat-popup] +flying-text-message=__1__: __2__ +flying-text-ping=__1__ 在信息中提到了你。 + +[exp_damage-popup] +flying-text-health=__1__ +flying-text-damage=__1__ + +[exp_deconstruction-log] +chat-admin=__1__ 試圖拆除在 __2__ 到 __3__ ,有 __4__ 個物品。 + +[exp_fast-decon] +tooltip-main=樹木快速拆除選項 +chat-enabled=啟用 +chat-disabled=停用 +chat-toggle=樹木快速拆除已 __1__ + +[exp_nuke-protection] +chat-found=你的用戶組不允許你有 __1__ ,所以該物品已放在出生點的箱子。 + +[exp_protection-jail] +chat-jailed=__1__ 因被多次拆除受保護物體而被禁止行動。請等候管理員作出下一步處理。 + +[exp_report-jail] +chat-jailed=__1__ 因被多次舉報而被禁止行動。請等候管理員作出下一步處理。 diff --git a/exp_scenario/module/locale/zh-TW.cfg b/exp_scenario/module/locale/zh-TW.cfg index 2005a62e..8768808a 100644 --- a/exp_scenario/module/locale/zh-TW.cfg +++ b/exp_scenario/module/locale/zh-TW.cfg @@ -375,3 +375,69 @@ caption-set-location=設 type-player=用戶 type-static=靜態 type-loop=循環 + +[exp_afk-kick] +kick-message=因地圖中沒有活躍玩家,所以所有人都已被請離。 + +[exp_chat-auto-reply] +chat-reply=[Compilatron] __1__ +chat-disallowed=你沒有權限使用這個指令。 +reply-online=現在有 __1__ 人上線。 +reply-players=本地圖現在有 __1__ 人曾上線。 +reply-dev=Cooldude2606 只是本場境的開發者 +reply-softmod=這裹用了自設情境。 +reply-clusterio=Clusterio is a factorio server hosting platform allowing internet enabled mods. +reply-blame=責怪 __1__ 吧。 +reply-afk=看看 __1__, 他已掛機 __2__ 。 +reply-evolution=現在敵人進化度為 __1__ 。 +reply-magic=Fear the admin magic (ノ゚∀゚)ノ⌒・*:.。. .。.:*・゜゚・*☆ +reply-aids=≖ ‿ ≖ Fear the aids of a public server ≖ ‿ ≖ +reply-riot=(admins) ┬┴┬┴┤ᵒ_ᵒ)├┬┴┬┴ ‹ ‹\(´ω` )/››‹‹\ (  ´)/››‹‹\ ( ´ω`)/›› (rest of server) +reply-loops=架設迴旋處最終後果都不好。 +reply-lenny=( ͡° ͜ʖ ͡°) +reply-hodor=Hodor +reply-popcorn-1=Heating the oil and waiting for the popping sound... +reply-popcorn-2=__1__ your popcorn is finished. Lean backwards and watch the drama unfold. +reply-snaps-1=Pouring the glasses and finding the correct song book... +reply-snaps-2=Singing a song...🎤🎶 +reply-snaps-3=schkål, my friends! +reply-cocktail-1= 🍸 Inintiating mind reading unit... 🍸 +reply-cocktail-2= 🍸 Mixing favourite ingredients of __1__ 🍸 +reply-cocktail-3=🍸 __1__ your cocktail is done.🍸 +reply-coffee-1= ☕ Boiling the water and grinding the coffee beans... ☕ +reply-coffee-2= ☕ __1__ we ran out of coffe beans! Have some tea instead. ☕ +reply-pizza-1= 🍕 Finding nearest pizza supplier... 🍕 +reply-pizza-2= 🍕 Figuring out the favourite pizza of __1__ 🍕 +reply-pizza-3= 🍕 __1__ your pizza is here! 🍕 +reply-tea-1= ☕ Boiling the water... ☕ +reply-tea-2= ☕ __1__ your tea is done! ☕ +reply-mead-1= Filling the drinking horn +reply-mead-2= Skål! +reply-beer-1= 🍺 Pouring A Glass 🍺 +reply-beer-2= 🍻 Chears Mate 🍻 + +[exp_chat-popup] +flying-text-message=__1__: __2__ +flying-text-ping=__1__ 在信息中提到了你。 + +[exp_damage-popup] +flying-text-health=__1__ +flying-text-damage=__1__ + +[exp_deconstruction-log] +chat-admin=__1__ 試圖拆除在 __2__ 到 __3__ ,有 __4__ 個物品。 + +[exp_fast-decon] +tooltip-main=樹木快速拆除選項 +chat-enabled=啟用 +chat-disabled=停用 +chat-toggle=樹木快速拆除已 __1__ + +[exp_nuke-protection] +chat-found=你的用戶組不允許你有 __1__ ,所以該物品已放在出生點的箱子。 + +[exp_protection-jail] +chat-jailed=__1__ 因被多次拆除受保護物體而被禁止行動。請等候管理員作出下一步處理。 + +[exp_report-jail] +chat-jailed=__1__ 因被多次舉報而被禁止行動。請等候管理員作出下一步處理。 diff --git a/exp_server_ups/module/control.lua b/exp_server_ups/module/control.lua index be5be941..b39eab29 100644 --- a/exp_server_ups/module/control.lua +++ b/exp_server_ups/module/control.lua @@ -57,7 +57,11 @@ end --- @param player LuaPlayer --- @return LuaGuiElement function Elements.server_ups.get_main_label(player) - return Elements.server_ups.data[player] or Elements.server_ups(player.gui.screen, UsesServerUps:get(player)) + local element = Elements.server_ups.data[player] + if element and element.valid then + return element + end + return Elements.server_ups(player.gui.screen, UsesServerUps:get(player)) end --- Set the visible state of the main label diff --git a/exp_util/module/async.lua b/exp_util/module/async.lua index b31554f6..9afaa377 100644 --- a/exp_util/module/async.lua +++ b/exp_util/module/async.lua @@ -109,7 +109,8 @@ Async._function_metatable = { --- @field tick number? If present, the function will be called on this game tick --- @field next_id number? The id of the async function to be called with the return value --- @field canceled boolean? True if the call has been canceled ---- @field returned any[]? The return values of the function call +--- @field completed boolean? True if the call has completed +--- @field return_values any? The return values of the function call Async._return_prototype = {} -- Prototype of the async return type Async._return_metatable = { @@ -164,8 +165,8 @@ end --- @param async_func Async.AsyncFunction The function which will be called using start_soon function Async._return_prototype:return_to(async_func) self.next_id = async_func.id - if self.returned then - async_func(table.unpack(self.returned)) + if self.completed then + async_func(table.unpack(self.return_values)) end end @@ -239,7 +240,8 @@ function Async._function_prototype:start_now(...) return setmetatable({ func_id = self.id, args = { ... }, - returned = rtn1, + completed = true, + return_values = rtn1, }, Async._return_metatable) else error("Async function " .. self.id .. " returned an invalid status: " .. table.inspect(status)) @@ -315,7 +317,8 @@ local function exec(pending, tick) elseif status == Async.status.complete or status == nil then -- The function has finished execution, raise the custom event Async._queue_pressure[pending.func_id] = Async._queue_pressure[pending.func_id] - 1 - pending.returned = rtn1 + pending.return_values = rtn1 or {} + pending.completed = true if pending.next_id then resolve_next[#resolve_next + 1] = setmetatable({ func_id = pending.next_id,