1
0
mirror of https://github.com/Refactorio/RedMew.git synced 2024-12-12 10:04:40 +02:00

Merge pull request #925 from linaori/feature/player-chat-colors-in-gui

Replaced command for player and chat colors
This commit is contained in:
Lynn 2019-05-31 21:13:02 +02:00 committed by GitHub
commit b3dbd2bc62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 399 additions and 158 deletions

View File

@ -44,6 +44,11 @@ global.config = {
time_for_trust = 3 * 60 * 60 * 60, -- 3 hours
everyone_is_regular = false
},
-- allows syncing player colors from and to the server. Disable this if you want to enforce custom colors
-- when enabled, /color will also be synced to the player settings
player_colors = {
enabled = true,
},
-- saves players' lives if they have a small-plane in their inventory, also adds the small-plane to the market and must therefor be loaded first
train_saviour = {
enabled = true
@ -276,9 +281,6 @@ global.config = {
donator_commands = {
enabled = true
},
player_colors = {
enabled = true
},
-- adds a command to generate a popup dialog box for players to see, useful for important announcements
popup = {
enabled = true

View File

@ -28,6 +28,10 @@ require 'features.player_create'
require 'features.rank_system'
require 'features.redmew_settings_sync'
if config.player_colors.enabled then
require 'features.player_colors'
end
-- Feature modules
-- Each can be disabled safely
if config.train_saviour.enabled then
@ -60,9 +64,6 @@ end
if config.nuke_control.enabled then
require 'features.nuke_control'
end
if config.player_colors.enabled then
require 'features.player_colors'
end
if config.reactor_meltdown.enabled then
require 'features.reactor_meltdown'
end

View File

@ -123,7 +123,7 @@ local function draw_main_frame(center, player)
label_style.height = 35
label_style.vertical_align = 'center'
local value = Settings.get(player_index, name)
local value = Settings.toScalar(name, Settings.get(player_index, name))
local input_container = setting_grid.add({type = 'flow'})
local input_container_style = input_container.style
input_container_style.height = 35
@ -238,7 +238,8 @@ local function setting_set(event)
return
end
local element_data = data[event.setting_name]
local setting_name = event.setting_name
local element_data = data[setting_name]
if not element_data then
return
@ -249,8 +250,8 @@ local function setting_set(event)
-- for some reason it has been removed already
return
end
set_element_value(input, event.new_value)
element_data.previous_value = event.old_value
set_element_value(input, Settings.toScalar(setting_name, event.new_value))
element_data.previous_value = Settings.toScalar(setting_name, event.old_value)
end
Gui.on_custom_close(main_frame_name, function(event)

View File

@ -1,84 +1,97 @@
local Event = require 'utils.event'
local Command = require 'utils.command'
local Server = require 'features.server'
local Token = require 'utils.token'
local Utils = require 'utils.core'
local Ranks = require 'resources.ranks'
local Settings = require 'utils.redmew_settings'
local Color = require 'resources.color_presets'
local serialize = serpent.line
local player_color_name = 'player-color'
local player_chat_color_name = 'player-chat-color'
Settings.register(player_color_name, Settings.types.color, nil, 'player_colors.player_color_setting_label')
Settings.register(player_chat_color_name, Settings.types.chat_color, nil, 'player_colors.player_chat_color_setting_label')
local Public = {}
local color_callback =
Token.register(
function(data)
local key = data.key
local value = data.value
if not value then
return
end
local player = game.players[key]
if not player then
return
end
player.chat_color = value.chat_color
player.color = value.color
end
)
-- left in for migration purposes, remove at a later point
local color_callback = Token.register(function(data)
local key = data.key
local value = data.value
if not value then
return
end
local player = game.players[key]
if not player then
return
end
Settings.set(player.index, player_color_name, value.color)
Settings.set(player.index, player_chat_color_name, value.chat_color)
end)
local function setting_set(event)
local value = event.new_value
if not value then
return
end
local setting_name = event.setting_name
if setting_name ~= player_color_name and setting_name ~= player_chat_color_name then
return
end
local player = game.get_player(event.player_index)
if not player or not player.valid then
return
end
if setting_name == player_color_name then
player.color = value
end
if setting_name == player_chat_color_name then
player.chat_color = value
end
end
local function player_joined_game(event)
local player_index = event.player_index
local player = game.get_player(player_index)
if not player or not player.valid then
return
end
-- already migrated
if Settings.get(player_index, player_color_name) then
return
end
--- Attempts to retrieve and get the saved color of a LuaPlayer
function Public.recall_player_color(player)
Server.try_get_data('colors', player.name, color_callback)
end
--- Assigns LuaPlayer random RGB values for color and player_color and returns the RGB table.
function Public.set_random_color(player)
return {
chat_color = Utils.set_and_return(player, 'chat_color', Utils.random_RGB()),
color = Utils.set_and_return(player, 'color', Utils.random_RGB())
}
local function on_command(event)
local player_index = event.player_index
if not player_index or event.command ~= 'color' then
return
end
local player = game.get_player(player_index)
if not player or not player.valid then
return
end
player.print({'player_colors.gui_setting_reference_message'}, Color.success)
Settings.set(player_index, player_color_name, player.color)
local error = Settings.validate(player_chat_color_name, player.chat_color)
if not error then
Settings.set(player_index, player_chat_color_name, player.chat_color)
end
end
Command.add(
'redmew-color',
{
description = {'command_description.redmew_color'},
arguments = {'set-reset-random'},
required_rank = Ranks.regular
},
function(args, player)
local player_name = player.name
local arg = args['set-reset-random']
if arg == 'set' then
local data = {
color = player.color,
chat_color = player.chat_color
}
Server.set_data('colors', player_name, data)
player.print({'player_colors.color_saved'})
Utils.print_except({'player_colors.color_saved_advert', player_name})
elseif arg == 'reset' then
Server.set_data('colors', player_name, nil)
player.print({'player_colors.color_reset'})
elseif arg == 'random' then
local color_data = Public.set_random_color(player)
player.print({'player_colors.color_random', serialize(color_data)})
else
player.print({'player_colors.fail_wrong_argument'})
end
end
)
Event.add(
defines.events.on_player_joined_game,
function(event)
local player = game.get_player(event.player_index)
if not player or not player.valid then
return
end
Public.recall_player_color(player)
end
)
Event.add(defines.events.on_player_joined_game, player_joined_game)
Event.add(Settings.events.on_setting_set, setting_set)
Event.add(defines.events.on_console_command, on_command)
return Public

View File

@ -80,6 +80,9 @@ color_saved_advert=__1__ has saved their color server-side for future maps. You
color_reset=Your saved color (if you had one) has been removed.
color_random=Your color has been changed to: __1__
fail_wrong_argument=Only set, reset, and random are accepted arguments
player_color_setting_label=Character color
player_chat_color_setting_label=Chat color
gui_setting_reference_message=Color saved and synchronized to Redmew. You can also use the Redmew Settings (gear icon) to set the character and chat colors.
[performance]
fail_wrong_argument=Scale must be a valid number ranging from 0.05 to 1

View File

@ -29,8 +29,11 @@ print_admins=__1__(ADMIN) __2__: __3__
[gui_util]
button_tooltip=Shows / hides the Redmew Gui buttons.
[redmew_settings_util]
fraction_invalid_value=fraction setting type requires the input to be a valid number between 0 and 1.
string_invalid_value=string setting type requires the input to be either a valid string or something that can be converted to a string.
boolean_invalid_value=boolean setting type requires the input to be either a boolean, number or string that can be transformed to a boolean.
color_invalid_string_value=color setting type requires the input to be either a valid preset such as "red" or "green", or a valid "r g b" or "r g b a" value.
color_invalid_table_value=color setting type with a table requires a valid {r, g, b} or {r, g, b, a} table with these keys.
invalid_color_value=color setting type only supports strings or tables as value type.
chat_color_too_dark=chat color is too dark.

249
resources/setting_types.lua Normal file
View File

@ -0,0 +1,249 @@
local Color = require 'resources.color_presets'
local type = type
local tonumber = tonumber
local tostring = tostring
local gmatch = string.gmatch
local pairs = pairs
local concat = table.concat
local size = table.size
local sqrt = math.sqrt
local floor = math.floor
local color_key_table = {'r', 'g', 'b', 'a'}
local function to_valid_rgba_table(input_table)
local output = {
r = input_table.r or input_table[1] or 0,
g = input_table.g or input_table[2] or 0,
b = input_table.b or input_table[3] or 0,
a = input_table.a or input_table[4],
}
if output.r <= 1 and output.g <= 1 and output.b <= 1 and (output.a and output.a <= 1 or not output.a) then
output.r = floor(output.r * 255)
output.g = floor(output.g * 255)
output.b = floor(output.b * 255)
if output.a and output.a <= 1 then
output.a = floor(output.a * 255)
end
end
return output
end
local function raw(input)
return input
end
local function equals_by_value(a, b)
return a == b
end
local function equals_by_table_values(a, b)
if type(a) ~= 'table' or type(b) ~= 'table' then
return a == b
end
if size(a) ~= size(b) then
return false
end
for index, value in pairs(a) do
local value_b = b[index]
if value_b == nil or value ~= value_b then
return false
end
end
for index, value in pairs(b) do
local value_a = a[index]
if value_a == nil or value ~= value_a then
return false
end
end
return true
end
local function color_to_scalar(input)
if type(input) ~= 'table' then
return ''
end
local out = {}
local i = 0
for _, value in pairs(input) do
i = i + 1
out[i] = value
end
return concat(out, ' ')
end
--- accepts either a table or a string
--- string must be in an "r g b" or "r g b a" format
--- optionally a preset name may be given instead (from resources/color_presets.lua)
--- table must contain the "r", "g" and "b" keys and may optionally contain an "a" key
--- the output will always be a valid color table for Factorio
local function color_sanitizer(input)
if input == nil or input == '' then
return true, nil
end
local input_type = type(input)
if input_type == 'string' then
local color = Color[input:match('^%s*(.-)%s*$'):gsub(' ', '_')]
if color then
return true, to_valid_rgba_table(color)
end
local data = {}
local index = 0
for value in gmatch(input, '%S+') do
index = index + 1
if index < 5 then
value = tonumber(value)
if value == nil then
return false, {'redmew_settings_util.color_invalid_string_value'}
end
if value < 0 then
value = 0
end
if value > 255 then
value = 255
end
data[color_key_table[index]] = value
end
end
if size(data) < 3 then
return false, {'redmew_settings_util.color_invalid_string_value'}
end
return true, to_valid_rgba_table(data)
end
if input_type == 'table' then
local table_size = size(input)
if table_size < 3 or table_size > 4 then
return false, {'redmew_settings_util.color_invalid_table_value'}
end
return true, to_valid_rgba_table({
r = input.r,
g = input.g,
b = input.b,
a = input.a,
})
end
return false, {'redmew_settings_util.invalid_color_value'}
end
--- Contains a set of callables that will attempt to sanitize and transform the input
--- sanitizer = takes any raw input and converts it to the final value used and stored
--- to_string = takes stored input and converts it to its string representation
return {
fraction = {
equals = equals_by_value,
toScalar = raw,
sanitizer = function(input)
input = tonumber(input)
if input == nil then
return false, {'redmew_settings_util.fraction_invalid_value'}
end
if input < 0 then
input = 0
end
if input > 1 then
input = 1
end
return true, input
end
},
string = {
equals = equals_by_value,
toScalar = raw,
sanitizer = function(input)
if input == nil then
return true, ''
end
local input_type = type(input)
if input_type == 'string' then
return true, input
end
if input_type == 'number' or input_type == 'boolean' then
return true, tostring(input)
end
return false, {'redmew_settings_util.string_invalid_value'}
end
},
boolean = {
equals = equals_by_value,
toScalar = raw,
sanitizer = function(input)
local input_type = type(input)
if input_type == 'boolean' then
return true, input
end
if input_type == 'string' then
if input == '0' or input == '' or input == 'false' or input == 'no' then
return true, false
end
if input == '1' or input == 'true' or input == 'yes' then
return true, true
end
return true, tonumber(input) ~= nil
end
if input_type == 'number' then
return true, input ~= 0
end
return false, {'redmew_settings_util.boolean_invalid_value'}
end
},
color = {
equals = equals_by_table_values,
toScalar = color_to_scalar,
sanitizer = color_sanitizer
},
chat_color = {
equals = equals_by_table_values,
toScalar = color_to_scalar,
sanitizer = function(input)
local suc, value = color_sanitizer(input)
if not suc then
return false, value
end
if not value then
return true, nil
end
local r, g, b = value.r, value.g, value.b
local brightness = sqrt(0.241 * r * r + 0.691 * g * g, 0.068 * b * b)
brightness = floor(brightness)
if brightness < 50 then
return false, {'redmew_settings_util.chat_color_too_dark'}
end
return true, value
end
}
}

View File

@ -15,6 +15,8 @@ local next = next
local serialize = serpent.line
local gmatch = string.gmatch
local get_rank_name = Rank.get_rank_name
local pairs = pairs
local pcall = pcall
local Command = {}
@ -24,12 +26,10 @@ local deprecated_command_alternatives = {
['tpplayer'] = 'tp <player>',
['tppos'] = 'tp',
['tpmode'] = 'tp mode',
['color-redmew'] = 'redmew-color'
}
local notify_on_commands = {
['version'] = 'RedMew has a version as well, accessible via /redmew-version',
['color'] = 'RedMew allows color saving and a color randomizer: check out /redmew-color',
['ban'] = 'In case your forgot: please remember to include a message on how to appeal a ban'
}

View File

@ -1,80 +1,29 @@
local Global = require 'utils.global'
local Event = require 'utils.event'
local type = type
local error = error
local tonumber = tonumber
local tostring = tostring
local pairs = pairs
local format = string.format
local tostring = tostring
local type = type
local raise_event = script.raise_event
--- Contains a set of callables that will attempt to sanitize and transform the input
local settings_type = {
fraction = function (input)
input = tonumber(input)
if input == nil then
return false, {'redmew_settings_util.fraction_invalid_value'}
end
if input < 0 then
input = 0
end
if input > 1 then
input = 1
end
return true, input
end,
string = function (input)
if input == nil then
return true, ''
end
local input_type = type(input)
if input_type == 'string' then
return true, input
end
if input_type == 'number' or input_type == 'boolean' then
return true, tostring(input)
end
return false, {'redmew_settings_util.string_invalid_value'}
end,
boolean = function (input)
local input_type = type(input)
if input_type == 'boolean' then
return true, input
end
if input_type == 'string' then
if input == '0' or input == '' or input == 'false' or input == 'no' then
return true, false
end
if input == '1' or input == 'true' or input == 'yes' then
return true, true
end
return true, tonumber(input) ~= nil
end
if input_type == 'number' then
return true, input ~= 0
end
return false, {'redmew_settings_util.boolean_invalid_value'}
end,
}
local settings_type = require 'resources.setting_types'
local settings = {}
local memory = {}
local raw_callback_setting = {
callback = function (input)
return true, input
end
local missing_setting = {
data_transformation = {
toScalar = function(input)
if type(input) ~= 'table' then
return input
end
return tostring(input)
end,
sanitizer = function (input)
return true, input
end
}
}
Global.register(memory, function (tbl) memory = tbl end)
@ -94,7 +43,13 @@ Public.events = {
on_setting_set = Event.generate_event_name('on_setting_set'),
}
Public.types = {fraction = 'fraction', string = 'string', boolean = 'boolean'}
Public.types = {
fraction = 'fraction',
string = 'string',
boolean = 'boolean',
color = 'color',
chat_color = 'chat_color'
}
---Register a specific setting with a sensitization setting type.
---
@ -118,15 +73,15 @@ function Public.register(name, setting_type, default, localisation_key)
error(format('Trying to register setting for "%s" while it has already been registered.', name), 2)
end
local callback = settings_type[setting_type]
if not callback then
error(format('Trying to register setting for "%s" with type "%s" while this type does not exist.', name, setting_type), 2)
local data_transformation = settings_type[setting_type]
if not data_transformation then
error(format('Trying to register data_transformation for "%s" with type "%s" while this type does not exist.', name, setting_type), 2)
end
local setting = {
type = setting_type,
default = default,
callback = callback,
data_transformation = data_transformation,
localised_string = localisation_key and {localisation_key} or name,
}
@ -144,7 +99,7 @@ function Public.validate(name, value)
return format('Setting "%s" does not exist.', name)
end
local success, sanitized_value = setting.callback(value)
local success, sanitized_value = setting.data_transformation.sanitizer(value)
if not success then
return sanitized_value
@ -163,10 +118,11 @@ end
function Public.set(player_index, name, value)
local setting = settings[name]
if not setting then
setting = raw_callback_setting
setting = missing_setting
end
local success, sanitized_value = setting.callback(value)
local data_transformation = setting.data_transformation
local success, sanitized_value = data_transformation.sanitizer(value)
if not success then
error(format('Setting "%s" failed: %s', name, sanitized_value), 2)
@ -186,7 +142,7 @@ function Public.set(player_index, name, value)
old_value = old_value,
new_value = sanitized_value,
player_index = player_index,
value_changed = old_value ~= sanitized_value
value_changed = not data_transformation.equals(old_value, sanitized_value)
})
return sanitized_value
@ -217,6 +173,19 @@ function Public.get(player_index, name)
return player_setting
end
---Returns the string representation of a given value based on a setting name.
---
---@param name string
---@param raw_value any
function Public.toScalar(name, raw_value)
local setting = settings[name]
if not setting then
setting = missing_setting
end
return setting.data_transformation.toScalar(raw_value)
end
---Returns a table of all settings for a given player in a key => value setup
---@param player_index number
function Public.all(player_index)