mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-12 15:54:52 +09:00
Address the second inline review of the features framework
- Feature.register returns the config as a second value, so files write `local feature, config = Feature.register(...)`. - The lib is ExpScenario.Features and the instance class with its fields sits on Feature._prototype, which is the __index, the same shape as exp_commands. - update_config only looks at values whose override was added, removed, or replaced. _apply_field sets one value and returns it, so the comparison happens while applying and the second loop only raises. Overrides dropped from the previous set count as candidates because the value reverts to its default. - The `@setting name` tags are gone from the config comments. - Protection is guarded as a whole instead of checking config.enabled in one handler. The handlers also forget mined entities, so get_entities and is_entity_protected now drop invalid references, which also covers entities destroyed without an event. The list getters are gone, the selection overlay builds its lists from Protection.config. - Chat and damage popups are one feature in control/popups.lua. - ignore_admins and command_admin_only are removed, roles cover them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
bff6a1c612
commit
f90b8fcee1
21 files changed
+289
-286
No files matched your search
@@ -86,7 +86,6 @@ export const features: Feature[] = [
|
||||
description: "Reply to key words in chat and run chat commands",
|
||||
fields: [
|
||||
str("command_prefix", "Command prefix", "Prefix for chat commands, and for key words to reply to everyone", "!"),
|
||||
bool("command_admin_only", "Commands for admins only", "Only admins can use chat commands", false),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -142,7 +141,6 @@ export const features: Feature[] = [
|
||||
title: "Nuke Protection",
|
||||
description: "Remove banned items from players without the nuke protection bypass permission",
|
||||
fields: [
|
||||
bool("ignore_admins", "Ignore admins", "Admins can hold banned items", true),
|
||||
list("banned_items", "Banned items", "Items which are moved to a chest at spawn", ["atomic-bomb"]),
|
||||
],
|
||||
},
|
||||
@@ -163,7 +161,6 @@ export const features: Feature[] = [
|
||||
title: "Entity Protection",
|
||||
description: "Stop players removing protected entities placed by others, players with the bypass permission are ignored",
|
||||
fields: [
|
||||
bool("ignore_admins", "Ignore admins", "Admins can remove protected entities", true),
|
||||
num("repeat_count", "Repeat count", "Protected entities removed within the repeat time which count as a repeat violation", 5, 1),
|
||||
num("repeat_minutes", "Repeat time", "How long removed protected entities are remembered", 20, 1, "minutes"),
|
||||
list("always_protected_names", "Protected entity names", "Entities which are always protected", []),
|
||||
|
||||
@@ -8,10 +8,9 @@ local Feature = require("modules/exp_scenario/features")
|
||||
local Selection = require("modules/exp_util/selection")
|
||||
local SelectArea = Selection.connect("ExpCommand_Lawnmower")
|
||||
|
||||
local feature = Feature.register("lawnmower", {
|
||||
destroy_decoratives = false, --- @setting destroy_decoratives remove decoratives under entities when they are built
|
||||
local feature, config = Feature.register("lawnmower", {
|
||||
destroy_decoratives = false, -- remove decoratives under entities when they are built
|
||||
})
|
||||
local config = feature.config
|
||||
|
||||
--- @class ExpCommand_Lawnmower.commands
|
||||
local commands = {}
|
||||
|
||||
@@ -153,6 +153,17 @@ SelectArea:on_alt_selection(function(event)
|
||||
end
|
||||
end)
|
||||
|
||||
--- The keys of a set as a list, find_entities_filtered takes a list of names
|
||||
--- @param set table<string, true>
|
||||
--- @return string[]
|
||||
local function set_keys(set)
|
||||
local keys = {}
|
||||
for key in pairs(set) do
|
||||
keys[#keys + 1] = key
|
||||
end
|
||||
return keys
|
||||
end
|
||||
|
||||
--- When selection starts show all protected entities and protected areas
|
||||
local function on_player_selection_start(event)
|
||||
local player = game.players[event.player_index]
|
||||
@@ -166,7 +177,7 @@ local function on_player_selection_start(event)
|
||||
end
|
||||
|
||||
-- Show always protected entities by name
|
||||
local protected_entity_names = EntityProtection.get_protected_entity_names()
|
||||
local protected_entity_names = set_keys(EntityProtection.config.always_protected_names)
|
||||
if #protected_entity_names > 0 then
|
||||
for _, entity in pairs(surface.find_entities_filtered{ name = protected_entity_names, force = player.force }) do
|
||||
show_protected_entity(player, entity)
|
||||
@@ -174,7 +185,7 @@ local function on_player_selection_start(event)
|
||||
end
|
||||
|
||||
-- Show always protected entities by type
|
||||
local protected_entity_types = EntityProtection.get_protected_entity_types()
|
||||
local protected_entity_types = set_keys(EntityProtection.config.always_protected_types)
|
||||
if #protected_entity_types > 0 then
|
||||
for _, entity in pairs(surface.find_entities_filtered{ type = protected_entity_types, force = player.force }) do
|
||||
show_protected_entity(player, entity)
|
||||
|
||||
@@ -27,7 +27,7 @@ local afk_time_units = {
|
||||
|
||||
return Feature.register("chat_auto_reply", {
|
||||
--- @type table<string, LocalisedString | fun(player: LuaPlayer, is_command: boolean): LocalisedString>
|
||||
messages = { --- @setting messages will trigger when ever the word is said
|
||||
messages = { -- will trigger when ever the word is said
|
||||
["discord"] = { "info.discord", config_server_detail["discord"] },
|
||||
["expgaming"] = { "info.website", config_server_detail["website"] },
|
||||
["website"] = { "info.website", config_server_detail["website"] },
|
||||
@@ -65,11 +65,10 @@ return Feature.register("chat_auto_reply", {
|
||||
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_prefix = "!", --- @setting command_prefix prefix used for commands below and to print to all players (if enabled above)
|
||||
allow_command_prefix_for_messages = true, -- when true any message trigger will print to all player when prefixed
|
||||
command_prefix = "!", -- prefix used for commands below and to print to all players (if enabled above)
|
||||
--- @type table<string, LocalisedString | fun(player: LuaPlayer, is_command: boolean): LocalisedString>
|
||||
commands = { --- @setting commands will trigger only when command prefix is given
|
||||
commands = { -- will trigger only when command prefix is given
|
||||
["dev"] = { "exp_chat-auto-reply.reply-dev" },
|
||||
["magic"] = { "exp_chat-auto-reply.reply-magic" },
|
||||
["aids"] = { "exp_chat-auto-reply.reply-aids" },
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
--- Popups for chat and damage, shared by control/chat_popup.lua and control/damage_popups.lua
|
||||
-- @config Popups
|
||||
|
||||
local Feature = require("modules/exp_scenario/features")
|
||||
|
||||
return Feature.register("popups", {
|
||||
show_player_messages = true, --- @setting show_player_messages weather a message in chat will make a popup above them
|
||||
show_player_mentions = true, --- @setting show_player_mentions weather a mentioned player will have a popup when mentioned in chat
|
||||
show_player_damage = true, --- @setting show_player_damage weather to show damage done by players
|
||||
show_player_health = true, --- @setting show_player_health weather to show player health when attacked
|
||||
damage_location_variance = 0.8, --- @setting damage_location_variance how close to the eade of an entity the popups will appear
|
||||
})
|
||||
@@ -4,39 +4,39 @@
|
||||
local Feature = require("modules/exp_scenario/features")
|
||||
|
||||
return Feature.register("spawn_area", {
|
||||
spawn_area = { --- @setting spawn_area Settings relating to the whole spawn area
|
||||
spawn_area = { -- Settings relating to the whole spawn area
|
||||
-- Enable predefined patches: 128, else: 32
|
||||
deconstruction_radius = 20, -- @setting deconstruction_radius All entities within this radius will be removed
|
||||
deconstruction_radius = 20, -- All entities within this radius will be removed
|
||||
tile_radius = 20,
|
||||
deconstruction_tile = "concrete", --- @setting deconstruction_tile Tile to be placed in the deconstruction radius, use nil for map gen
|
||||
landfill_radius = 50, --- @setting pattern_radius All water within this radius will be land filled
|
||||
deconstruction_tile = "concrete", -- Tile to be placed in the deconstruction radius, use nil for map gen
|
||||
landfill_radius = 50, -- All water within this radius will be land filled
|
||||
},
|
||||
turrets = { --- @setting turrets Settings relating to adding turrets to spawn
|
||||
enabled = true, --- @setting enabled Whether turrets will be added to spawn
|
||||
ammo_type = "uranium-rounds-magazine", --- @setting ammo_type The ammo type that will be used during refills
|
||||
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
|
||||
turrets = { -- Settings relating to adding turrets to spawn
|
||||
enabled = true, -- Whether turrets will be added to spawn
|
||||
ammo_type = "uranium-rounds-magazine", -- The ammo type that will be used during refills
|
||||
offset = { x = 0, y = 0 }, -- The position offset to apply to turrets
|
||||
locations = { -- The locations of all turrets, this list can change during runtime
|
||||
{ -3, -3 },
|
||||
{ 3, -3 },
|
||||
{ -3, 3 },
|
||||
{ 3, 3 },
|
||||
},
|
||||
},
|
||||
afk_belts = { --- @setting afk_belts Settings relating to adding afk belts to spawn
|
||||
enabled = true, --- @setting enabled Whether afk belts will be added to spawn
|
||||
belt_type = "transport-belt", --- @setting belt_type The belt to be used as afk belts
|
||||
protected = true, --- @setting protected Whether belts will be protected from player interaction
|
||||
offset = { x = 0, y = 0 }, --- @setting offset The position offset to apply to afk belts
|
||||
locations = { --- @setting locations The locations to spawn afk belts at, given as the top left position
|
||||
afk_belts = { -- Settings relating to adding afk belts to spawn
|
||||
enabled = true, -- Whether afk belts will be added to spawn
|
||||
belt_type = "transport-belt", -- The belt to be used as afk belts
|
||||
protected = true, -- Whether belts will be protected from player interaction
|
||||
offset = { x = 0, y = 0 }, -- The position offset to apply to afk belts
|
||||
locations = { -- The locations to spawn afk belts at, given as the top left position
|
||||
{ -5, -5 }, { 5, -5 },
|
||||
{ -5, 5 }, { 5, 5 },
|
||||
},
|
||||
},
|
||||
water = { --- @setting water Settings relating to adding water to spawn
|
||||
enabled = true, --- @setting enabled Whether water tiles will be added to spawn
|
||||
water_tile = "water-mud", --- @setting water_tile The tile to be used as the water tile
|
||||
offset = { x = 0, y = 0 }, --- @setting offset The position offset to apply to water tiles
|
||||
locations = { --- @setting locations The location of the water tiles {x,y}
|
||||
water = { -- Settings relating to adding water to spawn
|
||||
enabled = true, -- Whether water tiles will be added to spawn
|
||||
water_tile = "water-mud", -- The tile to be used as the water tile
|
||||
offset = { x = 0, y = 0 }, -- The position offset to apply to water tiles
|
||||
locations = { -- The location of the water tiles {x,y}
|
||||
-- Each is a 3x3 with the closest tile to 0,0 removed
|
||||
{ 7, 8 }, { 7, 9 }, { 8, 7 }, { 8, 8 }, { 8, 9 }, { 9, 7 }, { 9, 8 }, { 9, 9 }, -- Bottom Right
|
||||
{ 7, -9 }, { 7, -10 }, { 8, -8 }, { 8, -9 }, { 8, -10 }, { 9, -8 }, { 9, -9 }, { 9, -10 }, -- Top Right
|
||||
@@ -44,12 +44,12 @@ return Feature.register("spawn_area", {
|
||||
{ -8, 8 }, { -8, 9 }, { -9, 7 }, { -9, 8 }, { -9, 9 }, { -10, 7 }, { -10, 8 }, { -10, 9 }, -- Bottom Left
|
||||
},
|
||||
},
|
||||
entities = { --- @setting entities Settings relating to adding entities to spawn
|
||||
enabled = true, --- @setting enabled Whether entities will be added to spawn
|
||||
protected = true, --- @setting protected Whether entities will be protected from player interaction
|
||||
operable = true, --- @setting operable Whether entities can be opened by players, must be true if chests are used
|
||||
offset = { x = 0, y = -2 }, --- @setting offset The position offset to apply to entities
|
||||
locations = { --- @setting locations The location and names of entities {name,x,y}
|
||||
entities = { -- Settings relating to adding entities to spawn
|
||||
enabled = true, -- Whether entities will be added to spawn
|
||||
protected = true, -- Whether entities will be protected from player interaction
|
||||
operable = true, -- Whether entities can be opened by players, must be true if chests are used
|
||||
offset = { x = 0, y = -2 }, -- The position offset to apply to entities
|
||||
locations = { -- The location and names of entities {name,x,y}
|
||||
{ "stone-wall", -10, -5 }, { "stone-wall", -10, -4 }, { "stone-wall", -10, -3 }, { "stone-wall", -10, -2 }, { "stone-wall", -10, -1 }, { "stone-wall", -10, 0 }, { "stone-wall", -10, 3 }, { "stone-wall", -10, 4 }, { "stone-wall", -10, 5 },
|
||||
{ "stone-wall", -10, 6 }, { "stone-wall", -10, 7 }, { "stone-wall", -10, 8 }, { "small-lamp", -8, -4 }, { "small-lamp", -8, -1 }, { "iron-chest", -8, 0 }, { "iron-chest", -8, 3 }, { "small-lamp", -8, 4 },
|
||||
{ "small-lamp", -8, 7 }, { "stone-wall", -7, -8 }, { "small-electric-pole", -7, -2 }, { "iron-chest", -7, 0 }, { "iron-chest", -7, 3 }, { "small-electric-pole", -7, 5 }, { "stone-wall", -7, 11 }, { "stone-wall", -6, -8 }, { "small-lamp", -6, -6 },
|
||||
@@ -67,10 +67,10 @@ return Feature.register("spawn_area", {
|
||||
},
|
||||
},
|
||||
pattern = {
|
||||
enabled = true, --- @setting enabled Whether pattern tiles will be added to spawn
|
||||
pattern_tile = "stone-path", --- @setting pattern_tile The tile to be used for the pattern
|
||||
offset = { x = 0, y = -2 }, --- @setting offset The position offset to apply to pattern tiles
|
||||
locations = { --- @setting locations The location of the pattern tiles {x,y}
|
||||
enabled = true, -- Whether pattern tiles will be added to spawn
|
||||
pattern_tile = "stone-path", -- The tile to be used for the pattern
|
||||
offset = { x = 0, y = -2 }, -- The position offset to apply to pattern tiles
|
||||
locations = { -- The location of the pattern tiles {x,y}
|
||||
{ -49, -3 }, { -49, -2 }, { -49, 1 }, { -49, 2 }, { -49, 5 }, { -49, 6 }, { -48, -4 }, { -48, -3 }, { -48, -2 }, { -48, 1 }, { -48, 2 }, { -48, 5 }, { -48, 6 }, { -48, 7 }, { -47, -7 }, { -47, -6 }, { -47, -5 }, { -47, -4 }, { -47, -3 }, { -47, -2 }, { -47, 5 }, { -47, 6 }, { -47, 7 }, { -47, 8 }, { -47, 9 }, { -47, 10 }, { -46, -8 }, { -46, -7 }, { -46, -6 }, { -46, -5 },
|
||||
{ -46, -4 }, { -46, -3 }, { -46, -2 }, { -46, -1 }, { -46, 4 }, { -46, 5 }, { -46, 6 }, { -46, 7 }, { -46, 8 }, { -46, 9 }, { -46, 10 }, { -46, 11 }, { -45, -17 }, { -45, -16 }, { -45, -15 }, { -45, -14 }, { -45, -13 }, { -45, -12 }, { -45, -9 }, { -45, -8 }, { -45, -7 }, { -45, -2 }, { -45, -1 }, { -45, 0 }, { -45, 1 }, { -45, 2 }, { -45, 3 }, { -45, 4 }, { -45, 5 }, { -45, 10 },
|
||||
{ -45, 11 }, { -45, 12 }, { -45, 15 }, { -45, 16 }, { -45, 17 }, { -45, 18 }, { -45, 19 }, { -45, 20 }, { -44, -18 }, { -44, -17 }, { -44, -16 }, { -44, -15 }, { -44, -14 }, { -44, -13 }, { -44, -12 }, { -44, -9 }, { -44, -8 }, { -44, -1 }, { -44, 0 }, { -44, 1 }, { -44, 2 }, { -44, 3 }, { -44, 4 }, { -44, 11 }, { -44, 12 }, { -44, 15 }, { -44, 16 }, { -44, 17 }, { -44, 18 }, { -44, 19 },
|
||||
|
||||
@@ -43,9 +43,7 @@ require("modules/exp_scenario/commands/waterfill")
|
||||
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"))
|
||||
@@ -59,6 +57,7 @@ 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/popups"))
|
||||
add(require("modules/exp_scenario/control/protection"))
|
||||
add(require("modules/exp_scenario/control/protection_jail"))
|
||||
add(require("modules/exp_scenario/control/report_jail"))
|
||||
|
||||
@@ -6,15 +6,14 @@ local Async = require("modules/exp_util/async")
|
||||
local Feature = require("modules/exp_scenario/features")
|
||||
local Roles = require("modules/exp_roles")
|
||||
local Storage = require("modules/exp_util/storage")
|
||||
local feature = Feature.register("afk_kick", {
|
||||
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_id = Feature.optional("number"), --- @setting active_role_id Players with this role or higher are treated as active regardless of afk time
|
||||
afk_minutes = 10, --- @setting afk_minutes The time in minutes that must pass for a player to be considered afk
|
||||
kick_minutes = 30, --- @setting kick_minutes The time in minutes that must pass without any active players for all players to be kicked
|
||||
trust_minutes = 600, --- @setting trust_minutes The time in minutes that a player must be online for to count as trusted
|
||||
local feature, config = Feature.register("afk_kick", {
|
||||
admin_as_active = true, -- When true admins will be treated as active regardless of afk time
|
||||
trust_as_active = true, -- When true trusted players (by playtime) will be treated as active regardless of afk time
|
||||
active_role_id = Feature.optional("number"), -- Players with this role or higher are treated as active regardless of afk time
|
||||
afk_minutes = 10, -- The time in minutes that must pass for a player to be considered afk
|
||||
kick_minutes = 30, -- The time in minutes that must pass without any active players for all players to be kicked
|
||||
trust_minutes = 600, -- The time in minutes that a player must be online for to count as trusted
|
||||
})
|
||||
local config = feature.config
|
||||
|
||||
local ticks_per_minute = 3600
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ local function on_console_chat(event)
|
||||
|
||||
-- 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 not Roles.player_has_permission(player, "exp_scenario.chat.commands") then commands_allowed = false end
|
||||
|
||||
-- Check if a key word appears in the message
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
--[[-- Control - Chat Popup
|
||||
Creates flying text entities when a player sends a message in chat
|
||||
]]
|
||||
|
||||
local FlyingText = require("modules/exp_util/flying_text")
|
||||
local Feature = require("modules/exp_scenario/features")
|
||||
local feature = require("modules/exp_scenario/config/popups")
|
||||
local config = feature.config
|
||||
|
||||
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 feature:guard{
|
||||
events = {
|
||||
[e.on_console_chat] = on_console_chat,
|
||||
},
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
--[[-- 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 Feature = require("modules/exp_scenario/features")
|
||||
local feature = require("modules/exp_scenario/config/popups")
|
||||
local config = feature.config
|
||||
|
||||
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_position = entity.position
|
||||
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 = assert(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 feature:guard{
|
||||
events = {
|
||||
[e.on_entity_damaged] = on_entity_damaged,
|
||||
},
|
||||
}
|
||||
@@ -6,16 +6,15 @@ local ExpUtil = require("modules/exp_util")
|
||||
local Feature = require("modules/exp_scenario/features")
|
||||
local Storage = require("modules/exp_util/storage")
|
||||
|
||||
local feature = Feature.register("death_markers", {
|
||||
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, --- @setting clean_map_markers removes the map marker once the body is gone
|
||||
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
|
||||
local feature, config = Feature.register("death_markers", {
|
||||
collect_corpses = true, -- enables items being returned to the spawn point in chests upon corpse expiring
|
||||
show_map_markers = true, -- shows markers on the map where bodies are
|
||||
clean_map_markers = false, -- removes the map marker once the body is gone
|
||||
include_time_of_death = true, -- weather to include the time of death on the map marker
|
||||
map_icon = nil, -- the icon that the map marker shows; nil means no icon; format as a SingleID
|
||||
show_light_at_corpse = true, -- if a light should be rendered at the corpse
|
||||
show_line_to_corpse = true, -- if a line should be rendered from you to your corpse
|
||||
})
|
||||
local config = feature.config
|
||||
|
||||
local map_tag_time_format = ExpUtil.format_time_factory{ format = "short", hours = true, minutes = true }
|
||||
|
||||
|
||||
@@ -8,19 +8,18 @@ local Feature = require("modules/exp_scenario/features")
|
||||
local EntityProtection = require("modules/exp_scenario/control/protection")
|
||||
local Jail = require("modules/exp_scenario/control/jail")
|
||||
|
||||
local feature = Feature.register("discord_alerts", {
|
||||
show_playtime = true, --- @setting show_playtime Add the playtime of players to alerts
|
||||
entity_protection = true, --- @setting entity_protection Alert when a player repeatedly removes protected entities
|
||||
player_bans = true, --- @setting player_bans Alert when a player is banned or unbanned
|
||||
player_mutes = true, --- @setting player_mutes Alert when a player is muted or unmuted
|
||||
player_kicks = true, --- @setting player_kicks Alert when a player is kicked
|
||||
player_promotes = false, --- @setting player_promotes Alert when a player is promoted or demoted
|
||||
player_jail = true, --- @setting player_jail Alert when a player is jailed or unjailed
|
||||
logged_commands = Feature.set{ --- @setting logged_commands Alert when a player uses one of these commands
|
||||
local feature, config = Feature.register("discord_alerts", {
|
||||
show_playtime = true, -- Add the playtime of players to alerts
|
||||
entity_protection = true, -- Alert when a player repeatedly removes protected entities
|
||||
player_bans = true, -- Alert when a player is banned or unbanned
|
||||
player_mutes = true, -- Alert when a player is muted or unmuted
|
||||
player_kicks = true, -- Alert when a player is kicked
|
||||
player_promotes = false, -- Alert when a player is promoted or demoted
|
||||
player_jail = true, -- Alert when a player is jailed or unjailed
|
||||
logged_commands = Feature.set{ -- Alert when a player uses one of these commands
|
||||
"config", "purge", "c", "command", "silent-command", "measured-command", "banlist", "permissions", "editor", "cheat",
|
||||
},
|
||||
})
|
||||
local config = feature.config
|
||||
|
||||
--- Wrap an event handler so it only runs while the alert is enabled
|
||||
--- @param setting string
|
||||
|
||||
@@ -5,12 +5,11 @@ Marks mining drills for deconstruction when resources deplete
|
||||
local Async = require("modules/exp_util/async")
|
||||
local Feature = require("modules/exp_scenario/features")
|
||||
|
||||
local feature = Feature.register("mine_depletion", {
|
||||
fluid = true, --- @setting fluid When true, checks for for fluid pipes when removing miners
|
||||
chest = true, --- @setting chest When true, checks for for chest when removing miners
|
||||
beacon = true, --- @setting beacon When true, checks for for beacon when removing miners
|
||||
local feature, config = Feature.register("mine_depletion", {
|
||||
fluid = true, -- When true, checks for for fluid pipes when removing miners
|
||||
chest = true, -- When true, checks for for chest when removing miners
|
||||
beacon = true, -- When true, checks for for beacon when removing miners
|
||||
})
|
||||
local config = feature.config
|
||||
|
||||
local floor = math.floor
|
||||
|
||||
|
||||
@@ -6,11 +6,9 @@ local ExpUtil = require("modules/exp_util")
|
||||
local Feature = require("modules/exp_scenario/features")
|
||||
local Roles = require("modules/exp_roles")
|
||||
|
||||
local feature = Feature.register("nuke_protection", {
|
||||
ignore_admins = true, --- @setting ignore_admins Admins can hold banned items
|
||||
banned_items = Feature.set{ "atomic-bomb" }, --- @setting banned_items Items which are removed from the inventory of players without the bypass permission
|
||||
local feature, config = Feature.register("nuke_protection", {
|
||||
banned_items = Feature.set{ "atomic-bomb" }, -- Items which are removed from the inventory of players without the bypass permission
|
||||
})
|
||||
local config = feature.config
|
||||
|
||||
--- The inventories which are checked and the events which trigger the check
|
||||
local inventories = {
|
||||
@@ -26,7 +24,6 @@ local inventories = {
|
||||
local function check_items(player, type)
|
||||
-- If the player has perms to be ignored, then they should be
|
||||
if Roles.player_has_permission(player, "exp_scenario.bypass.nuke_protection") then return end
|
||||
if config.ignore_admins and player.admin then return end
|
||||
|
||||
local banned_items = config.banned_items
|
||||
local items = {} --- @type LuaItemStack[]
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
--[[-- Control - Popups
|
||||
Creates flying text for chat messages and for damage dealt to and by players
|
||||
]]
|
||||
|
||||
local FlyingText = require("modules/exp_util/flying_text")
|
||||
local Feature = require("modules/exp_scenario/features")
|
||||
|
||||
local feature, config = Feature.register("popups", {
|
||||
show_player_messages = true, -- weather a message in chat will make a popup above them
|
||||
show_player_mentions = true, -- weather a mentioned player will have a popup when mentioned in chat
|
||||
show_player_damage = true, -- weather to show damage done by players
|
||||
show_player_health = true, -- weather to show player health when attacked
|
||||
damage_location_variance = 0.8, -- how close to the eade of an entity the popups will appear
|
||||
})
|
||||
|
||||
local lower = string.lower
|
||||
local find = string.find
|
||||
local random = math.random
|
||||
local floor = math.floor
|
||||
local max = math.max
|
||||
|
||||
--- 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
|
||||
|
||||
--- 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_position = entity.position
|
||||
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 = assert(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 feature:guard{
|
||||
events = {
|
||||
[e.on_console_chat] = on_console_chat,
|
||||
[e.on_entity_damaged] = on_entity_damaged,
|
||||
},
|
||||
}
|
||||
@@ -6,20 +6,18 @@ local Storage = require("modules/exp_util/storage")
|
||||
local Feature = require("modules/exp_scenario/features")
|
||||
local Roles = require("modules/exp_roles")
|
||||
|
||||
local feature = Feature.register("protection", {
|
||||
ignore_admins = true, --- @setting ignore_admins If admins are ignored by the protection filter
|
||||
repeat_count = 5, --- @setting repeat_count Number of protected entities that must be removed within repeat_minutes in order to trigger repeated removal protection
|
||||
repeat_minutes = 20, --- @setting repeat_minutes The length of time, in minutes, that protected removals will be remembered for
|
||||
always_protected_names = Feature.set{}, --- @setting always_protected_names Names of entities which are always protected
|
||||
always_protected_types = Feature.set{ --- @setting always_protected_types Types of entities which are always protected
|
||||
local feature, config = Feature.register("protection", {
|
||||
repeat_count = 5, -- Number of protected entities that must be removed within repeat_minutes in order to trigger repeated removal protection
|
||||
repeat_minutes = 20, -- The length of time, in minutes, that protected removals will be remembered for
|
||||
always_protected_names = Feature.set{}, -- Names of entities which are always protected
|
||||
always_protected_types = Feature.set{ -- Types of entities which are always protected
|
||||
"boiler", "generator", "offshore-pump", "reactor", "heat-exchanger", "heat-pipe", "fusion-reactor", "fusion-generator", "power-switch", "rocket-silo",
|
||||
},
|
||||
always_trigger_repeat_names = Feature.set{}, --- @setting always_trigger_repeat_names Names of entities which always trigger repeated removal protection
|
||||
always_trigger_repeat_types = Feature.set{ --- @setting always_trigger_repeat_types Types of entities which always trigger repeated removal protection
|
||||
always_trigger_repeat_names = Feature.set{}, -- Names of entities which always trigger repeated removal protection
|
||||
always_trigger_repeat_types = Feature.set{ -- Types of entities which always trigger repeated removal protection
|
||||
"reactor", "fusion-reactor", "rocket-silo",
|
||||
},
|
||||
})
|
||||
local config = feature.config
|
||||
|
||||
local format_string = string.format
|
||||
local floor = math.floor
|
||||
@@ -32,6 +30,8 @@ local Protection = {
|
||||
--- Raised when a player mines protected entities repeatedly, or one which always counts as repeated
|
||||
--- @type EventData.ExpScenario_Protection.on_repeat_violation
|
||||
on_repeat_violation = script.generate_event_name(),
|
||||
--- The config of the feature, read only
|
||||
config = config,
|
||||
--- @package
|
||||
events = {},
|
||||
--- @package
|
||||
@@ -45,29 +45,6 @@ local Protection = {
|
||||
--- @field last uint Tick of the last protected removal
|
||||
--- @field count number Protected removals since the last repeat violation
|
||||
|
||||
--- Get the keys of a set as a list, for the filters of find_entities_filtered
|
||||
--- @param set table<string, true>
|
||||
--- @return string[]
|
||||
local function set_to_list(set)
|
||||
local list = {}
|
||||
for item in pairs(set) do
|
||||
list[#list + 1] = item
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
--- Names of entities which are always protected
|
||||
--- @return string[]
|
||||
function Protection.get_protected_entity_names()
|
||||
return set_to_list(config.always_protected_names)
|
||||
end
|
||||
|
||||
--- Types of entities which are always protected
|
||||
--- @return string[]
|
||||
function Protection.get_protected_entity_types()
|
||||
return set_to_list(config.always_protected_types)
|
||||
end
|
||||
|
||||
local protected_entities = {} --- @type table<uint, table<string, LuaEntity>> Keyed by surface index then entity key
|
||||
local protected_areas = {} --- @type table<uint, table<string, BoundingBox>> Keyed by surface index then area key
|
||||
local repeats = {} --- @type table<string, ExpScenario_Protection.Repeat> Keyed by player name
|
||||
@@ -116,10 +93,17 @@ function Protection.remove_entity(entity)
|
||||
end
|
||||
|
||||
--- Get the protected entities on a surface, always protected entities are not included
|
||||
-- Entities removed while the feature was disabled, or without an event, are forgotten here
|
||||
--- @param surface LuaSurface
|
||||
--- @return table<string, LuaEntity>
|
||||
function Protection.get_entities(surface)
|
||||
return protected_entities[surface.index] or {}
|
||||
local entities = protected_entities[surface.index] or {}
|
||||
for key, entity in pairs(entities) do
|
||||
if not entity.valid then
|
||||
entities[key] = nil
|
||||
end
|
||||
end
|
||||
return entities
|
||||
end
|
||||
|
||||
--- Check if an entity is protected, either directly or by its name or type
|
||||
@@ -129,7 +113,13 @@ function Protection.is_entity_protected(entity)
|
||||
if config.always_protected_names[entity.name] or config.always_protected_types[entity.type] then return true end
|
||||
local entities = protected_entities[entity.surface.index]
|
||||
if not entities then return false end
|
||||
return entities[Protection.get_entity_key(entity)] == entity
|
||||
local key = Protection.get_entity_key(entity)
|
||||
local protected = entities[key]
|
||||
if protected and not protected.valid then
|
||||
entities[key] = nil
|
||||
return false
|
||||
end
|
||||
return protected == entity
|
||||
end
|
||||
|
||||
--- Protect every position within an area
|
||||
@@ -178,12 +168,11 @@ function Protection.is_position_protected(surface, position)
|
||||
return false
|
||||
end
|
||||
|
||||
--- Players are never checked against their own entities, and can be excluded by permission or admin status
|
||||
--- Players are never checked against their own entities, and can be excluded by permission
|
||||
--- @param player LuaPlayer
|
||||
--- @param entity LuaEntity
|
||||
--- @return boolean
|
||||
local function is_ignored(player, entity)
|
||||
if config.ignore_admins and player.admin then return true end
|
||||
if entity.last_user == nil or entity.last_user.index == player.index then return true end
|
||||
if Roles.player_has_permission(player, "exp_scenario.bypass.entity_protection") then return true end
|
||||
return false
|
||||
@@ -216,10 +205,8 @@ end
|
||||
local function on_pre_player_mined_item(event)
|
||||
local entity = event.entity
|
||||
local player = game.players[event.player_index]
|
||||
-- Protected entities are still forgotten while disabled, only the violations stop
|
||||
if
|
||||
config.enabled
|
||||
and not is_ignored(player, entity)
|
||||
not is_ignored(player, entity)
|
||||
and (Protection.is_entity_protected(entity) or Protection.is_position_protected(entity.surface, entity.position))
|
||||
then
|
||||
raise_violation(event, player)
|
||||
@@ -253,4 +240,4 @@ Protection.events[e.on_entity_died] = on_entity_removed
|
||||
Protection.events[e.script_raised_destroy] = on_entity_removed
|
||||
Protection.on_nth_tick[3600 * 5] = clear_old_repeats
|
||||
|
||||
return Protection
|
||||
return feature:guard(Protection)
|
||||
@@ -4,12 +4,11 @@ Automatically name stations when they are placed based on closest resource and d
|
||||
|
||||
local Feature = require("modules/exp_scenario/features")
|
||||
|
||||
local feature = Feature.register("station_auto_name", {
|
||||
--- @setting station_name the name given to new stations, placeholders:
|
||||
local feature, config = Feature.register("station_auto_name", {
|
||||
-- the name given to new stations, placeholders:
|
||||
-- __icon__ __item_name__ __backer_name__ __direction__ __x__ __y__
|
||||
station_name = "[L] __icon__",
|
||||
})
|
||||
local config = feature.config
|
||||
|
||||
local get_direction do
|
||||
local directions = {
|
||||
|
||||
@@ -12,13 +12,12 @@ features.ts.
|
||||
|
||||
--- Registering a feature, small configs live at the top of the file which uses them:
|
||||
local Feature = require("modules/exp_scenario/features")
|
||||
local feature = Feature.register("my_feature", {
|
||||
local feature, config = Feature.register("my_feature", {
|
||||
show_message = true,
|
||||
role_id = Feature.optional("number"),
|
||||
item_names = Feature.set{ "iron-plate" },
|
||||
surface_names = Feature.list{ "nauvis" },
|
||||
})
|
||||
local config = feature.config
|
||||
|
||||
--- Handlers do nothing while the feature is disabled:
|
||||
return feature:guard{
|
||||
@@ -48,11 +47,7 @@ local Storage = require("modules/exp_util/storage")
|
||||
--- @field default any
|
||||
--- @field type string "boolean", "number", "string", "list" or "set" of strings, or a type which can not be overridden
|
||||
|
||||
--- @class ExpScenario.Feature
|
||||
--- @field name string
|
||||
--- @field config table Changed in place when the config is updated
|
||||
--- @field fields table<string, ExpScenario.FeatureField> The values in the config by path
|
||||
--- @field overrides table<string, any> The overrides from the controller by path, restored from storage on load
|
||||
--- @class ExpScenario.Features
|
||||
local Feature = {
|
||||
_registered = {}, --- @type table<string, ExpScenario.Feature>
|
||||
events = {
|
||||
@@ -62,6 +57,14 @@ local Feature = {
|
||||
},
|
||||
}
|
||||
|
||||
--- The methods of every feature, kept apart from the lib so they are not called on it by mistake
|
||||
--- @class ExpScenario.Feature
|
||||
--- @field name string
|
||||
--- @field config table Changed in place when the config is updated
|
||||
--- @field fields table<string, ExpScenario.FeatureField> The values in the config by path
|
||||
--- @field overrides table<string, any> The overrides from the controller by path, restored from storage on load
|
||||
Feature._prototype = {}
|
||||
|
||||
--- @class EventData.ExpScenario.on_config_updated : EventData
|
||||
--- @field feature_name string
|
||||
--- @field path string The path of the value within the config, "enabled" when the feature is enabled or disabled
|
||||
@@ -69,7 +72,7 @@ local Feature = {
|
||||
--- @field new_value any
|
||||
|
||||
local Feature_mt = {
|
||||
__index = Feature,
|
||||
__index = Feature._prototype,
|
||||
}
|
||||
|
||||
--- The overrides of each feature by name, only these are stored because the rest is rebuilt when the file loads
|
||||
@@ -173,7 +176,8 @@ end
|
||||
--- Register a feature and its config, features are enabled unless the config says otherwise
|
||||
--- @param name string
|
||||
--- @param config table?
|
||||
--- @return ExpScenario.Feature
|
||||
--- @return ExpScenario.Feature feature
|
||||
--- @return table config The same table as feature.config
|
||||
function Feature.register(name, config)
|
||||
assert(Feature._registered[name] == nil, "Feature already registered: " .. name)
|
||||
config = config or {}
|
||||
@@ -192,7 +196,7 @@ function Feature.register(name, config)
|
||||
}, Feature_mt) --[[@as ExpScenario.Feature]]
|
||||
|
||||
Feature._registered[name] = feature
|
||||
return feature
|
||||
return feature, config
|
||||
end
|
||||
|
||||
--- Get a registered feature by name
|
||||
@@ -213,26 +217,36 @@ local function is_valid(field, value)
|
||||
return type(value) == field.type and (field.type == "boolean" or field.type == "number" or field.type == "string")
|
||||
end
|
||||
|
||||
--- Set a value to its override, or its default when there is no valid override
|
||||
--- @package
|
||||
--- @param path string
|
||||
--- @param field ExpScenario.FeatureField
|
||||
--- @return any # The value which was set
|
||||
function Feature._prototype:_apply_field(path, field)
|
||||
local value = self.overrides[path]
|
||||
if value == nil then
|
||||
value = field.default
|
||||
elseif not is_valid(field, value) then
|
||||
log("[WARNING] Invalid override for " .. self.name .. "." .. path .. ", using the default")
|
||||
value = field.default
|
||||
elseif field.type == "set" then
|
||||
value = list_to_set(value)
|
||||
end
|
||||
field.parent[field.key] = value
|
||||
return value
|
||||
end
|
||||
|
||||
--- Set every value to its override, or its default when there is no valid override
|
||||
--- @package
|
||||
function Feature:_apply()
|
||||
function Feature._prototype:_apply()
|
||||
for path, field in pairs(self.fields) do
|
||||
local value = self.overrides[path]
|
||||
if value == nil then
|
||||
value = field.default
|
||||
elseif not is_valid(field, value) then
|
||||
log("[WARNING] Invalid override for " .. self.name .. "." .. path .. ", using the default")
|
||||
value = field.default
|
||||
elseif field.type == "set" then
|
||||
value = list_to_set(value)
|
||||
end
|
||||
field.parent[field.key] = value
|
||||
self:_apply_field(path, field)
|
||||
end
|
||||
end
|
||||
|
||||
--- Check if the feature is enabled
|
||||
--- @return boolean
|
||||
function Feature:is_enabled()
|
||||
function Feature._prototype:is_enabled()
|
||||
return self.config.enabled
|
||||
end
|
||||
|
||||
@@ -240,7 +254,7 @@ end
|
||||
--- @generic T : table
|
||||
--- @param lib T
|
||||
--- @return T
|
||||
function Feature:guard(lib)
|
||||
function Feature._prototype:guard(lib)
|
||||
local config = self.config
|
||||
for _, handlers in pairs{ lib.events or {}, lib.on_nth_tick or {} } do
|
||||
for key, handler in pairs(handlers) do
|
||||
@@ -258,7 +272,8 @@ end
|
||||
--- Replace the overrides of the feature, then raise on_config_updated for each value which changed
|
||||
--- @param enabled boolean
|
||||
--- @param values table<string, boolean | number | string | string[]>
|
||||
function Feature:update_config(enabled, values)
|
||||
function Feature._prototype:update_config(enabled, values)
|
||||
local old_overrides = self.overrides
|
||||
local overrides = { enabled = enabled }
|
||||
for path, value in pairs(values) do
|
||||
if self.fields[path] == nil then
|
||||
@@ -267,27 +282,34 @@ function Feature:update_config(enabled, values)
|
||||
overrides[path] = value
|
||||
end
|
||||
end
|
||||
|
||||
local old_values = {}
|
||||
for path, field in pairs(self.fields) do
|
||||
old_values[path] = field.parent[field.key]
|
||||
end
|
||||
|
||||
self.overrides = overrides
|
||||
stored_overrides[self.name] = overrides
|
||||
self:_apply()
|
||||
|
||||
for path, field in pairs(self.fields) do
|
||||
local new_value = field.parent[field.key]
|
||||
if not values_equal(old_values[path], new_value) then
|
||||
script.raise_event(Feature.events.on_config_updated, {
|
||||
feature_name = self.name,
|
||||
path = path,
|
||||
old_value = old_values[path],
|
||||
new_value = new_value,
|
||||
})
|
||||
-- Only a value whose override was added, removed, or replaced can change
|
||||
local paths = {}
|
||||
for path in pairs(overrides) do
|
||||
paths[path] = true
|
||||
end
|
||||
for path in pairs(old_overrides) do
|
||||
-- A save can hold overrides for settings which no longer exist
|
||||
if self.fields[path] then
|
||||
paths[path] = true
|
||||
end
|
||||
end
|
||||
|
||||
local updates = {}
|
||||
for path in pairs(paths) do
|
||||
local field = self.fields[path]
|
||||
local old_value = field.parent[field.key]
|
||||
local new_value = self:_apply_field(path, field)
|
||||
if not values_equal(old_value, new_value) then
|
||||
updates[#updates + 1] = { feature_name = self.name, path = path, old_value = old_value, new_value = new_value }
|
||||
end
|
||||
end
|
||||
|
||||
for _, update in ipairs(updates) do
|
||||
script.raise_event(Feature.events.on_config_updated, update)
|
||||
end
|
||||
end
|
||||
|
||||
--- @class ExpScenario.FeatureUpdate
|
||||
|
||||
@@ -13,7 +13,7 @@ local plugin_root = assert(source:match("^@(.*)/test/module/env%.lua$"))
|
||||
local Framework = assert(loadfile(shared_root .. "/framework.lua"))() --- @type Framework
|
||||
|
||||
--- @class ExpScenario.TestEnv : Stubs
|
||||
--- @field Feature ExpScenario.Feature A fresh copy of the features module
|
||||
--- @field Feature ExpScenario.Features A fresh copy of the features module
|
||||
--- @field storage table The table registered with storage, as on_init would store it
|
||||
--- @field on_load fun(tbl: table) Act out on_load with the given storage table
|
||||
--- @field load_config fun(name: string): ExpScenario.Feature Load module/config/<name>.lua
|
||||
|
||||
@@ -135,11 +135,32 @@ Suite.test("optional() declares a value without a default", function(env)
|
||||
end)
|
||||
|
||||
|
||||
Suite.test("register() returns the config as well", function(env)
|
||||
local feature, config = env.Feature.register("test", { flag = true })
|
||||
Suite.eq(config == feature.config, true, "the same table")
|
||||
end)
|
||||
|
||||
Suite.test("update_config() raises when an override is removed", function(env)
|
||||
local feature = env.Feature.register("test", { flag = true, count = 1 })
|
||||
feature:update_config(true, { flag = false })
|
||||
env.events = {}
|
||||
feature:update_config(true, {})
|
||||
Suite.eq(updates(env), { { "test", "flag", false, true } }, "the value goes back to its default")
|
||||
end)
|
||||
|
||||
Suite.test("update_config() skips stored overrides for settings which no longer exist", function(env)
|
||||
local feature = env.Feature.register("test", { flag = true })
|
||||
env.on_load{ test = { enabled = true, gone = 1 } }
|
||||
feature:update_config(true, { flag = false })
|
||||
Suite.eq(updates(env), { { "test", "flag", true, false } }, "only the known value is compared")
|
||||
Suite.eq(env.storage.test, { enabled = true, flag = false }, "and the stale key is not kept")
|
||||
end)
|
||||
|
||||
Suite.test("config files register their feature", function(env)
|
||||
local feature = env.load_config("popups")
|
||||
feature:update_config(true, { show_player_damage = false })
|
||||
Suite.eq(feature.config.show_player_damage, false, "popups can be changed")
|
||||
Suite.eq(feature.config.show_player_health, true, "other values keep their default")
|
||||
local feature = env.load_config("spawn_area")
|
||||
feature:update_config(true, { ["turrets.enabled"] = false })
|
||||
Suite.eq(feature.config.turrets.enabled, false, "spawn_area can be changed")
|
||||
Suite.eq(feature.config.turrets.ammo_type, "uranium-rounds-magazine", "other values keep their default")
|
||||
end)
|
||||
|
||||
return Suite.run()
|
||||
Reference in new issue
Block a user