mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2025-12-27 03:25:23 +09:00
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
This commit is contained in:
@@ -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",
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
|
||||
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"] = {
|
||||
spawn_position = { x = 0, y = 0 },
|
||||
spawn_surface = "nauvis",
|
||||
entity_name = "small-biter",
|
||||
messages = {
|
||||
{ "info.website" },
|
||||
{ "info.read-readme" },
|
||||
{ "info.discord" },
|
||||
@@ -19,5 +20,6 @@ return {
|
||||
{ "info.github" },
|
||||
{ "info.patreon" },
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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__ 個物品。
|
||||
|
||||
@@ -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 來停用。
|
||||
|
||||
@@ -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__ 個物品。
|
||||
|
||||
@@ -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 來停用。
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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("<inline>", "", 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"] = "<inline>" .. append_playtime(player_name),
|
||||
["Entity"] = "<inline>" .. 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"] = "<inline>" .. append_playtime(player_name),
|
||||
["By"] = "<inline>" .. append_playtime(by_player_name),
|
||||
["Report Count"] = "<inline>" .. 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"] = "<inline>" .. player_name,
|
||||
["By"] = "<inline>" .. event.removed_by_name,
|
||||
["Report Count"] = "<inline>" .. 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"] = "<inline>" .. player_name,
|
||||
["By"] = "<inline>" .. by_player_name,
|
||||
["Warning Count"] = "<inline>" .. 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"] = "<inline>" .. player_name,
|
||||
["By"] = "<inline>" .. event.removed_by_name,
|
||||
["Warning Count"] = "<inline>" .. 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"] = "<inline>" .. player_name,
|
||||
["By"] = "<inline>" .. 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"] = "<inline>" .. player_name,
|
||||
["By"] = "<inline>" .. 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"] = "<inline>" .. event.player_name,
|
||||
["By"] = "<inline>" .. 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"] = "<inline>" .. event.player_name,
|
||||
["By"] = "<inline>" .. 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"] = "<inline>" .. 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"] = "<inline>" .. 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"] = "<inline>" .. player_name,
|
||||
["By"] = "<inline>" .. 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"] = "<inline>" .. 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"] = "<inline>" .. 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"] = "<inline>" .. player_name,
|
||||
["Details"] = event.parameters ~= "" and event.parameters or nil,
|
||||
}
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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, "<protection>", "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)
|
||||
@@ -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, "<reports>", "Reported by too many players, please wait for a moderator.")
|
||||
game.print{ "report-jail.jail", player_name_color }
|
||||
end
|
||||
end)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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"))
|
||||
|
||||
84
exp_scenario/module/control/afk_kick.lua
Normal file
84
exp_scenario/module/control/afk_kick.lua
Normal file
@@ -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,
|
||||
}
|
||||
63
exp_scenario/module/control/chat_auto_reply.lua
Normal file
63
exp_scenario/module/control/chat_auto_reply.lua
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
48
exp_scenario/module/control/chat_popup.lua
Normal file
48
exp_scenario/module/control/chat_popup.lua
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
64
exp_scenario/module/control/custom_start.lua
Normal file
64
exp_scenario/module/control/custom_start.lua
Normal file
@@ -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,
|
||||
}
|
||||
50
exp_scenario/module/control/damage_popups.lua
Normal file
50
exp_scenario/module/control/damage_popups.lua
Normal file
@@ -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,
|
||||
},
|
||||
}
|
||||
157
exp_scenario/module/control/death_markers.lua
Normal file
157
exp_scenario/module/control/death_markers.lua
Normal file
@@ -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<number, CorpseData>
|
||||
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,
|
||||
}
|
||||
181
exp_scenario/module/control/deconstruction_log.lua
Normal file
181
exp_scenario/module/control/deconstruction_log.lua
Normal file
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
332
exp_scenario/module/control/discord_alerts.lua
Normal file
332
exp_scenario/module/control/discord_alerts.lua
Normal file
@@ -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 "<no details>" },
|
||||
},
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
events = events,
|
||||
}
|
||||
83
exp_scenario/module/control/extra_logging.lua
Normal file
83
exp_scenario/module/control/extra_logging.lua
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
181
exp_scenario/module/control/fast_deconstruction.lua
Normal file
181
exp_scenario/module/control/fast_deconstruction.lua
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
100
exp_scenario/module/control/help_bubbles.lua
Normal file
100
exp_scenario/module/control/help_bubbles.lua
Normal file
@@ -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<string, Async.AsyncReturn>
|
||||
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,
|
||||
}
|
||||
34
exp_scenario/module/control/inserter_pickup.lua
Normal file
34
exp_scenario/module/control/inserter_pickup.lua
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
27
exp_scenario/module/control/inventory_clear.lua
Normal file
27
exp_scenario/module/control/inventory_clear.lua
Normal file
@@ -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,
|
||||
}
|
||||
231
exp_scenario/module/control/mine_depletion.lua
Normal file
231
exp_scenario/module/control/mine_depletion.lua
Normal file
@@ -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,
|
||||
},
|
||||
}
|
||||
56
exp_scenario/module/control/nuke_protection.lua
Normal file
56
exp_scenario/module/control/nuke_protection.lua
Normal file
@@ -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,
|
||||
}
|
||||
27
exp_scenario/module/control/pollution_grading.lua
Normal file
27
exp_scenario/module/control/pollution_grading.lua
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
47
exp_scenario/module/control/protection_jail.lua
Normal file
47
exp_scenario/module/control/protection_jail.lua
Normal file
@@ -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<number, number>
|
||||
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, "<protection>", "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,
|
||||
}
|
||||
}
|
||||
38
exp_scenario/module/control/report_jail.lua
Normal file
38
exp_scenario/module/control/report_jail.lua
Normal file
@@ -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, "<reports>", "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,
|
||||
}
|
||||
}
|
||||
292
exp_scenario/module/control/spawn_area.lua
Normal file
292
exp_scenario/module/control/spawn_area.lua
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
104
exp_scenario/module/control/station_auto_name.lua
Normal file
104
exp_scenario/module/control/station_auto_name.lua
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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<string, ExpGui_Autofill.entity_settings>
|
||||
local player_data = def.data[player] or table.deep_copy(config.default_entities)
|
||||
def.data[player] = player_data
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
@@ -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__ 因被多次舉報而被禁止行動。請等候管理員作出下一步處理。
|
||||
|
||||
@@ -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__ 因被多次舉報而被禁止行動。請等候管理員作出下一步處理。
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user