Removed useless files

This commit is contained in:
Cooldude2606
2020-03-29 22:50:21 +01:00
parent 3141b8a75f
commit 6a2f99b069
56 changed files with 499 additions and 1846 deletions

View File

@@ -1,179 +0,0 @@
--[[-- info
Original (javascript) version: https://hastebin.com/udakacavap.js
Can be tested against: https://wiki.factorio.com/Enemies#Spawn_chances_by_evolution_factor
]]
-- dependencies
local Global = require 'utils.global' --- @dep utils.global
local Debug = require 'utils.debug' --- @dep utils.debug
local table = require 'utils.table' --- @dep utils.table
-- localized functions
local get_random_weighted = table.get_random_weighted
local round = math.round
local ceil = math.ceil
local floor = math.floor
local random = math.random
local pairs = pairs
local format = string.format
-- this
local AlienEvolutionProgress = {}
local memory = {
spawner_specifications = {},
spawner_specifications_count = 0,
evolution_cache = {
['biter-spawner'] = {
evolution = -1,
weight_table = {},
},
['spitters-spawner'] = {
evolution = -1,
weight_table = {},
},
},
}
Global.register_init({
memory = memory,
}, function(tbl)
for name, prototype in pairs(game.entity_prototypes) do
if prototype.type == 'unit-spawner' and prototype.subgroup.name == 'enemies' then
tbl.memory.spawner_specifications[name] = prototype.result_units
memory.spawner_specifications_count = memory.spawner_specifications_count + 1
end
end
end, function(tbl)
memory = tbl.memory
end)
local function lerp(low, high, pos)
local s = high.evolution_factor - low.evolution_factor;
local l = (pos - low.evolution_factor) / s;
return (low.weight * (1 - l)) + (high.weight * l)
end
local function get_values(map, evolution_factor)
local result = {}
local sum = 0
for _, spawner_data in pairs(map) do
local list = spawner_data.spawn_points;
local low = list[1];
local high = list[#list];
for _, val in pairs(list) do
local val_evolution = val.evolution_factor
if val_evolution <= evolution_factor and val_evolution > low.evolution_factor then
low = val;
end
if val_evolution >= evolution_factor and val_evolution < high.evolution_factor then
high = val
end
end
local val
if evolution_factor <= low.evolution_factor then
val = low.weight
elseif evolution_factor >= high.evolution_factor then
val = high.weight;
else
val = lerp(low, high, evolution_factor)
end
sum = sum + val;
result[spawner_data.unit] = val;
end
local weighted_table = {}
local count = 0
for index, _ in pairs(result) do
count = count + 1
weighted_table[count] = {index, result[index] / sum}
end
return weighted_table;
end
local function get_spawner_values(spawner, evolution)
local spawner_specification = memory.spawner_specifications[spawner]
if not spawner_specification then
Debug.print(format('Spawner "%s" does not exist in the prototype data', spawner))
return
end
local cache = memory.evolution_cache[spawner]
if not cache then
cache = {
evolution = -1,
weight_table = {},
}
memory.evolution_cache[spawner] = cache
end
local evolution_value = round(evolution * 100)
if (cache.evolution < evolution_value) then
cache.evolution = evolution_value
cache.weight_table = get_values(spawner_specification, evolution)
end
return cache.weight_table
end
local function calculate_total(count, spawner, evolution)
if count == 0 then
return {}
end
local spawner_values = get_spawner_values(spawner, evolution)
if not spawner_values then
return {}
end
local aliens = {}
for _ = 1, count do
local name = get_random_weighted(spawner_values)
aliens[name] = (aliens[name] or 0) + 1
end
return aliens
end
---Creates the spawner_request structure required for AlienEvolutionProgress.get_aliens for all
---available spawner's. If dividing the total spawner's by the total aliens causes a fraction, the
---fraction will decide a chance to spawn. 1 alien for 2 spawner's will have 50% on both.
---@param total_aliens table
function AlienEvolutionProgress.create_spawner_request(total_aliens)
local per_spawner = total_aliens / memory.spawner_specifications_count
local fraction = per_spawner % 1
local spawner_request = {}
for spawner, _ in pairs(memory.spawner_specifications) do
local count = per_spawner
if fraction > 0 then
if random() > fraction then
count = ceil(count)
else
count = floor(count)
end
end
spawner_request[spawner] = count
end
return spawner_request
end
function AlienEvolutionProgress.get_aliens(spawner_requests, evolution)
local aliens = {}
for spawner, count in pairs(spawner_requests) do
for name, amount in pairs(calculate_total(count, spawner, evolution)) do
aliens[name] = (aliens[name] or 0) + amount
end
end
return aliens
end
return AlienEvolutionProgress

163
utils/color_presets.lua Normal file
View File

@@ -0,0 +1,163 @@
-- source: https://www.rapidtables.com/web/color/RGB_Color.html
return {
maroon = {r = 128, g = 0, b = 0},
dark_red = {r = 139, g = 0, b = 0},
brown = {r = 165, g = 42, b = 42},
firebrick = {r = 178, g = 34, b = 34},
crimson = {r = 220, g = 20, b = 60},
red = {r = 255, g = 0, b = 0},
tomato = {r = 255, g = 99, b = 71},
coral = {r = 255, g = 127, b = 80},
indian_red = {r = 205, g = 92, b = 92},
light_coral = {r = 240, g = 128, b = 128},
dark_salmon = {r = 233, g = 150, b = 122},
salmon = {r = 250, g = 128, b = 114},
light_salmon = {r = 255, g = 160, b = 122},
orange_red = {r = 255, g = 69, b = 0},
dark_orange = {r = 255, g = 140, b = 0},
orange = {r = 255, g = 165, b = 0},
gold = {r = 255, g = 215, b = 0},
dark_golden_rod = {r = 184, g = 134, b = 11},
golden_rod = {r = 218, g = 165, b = 32},
pale_golden_rod = {r = 238, g = 232, b = 170},
dark_khaki = {r = 189, g = 183, b = 107},
khaki = {r = 240, g = 230, b = 140},
olive = {r = 128, g = 128, b = 0},
yellow = {r = 255, g = 255, b = 0},
yellow_green = {r = 154, g = 205, b = 50},
dark_olive_green = {r = 85, g = 107, b = 47},
olive_drab = {r = 107, g = 142, b = 35},
lawn_green = {r = 124, g = 252, b = 0},
chart_reuse = {r = 127, g = 255, b = 0},
green_yellow = {r = 173, g = 255, b = 47},
dark_green = {r = 0, g = 100, b = 0},
green = {r = 0, g = 128, b = 0},
forest_green = {r = 34, g = 139, b = 34},
lime = {r = 0, g = 255, b = 0},
lime_green = {r = 50, g = 205, b = 50},
light_green = {r = 144, g = 238, b = 144},
pale_green = {r = 152, g = 251, b = 152},
dark_sea_green = {r = 143, g = 188, b = 143},
medium_spring_green = {r = 0, g = 250, b = 154},
spring_green = {r = 0, g = 255, b = 127},
sea_green = {r = 46, g = 139, b = 87},
medium_aqua_marine = {r = 102, g = 205, b = 170},
medium_sea_green = {r = 60, g = 179, b = 113},
light_sea_green = {r = 32, g = 178, b = 170},
dark_slate_gray = {r = 47, g = 79, b = 79},
teal = {r = 0, g = 128, b = 128},
dark_cyan = {r = 0, g = 139, b = 139},
aqua = {r = 0, g = 255, b = 255},
cyan = {r = 0, g = 255, b = 255},
light_cyan = {r = 224, g = 255, b = 255},
dark_turquoise = {r = 0, g = 206, b = 209},
turquoise = {r = 64, g = 224, b = 208},
medium_turquoise = {r = 72, g = 209, b = 204},
pale_turquoise = {r = 175, g = 238, b = 238},
aqua_marine = {r = 127, g = 255, b = 212},
powder_blue = {r = 176, g = 224, b = 230},
cadet_blue = {r = 95, g = 158, b = 160},
steel_blue = {r = 70, g = 130, b = 180},
corn_flower_blue = {r = 100, g = 149, b = 237},
deep_sky_blue = {r = 0, g = 191, b = 255},
dodger_blue = {r = 30, g = 144, b = 255},
light_blue = {r = 173, g = 216, b = 230},
sky_blue = {r = 135, g = 206, b = 235},
light_sky_blue = {r = 135, g = 206, b = 250},
midnight_blue = {r = 25, g = 25, b = 112},
navy = {r = 0, g = 0, b = 128},
dark_blue = {r = 0, g = 0, b = 139},
medium_blue = {r = 0, g = 0, b = 205},
blue = {r = 0, g = 0, b = 255},
royal_blue = {r = 65, g = 105, b = 225},
blue_violet = {r = 138, g = 43, b = 226},
indigo = {r = 75, g = 0, b = 130},
dark_slate_blue = {r = 72, g = 61, b = 139},
slate_blue = {r = 106, g = 90, b = 205},
medium_slate_blue = {r = 123, g = 104, b = 238},
medium_purple = {r = 147, g = 112, b = 219},
dark_magenta = {r = 139, g = 0, b = 139},
dark_violet = {r = 148, g = 0, b = 211},
dark_orchid = {r = 153, g = 50, b = 204},
medium_orchid = {r = 186, g = 85, b = 211},
purple = {r = 128, g = 0, b = 128},
thistle = {r = 216, g = 191, b = 216},
plum = {r = 221, g = 160, b = 221},
violet = {r = 238, g = 130, b = 238},
magenta = {r = 255, g = 0, b = 255},
fuchsia = {r = 255, g = 0, b = 255},
orchid = {r = 218, g = 112, b = 214},
medium_violet_red = {r = 199, g = 21, b = 133},
pale_violet_red = {r = 219, g = 112, b = 147},
deep_pink = {r = 255, g = 20, b = 147},
hot_pink = {r = 255, g = 105, b = 180},
light_pink = {r = 255, g = 182, b = 193},
pink = {r = 255, g = 192, b = 203},
antique_white = {r = 250, g = 235, b = 215},
beige = {r = 245, g = 245, b = 220},
bisque = {r = 255, g = 228, b = 196},
blanched_almond = {r = 255, g = 235, b = 205},
wheat = {r = 245, g = 222, b = 179},
corn_silk = {r = 255, g = 248, b = 220},
lemon_chiffon = {r = 255, g = 250, b = 205},
light_golden_rod_yellow = {r = 250, g = 250, b = 210},
light_yellow = {r = 255, g = 255, b = 224},
saddle_brown = {r = 139, g = 69, b = 19},
sienna = {r = 160, g = 82, b = 45},
chocolate = {r = 210, g = 105, b = 30},
peru = {r = 205, g = 133, b = 63},
sandy_brown = {r = 244, g = 164, b = 96},
burly_wood = {r = 222, g = 184, b = 135},
tan = {r = 210, g = 180, b = 140},
rosy_brown = {r = 188, g = 143, b = 143},
moccasin = {r = 255, g = 228, b = 181},
navajo_white = {r = 255, g = 222, b = 173},
peach_puff = {r = 255, g = 218, b = 185},
misty_rose = {r = 255, g = 228, b = 225},
lavender_blush = {r = 255, g = 240, b = 245},
linen = {r = 250, g = 240, b = 230},
old_lace = {r = 253, g = 245, b = 230},
papaya_whip = {r = 255, g = 239, b = 213},
sea_shell = {r = 255, g = 245, b = 238},
mint_cream = {r = 245, g = 255, b = 250},
slate_gray = {r = 112, g = 128, b = 144},
light_slate_gray = {r = 119, g = 136, b = 153},
light_steel_blue = {r = 176, g = 196, b = 222},
lavender = {r = 230, g = 230, b = 250},
floral_white = {r = 255, g = 250, b = 240},
alice_blue = {r = 240, g = 248, b = 255},
ghost_white = {r = 248, g = 248, b = 255},
honeydew = {r = 240, g = 255, b = 240},
ivory = {r = 255, g = 255, b = 240},
azure = {r = 240, g = 255, b = 255},
snow = {r = 255, g = 250, b = 250},
black = {r = 0, g = 0, b = 0},
silver = {r = 192, g = 192, b = 192},
dim_grey = {r = 105, g = 105, b = 105},
dim_gray = {r = 105, g = 105, b = 105},
grey = {r = 128, g = 128, b = 128},
gray = {r = 128, g = 128, b = 128},
dark_grey = {r = 169, g = 169, b = 169},
dark_gray = {r = 169, g = 169, b = 169},
light_grey = {r = 211, g = 211, b = 211},
light_gray = {r = 211, g = 211, b = 211},
gainsboro = {r = 220, g = 220, b = 220},
white_smoke = {r = 245, g = 245, b = 245},
white = {r = 255, g = 255, b = 255},
jailed = {r = 255, g = 255, b = 255},
probation = {r = 255, g = 255, b = 255},
guest = {r = 255, g = 255, b = 255},
auto_trusted = {r = 192, g = 192, b = 192},
regular = {r = 0.155, g = 0.540, b = 0.898},
admin = {r = 0.093, g = 0.768, b = 0.172},
donator = {r = 172.6, g = 70.2, b = 215.8},
[-10] = {r = 255, g = 255, b = 255}, -- probation
[0] = {r = 255, g = 255, b = 255}, -- guest
[10] = {r = 192, g = 192, b = 192}, -- auto_trusted
[20] = {r = 0.155, g = 0.540, b = 0.898}, -- regular
[30] = {r = 0.093, g = 0.768, b = 0.172}, -- admin
success = {r = 0, g = 255, b = 0},
warning = {r = 255, g = 255, b = 0},
fail = {r = 255, g = 0, b = 0},
info = {r = 255, g = 255, b = 255}
}

View File

@@ -1,326 +0,0 @@
local Event = require 'utils.event' --- @dep utils.event
local Game = require 'utils.game' --- @dep utils.game
local Utils = require 'utils.core' --- @dep utils.core
local Timestamp = require 'utils.timestamp' --- @dep utils.timestamp
local Rank = require 'features.rank_system' --- @dep features.rank_system
local Donator = require 'features.donator' --- @dep features.donator
local Server = require 'features.server' --- @dep features.server
local Ranks = require 'resources.ranks' --- @dep resources.ranks
local insert = table.insert
local format = string.format
local next = next
local serialize = serpent.line
local match = string.match
local gmatch = string.gmatch
local get_rank_name = Rank.get_rank_name
local Command = {}
local deprecated_command_alternatives = {
['silent-command'] = 'sc',
['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 randomiser: check out /redmew-color',
['ban'] = 'In case your forgot: please remember to include a message on how to appeal a ban'
}
local option_names = {
['description'] = 'A description of the command',
['arguments'] = 'A table of arguments, example: {"foo", "bar"} would map the first 2 arguments to foo and bar',
['default_values'] = 'A default value for a given argument when omitted, example: {bar = false}',
['required_rank'] = 'Set this to determines what rank is required to execute a command',
['donator_only'] = 'Set this to true if only donators may execute this command',
['debug_only'] = 'Set this to true if it should be registered when _DEBUG is true',
['cheat_only'] = 'Set this to true if it should be registered when _CHEATS is true',
['allowed_by_server'] = 'Set to true if the server (host) may execute this command',
['allowed_by_player'] = 'Set to false to disable players from executing this command',
['log_command'] = 'Set to true to log commands. Always true when admin is required',
['capture_excess_arguments'] = 'Allows the last argument to be the remaining text in the command',
['custom_help_text'] = 'Sets a custom help text to override the auto-generated help',
}
---Validates if there aren't any wrong fields in the options.
---@param command_name string
---@param options table
local function assert_existing_options(command_name, options)
local invalid = {}
for name, _ in pairs(options) do
if not option_names[name] then
insert(invalid, name)
end
end
if next(invalid) then
error(format("The following options were given to the command '%s' but are invalid: %s", command_name, serialize(invalid)))
end
end
---Adds a command to be executed.
---
---Options table accepts the following structure: {
--- description = 'A description of the command',
--- arguments = {'foo', 'bar'}, -- maps arguments to these names in the given sequence
--- default_values = {bar = false}, -- gives a default value to 'bar' when omitted
--- required_rank = Ranks.regular, -- defaults to Ranks.guest
--- donator_only = true, -- defaults to false
--- debug_only = true, -- registers the command if _DEBUG is set to true, defaults to false
--- cheat_only = true, -- registers the command if _CHEATS is set to true, defaults to false
--- allowed_by_server = true, -- lets the server execute this, defaults to false
--- allowed_by_player = false, -- lets players execute this, defaults to true
--- log_command = true, -- defaults to false unless admin only, then always true
--- capture_excess_arguments = true, -- defaults to false, captures excess arguments in the last argument, useful for sentences
---}
---
---The callback receives the following arguments:
--- - arguments (indexed by name, value is extracted from the parameters)
--- - the LuaPlayer or nil if it doesn't exist (such as the server player)
--- - the game tick in which the command was executed
---
---@param command_name string
---@param options table
---@param callback function
function Command.add(command_name, options, callback)
local description = options.description or '[Undocumented command]'
local arguments = options.arguments or {}
local default_values = options.default_values or {}
local required_rank = options.required_rank or Ranks.guest
local donator_only = options.donator_only or false
local debug_only = options.debug_only or false
local cheat_only = options.cheat_only or false
local capture_excess_arguments = options.capture_excess_arguments or false
local custom_help_text = options.custom_help_text or false
local allowed_by_server = options.allowed_by_server or false
local allowed_by_player = options.allowed_by_player
local log_command = options.log_command or (required_rank >= Ranks.admin) or false
local argument_list_size = table_size(arguments)
local argument_list = ''
assert_existing_options(command_name, options)
if nil == options.allowed_by_player then
allowed_by_player = true
end
if (not _DEBUG and debug_only) and (not _CHEATS and cheat_only) then
return
end
if not allowed_by_player and not allowed_by_server then
error(format("The command '%s' is not allowed by the server nor player, please enable at least one of them.", command_name))
end
for index, argument_name in pairs(arguments) do
local argument_display = argument_name
for default_value_name, _ in pairs(default_values) do
if default_value_name == argument_name then
argument_display = argument_display .. ':optional'
break
end
end
if argument_list_size == index and capture_excess_arguments then
argument_display = argument_display .. ':sentence'
end
argument_list = format('%s<%s> ', argument_list, argument_display)
end
local extra = ''
if allowed_by_server and not allowed_by_player then
extra = ' (Server only)'
elseif allowed_by_player and (required_rank > Ranks.guest) then
extra = {'command.required_rank', get_rank_name(required_rank)}
elseif allowed_by_player and donator_only then
extra = ' (Donator only)'
end
local help_text = {'command.help_text_format',(custom_help_text or argument_list), description, extra}
commands.add_command(command_name, help_text, function (command)
local print -- custom print reference in case no player is present
local player = game.player
local player_name = player and player.valid and player.name or '<server>'
if not player or not player.valid then
print = log
if not allowed_by_server then
print(format("The command '%s' is not allowed to be executed by the server.", command_name))
return
end
else
print = player.print
if not allowed_by_player then
print(format("The command '%s' is not allowed to be executed by players.", command_name))
return
end
if Rank.less_than(player_name, required_rank) then
print({'command.higher_rank_needed', command_name, get_rank_name(required_rank)})
return
end
if donator_only and not Donator.is_donator(player_name) then
print(format("The command '%s' is only allowed for donators.", command_name))
return
end
end
local named_arguments = {}
local from_command = {}
local raw_parameter_index = 1
for param in gmatch(command.parameter or '', '%S+') do
if capture_excess_arguments and raw_parameter_index == argument_list_size then
if not from_command[raw_parameter_index] then
from_command[raw_parameter_index] = param
else
from_command[raw_parameter_index] = from_command[raw_parameter_index] .. ' ' .. param
end
else
from_command[raw_parameter_index] = param
raw_parameter_index = raw_parameter_index + 1
end
end
local errors = {}
for index, argument in pairs(arguments) do
local parameter = from_command[index]
if not parameter then
for default_value_name, default_value in pairs(default_values) do
if default_value_name == argument then
parameter = default_value
break
end
end
end
if parameter == nil then
insert(errors, format('Argument "%s" from command %s is missing.', argument, command_name))
else
named_arguments[argument] = parameter
end
end
local return_early = false
for _, error in pairs(errors) do
return_early = true
print(error)
end
if return_early then
return
end
if log_command then
local tick = 'pre-game'
if game then
tick = Utils.format_time(game.tick)
end
local server_time = Server.get_current_time()
if server_time then
server_time = format('(Server time: %s)', Timestamp.to_string(server_time))
else
server_time = ''
end
log(format('%s(Map time: %s) [%s Command] %s, used: %s %s', server_time, tick, (options.required_rank >= Ranks.admin) and 'Admin' or 'Player', player_name, command_name, serialize(named_arguments)))
end
local success, error = pcall(function ()
callback(named_arguments, player, command.tick)
end)
if not success then
local serialized_arguments = serialize(named_arguments)
if _DEBUG then
print(format("%s triggered an error running a command and has been logged: '%s' with arguments %s", player_name, command_name, serialized_arguments))
print(error)
return
end
print(format('There was an error running %s, it has been logged.', command_name))
log(format("Error while running '%s' with arguments %s: %s", command_name, serialized_arguments, error))
end
end)
end
function Command.search(keyword)
local matches = {}
local count = 0
keyword = keyword:lower()
for name, description in pairs(commands.commands) do
local command = format('%s %s', name, description)
if match(command:lower(), keyword) then
count = count + 1
matches[count] = command
end
end
-- built-in commands use LocalisedString, which cannot be translated until player.print is called
for name in pairs(commands.game_commands) do
name = name
if match(name:lower(), keyword) then
count = count + 1
matches[count] = name
end
end
return matches
end
--- Trigger messages on deprecated or defined commands, ignores the server
local function on_command(event)
if not event.player_index then
return
end
local alternative = deprecated_command_alternatives[event.command]
if alternative then
local player = Game.get_player_by_index(event.player_index)
if player then
player.print(format('Warning! Usage of the command "/%s" is deprecated. Please use "/%s" instead.', event.command, alternative))
end
end
local notification = notify_on_commands[event.command]
if notification and event.player_index then
local player = Game.get_player_by_index(event.player_index)
if player then
player.print(notification)
end
end
end
--- Traps command errors if not in DEBUG.
if not _DEBUG then
local old_add_command = commands.add_command
commands.add_command =
function(name, desc, func)
old_add_command(
name,
desc,
function(cmd)
local success, error = pcall(func, cmd)
if not success then
log(error)
Game.player_print('Sorry there was an error running ' .. cmd.name)
end
end
)
end
end
Event.add(defines.events.on_console_command, on_command)
return Command

View File

@@ -1,223 +0,0 @@
--- This file contains core utilities used by the redmew scenario.
-- Dependencies
local Game = require 'utils.game' --- @dep utils.game
local Color = require 'resources.color_presets' --- @dep resources.color_presets
-- localized functions
local random = math.random
local sqrt = math.sqrt
local floor = math.floor
local match = string.match
local insert = table.insert
local concat = table.concat
-- local constants
local prefix = '## - '
local minutes_to_ticks = 60 * 60
local hours_to_ticks = 60 * 60 * 60
local ticks_to_minutes = 1 / minutes_to_ticks
local ticks_to_hours = 1 / hours_to_ticks
-- local variables
local Module = {}
--- Measures distance between pos1 and pos2
function Module.distance(pos1, pos2)
local dx = pos2.x - pos1.x
local dy = pos2.y - pos1.y
return sqrt(dx * dx + dy * dy)
end
--- Takes msg and prints it to all players except provided player
-- @param msg <string|table> table if locale is used
-- @param player <LuaPlayer> the player not to send the message to
-- @param color <table> the color to use for the message, defaults to white
function Module.print_except(msg, player, color)
if not color then
color = Color.white
end
for _, p in pairs(game.connected_players) do
if p ~= player then
p.print(msg, color)
end
end
end
--- Prints a message to all online admins
-- @param msg <string|table> table if locale is used
-- @param source <LuaPlayer|string|nil> string must be the name of a player, nil for server.
function Module.print_admins(msg, source)
local source_name
local chat_color
if source then
if type(source) == 'string' then
source_name = source
chat_color = game.players[source].chat_color
else
source_name = source.name
chat_color = source.chat_color
end
else
source_name = 'Server'
chat_color = Color.yellow
end
local formatted_msg = {'utils_core.print_admins',prefix, source_name, msg}
log(formatted_msg)
for _, p in pairs(game.connected_players) do
if p.admin then
p.print(formatted_msg, chat_color)
end
end
end
--- Returns a valid string with the name of the actor of a command.
function Module.get_actor()
if game.player then
return game.player.name
end
return '<server>'
end
function Module.cast_bool(var)
if var then
return true
else
return false
end
end
function Module.find_entities_by_last_user(player, surface, filters)
if type(player) == 'string' or not player then
error("bad argument #1 to '" .. debug.getinfo(1, 'n').name .. "' (number or LuaPlayer expected, got " .. type(player) .. ')', 1)
return
end
if type(surface) ~= 'table' and type(surface) ~= 'number' then
error("bad argument #2 to '" .. debug.getinfo(1, 'n').name .. "' (number or LuaSurface expected, got " .. type(surface) .. ')', 1)
return
end
local entities = {}
local filter = filters or {}
if type(surface) == 'number' then
surface = game.surfaces[surface]
end
if type(player) == 'number' then
player = Game.get_player_by_index(player)
end
filter.force = player.force.name
for _, e in pairs(surface.find_entities_filtered(filter)) do
if e.last_user == player then
insert(entities, e)
end
end
return entities
end
function Module.ternary(c, t, f)
if c then
return t
else
return f
end
end
--- Takes a time in ticks and returns a string with the time in format "x hour(s) x minute(s)"
function Module.format_time(ticks)
local result = {}
local hours = floor(ticks * ticks_to_hours)
if hours > 0 then
ticks = ticks - hours * hours_to_ticks
insert(result, hours)
if hours == 1 then
insert(result, 'hour')
else
insert(result, 'hours')
end
end
local minutes = floor(ticks * ticks_to_minutes)
insert(result, minutes)
if minutes == 1 then
insert(result, 'minute')
else
insert(result, 'minutes')
end
return concat(result, ' ')
end
--- Prints a message letting the player know they cannot run a command
-- @param name string name of the command
function Module.cant_run(name)
Game.player_print("Can't run command (" .. name .. ') - insufficient permission.')
end
--- Logs the use of a command and its user
-- @param actor string with the actor's name (usually acquired by calling get_actor)
-- @param command the command's name as table element
-- @param parameters the command's parameters as a table (optional)
function Module.log_command(actor, command, parameters)
local action = concat {'[Admin-Command] ', actor, ' used: ', command}
if parameters then
action = concat {action, ' ', parameters}
end
log(action)
end
function Module.comma_value(n) -- credit http://richard.warburton.it
local left, num, right = match(n, '^([^%d]*%d)(%d*)(.-)$')
return left .. (num:reverse():gsub('(%d%d%d)', '%1,'):reverse()) .. right
end
--- Asserts the argument is one of type arg_types
-- @param arg the variable to check
-- @param arg_types the type as a table of sings
-- @return boolean
function Module.verify_mult_types(arg, arg_types)
for _, arg_type in pairs(arg_types) do
if type(arg) == arg_type then
return true
end
end
return false
end
--- Returns a random RGB color as a table
function Module.random_RGB()
return {r = random(0, 255), g = random(0, 255), b = random(0, 255)}
end
--- Sets a table element to value while also returning value.
-- @param tbl table to change the element of
-- @param key string
-- @param value nil|boolean|number|string|table to set the element to
-- @return value
function Module.set_and_return(tbl, key, value)
tbl[key] = value
return value
end
-- add utility functions that exist in base factorio/util
require 'util'
--- Moves a position according to the parameters given
-- Notice: only accepts cardinal directions as direction
-- @param position <table> table containing a map position
-- @param direction <defines.direction> north, east, south, west
-- @param distance <number>
-- @return <table> modified position
Module.move_position = util.moveposition
--- Takes a direction and gives you the opposite
-- @param direction <defines.direction> north, east, south, west, northeast, northwest, southeast, southwest
-- @return <number> representing the direction
Module.opposite_direction = util.oppositedirection
--- Takes the string of a module and returns whether is it available or not
-- @param name <string> the name of the module (ex. 'utils.core')
-- @return <boolean>
Module.is_module_available = util.ismoduleavailable
return Module

View File

@@ -1,164 +0,0 @@
-- localised functions
local format = string.format
local match = string.match
local gsub = string.gsub
local serialize = serpent.line
local debug_getupvalue = debug.getupvalue
-- this
local Debug = {}
global.debug_message_count = 0
---@return number next index
local function increment()
local next = global.debug_message_count + 1
global.debug_message_count = next
return next
end
--- Takes the table output from debug.getinfo and pretties it
local function cleanup_debug(debug_table)
local short_src = match(debug_table.source, '/[^/]*/[^/]*$')
-- require will not return a valid string so short_src may be nil here
if short_src then
short_src = gsub(short_src, '%.lua', '')
end
return format('[function: %s file: %s line number: %s]', debug_table.name, short_src, debug_table.currentline)
end
---Shows the given message if debug is enabled. Uses serpent to print non scalars.
-- @param message <table|string|number|boolean>
-- @param trace_levels <number|nil> levels of stack trace to give, defaults to 1 level if nil
function Debug.print(message, trace_levels)
if not _DEBUG then
return
end
if not trace_levels then
trace_levels = 2
else
trace_levels = trace_levels + 1
end
local traceback_string = ''
if type(message) ~= 'string' and type(message) ~= 'number' and type(message) ~= 'boolean' then
message = serialize(message)
end
message = format('[%d] %s', increment(), tostring(message))
if trace_levels >= 2 then
for i = 2, trace_levels do
local debug_table = debug.getinfo(i)
if debug_table then
traceback_string = format('%s -> %s', traceback_string, cleanup_debug(debug_table))
else
break
end
end
message = format('%s - Traceback%s', message, traceback_string)
end
if _LIFECYCLE == _STAGE.runtime then
game.print(message)
end
log(message)
end
local function get(obj, prop)
return obj[prop]
end
local function get_lua_object_type_safe(obj)
local s, r = pcall(get, obj, 'help')
if not s then
return
end
return r():match('Lua%a+')
end
--- Returns the value of the key inside the object
-- or 'InvalidLuaObject' if the LuaObject is invalid.
-- or 'InvalidLuaObjectKey' if the LuaObject does not have an entry at that key
-- @param object <table> LuaObject or metatable
-- @param key <string>
-- @return <any>
function Debug.get_meta_value(object, key)
if Debug.object_type(object) == 'InvalidLuaObject' then
return 'InvalidLuaObject'
end
local suc, value = pcall(get, object, key)
if not suc then
return 'InvalidLuaObjectKey'
end
return value
end
--- Returns the Lua data type or the factorio LuaObject type
-- or 'NoHelpLuaObject' if the LuaObject does not have a help function
-- or 'InvalidLuaObject' if the LuaObject is invalid.
-- @param object <any>
-- @return string
function Debug.object_type(object)
local obj_type = type(object)
if obj_type ~= 'table' or type(object.__self) ~= 'userdata' then
return obj_type
end
local suc, valid = pcall(get, object, 'valid')
if not suc then
-- no 'valid' property
return get_lua_object_type_safe(object) or 'NoHelpLuaObject'
end
if not valid then
return 'InvalidLuaObject'
else
return get_lua_object_type_safe(object) or 'NoHelpLuaObject'
end
end
---Shows the given message if debug is on.
---@param position Position
---@param message string
function Debug.print_position(position, message)
Debug.print(format('%s %s', serialize(position), message))
end
---Executes the given callback if cheating is enabled.
---@param callback function
function Debug.cheat(callback)
if _CHEATS then
callback()
end
end
--- Returns true if the function is a closure, false otherwise.
-- A closure is a function that contains 'upvalues' or in other words
-- has a reference to a local variable defined outside the function's scope.
-- @param func<function>
-- @return boolean
function Debug.is_closure(func)
local i = 1
while true do
local n = debug_getupvalue(func, i)
if n == nil then
return false
elseif n ~= '_ENV' then
return true
end
i = i + 1
end
end
return Debug

View File

@@ -1,32 +0,0 @@
--- A small debugging tool that writes the contents of _ENV to a file when the game loads.
-- Useful for ensuring you get the same information when loading
-- the reference and desync levels in desync reports.
-- dependencies
local table = require 'utils.table' --- @dep utils.table
local Event = require 'utils.event' --- @dep utils.event
-- localized functions
local inspect = table.inspect
-- local constants
local filename = 'env_dump.lua'
-- Removes metatables and the package table
local filter = function(item, path)
if path[#path] ~= inspect.METATABLE and item ~= 'package' then
return item
end
end
local function player_joined(event)
local dump_string = inspect(_ENV, {process = filter})
if dump_string then
local s = string.format('tick on join: %s\n%s', event.tick, dump_string)
game.write_file(filename, s)
game.print('_ENV dumped into ' .. filename)
else
game.print('_ENV not dumped, dump_string was nil')
end
end
Event.add(defines.events.on_player_joined_game, player_joined)

View File

@@ -97,7 +97,7 @@
local EventCore = require 'utils.event_core' --- @dep utils.event_core
local Global = require 'utils.global' --- @dep utils.global
local Token = require 'utils.token' --- @dep utils.token
local Debug = require 'utils.debug' --- @dep utils.debug
local Debug = require 'overrides.debug' --- @dep overrides.debug
local table_remove = table.remove
local core_add = EventCore.add

View File

@@ -1,5 +1,5 @@
local Global = require 'utils.global' --- @dep utils.global
local Color = require 'resources.color_presets' --- @dep resources.color_presets
local Color = require 'utils.color_presets' --- @dep utils.color_presets
local pairs = pairs
local Game = {}

View File

@@ -1,342 +0,0 @@
local inspect ={
_VERSION = 'inspect.lua 3.1.0',
_URL = 'http://github.com/kikito/inspect.lua',
_DESCRIPTION = 'human-readable representations of tables',
_LICENSE = [[
MIT LICENSE
Copyright (c) 2013 Enrique García Cota
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]]
}
local tostring = tostring
inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end})
inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end})
-- Apostrophizes the string if it has quotes, but not aphostrophes
-- Otherwise, it returns a regular quoted string
local function smartQuote(str)
if str:match('"') and not str:match("'") then
return "'" .. str .. "'"
end
return '"' .. str:gsub('"', '\\"') .. '"'
end
-- \a => '\\a', \0 => '\\0', 31 => '\31'
local shortControlCharEscapes = {
["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v"
}
local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031
for i=0, 31 do
local ch = string.char(i)
if not shortControlCharEscapes[ch] then
shortControlCharEscapes[ch] = "\\"..i
longControlCharEscapes[ch] = string.format("\\%03d", i)
end
end
local function escape(str)
return (str:gsub("\\", "\\\\")
:gsub("(%c)%f[0-9]", longControlCharEscapes)
:gsub("%c", shortControlCharEscapes))
end
local function isIdentifier(str)
return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" )
end
local function isSequenceKey(k, sequenceLength)
return type(k) == 'number'
and 1 <= k
and k <= sequenceLength
and math.floor(k) == k
end
local defaultTypeOrders = {
['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
['function'] = 5, ['userdata'] = 6, ['thread'] = 7
}
local function sortKeys(a, b)
local ta, tb = type(a), type(b)
-- strings and numbers are sorted numerically/alphabetically
if ta == tb and (ta == 'string' or ta == 'number') then return a < b end
local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb]
-- Two default types are compared according to the defaultTypeOrders table
if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb]
elseif dta then return true -- default types before custom ones
elseif dtb then return false -- custom types after default ones
end
-- custom types are sorted out alphabetically
return ta < tb
end
-- For implementation reasons, the behavior of rawlen & # is "undefined" when
-- tables aren't pure sequences. So we implement our own # operator.
local function getSequenceLength(t)
local len = 1
local v = rawget(t,len)
while v ~= nil do
len = len + 1
v = rawget(t,len)
end
return len - 1
end
local function getNonSequentialKeys(t)
local keys = {}
local sequenceLength = getSequenceLength(t)
for k,_ in pairs(t) do
if not isSequenceKey(k, sequenceLength) then table.insert(keys, k) end
end
table.sort(keys, sortKeys)
return keys, sequenceLength
end
local function getToStringResultSafely(t, mt)
local __tostring = type(mt) == 'table' and rawget(mt, '__tostring')
local str, ok
if type(__tostring) == 'function' then
ok, str = pcall(__tostring, t)
str = ok and str or 'error: ' .. tostring(str)
end
if type(str) == 'string' and #str > 0 then return str end
end
local function countTableAppearances(t, tableAppearances)
tableAppearances = tableAppearances or {}
if type(t) == 'table' then
if not tableAppearances[t] then
tableAppearances[t] = 1
for k,v in pairs(t) do
countTableAppearances(k, tableAppearances)
countTableAppearances(v, tableAppearances)
end
countTableAppearances(getmetatable(t), tableAppearances)
else
tableAppearances[t] = tableAppearances[t] + 1
end
end
return tableAppearances
end
local copySequence = function(s)
local copy, len = {}, #s
for i=1, len do copy[i] = s[i] end
return copy, len
end
local function makePath(path, ...)
local keys = {...}
local newPath, len = copySequence(path)
for i=1, #keys do
newPath[len + i] = keys[i]
end
return newPath
end
local function processRecursive(process, item, path, visited)
if item == nil then return nil end
if visited[item] then return visited[item] end
local processed = process(item, path)
if type(processed) == 'table' then
local processedCopy = {}
visited[item] = processedCopy
local processedKey
for k,v in pairs(processed) do
processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited)
if processedKey ~= nil then
processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited)
end
end
local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
setmetatable(processedCopy, mt)
processed = processedCopy
end
return processed
end
-------------------------------------------------------------------
local Inspector = {}
local Inspector_mt = {__index = Inspector}
function Inspector:puts(...)
local args = {...}
local buffer = self.buffer
local len = #buffer
for i=1, #args do
len = len + 1
buffer[len] = args[i]
end
end
function Inspector:down(f)
self.level = self.level + 1
f()
self.level = self.level - 1
end
function Inspector:tabify()
self:puts(self.newline, string.rep(self.indent, self.level))
end
function Inspector:alreadyVisited(v)
return self.ids[v] ~= nil
end
function Inspector:getId(v)
local id = self.ids[v]
if not id then
local tv = type(v)
id = (self.maxIds[tv] or 0) + 1
self.maxIds[tv] = id
self.ids[v] = id
end
return tostring(id)
end
function Inspector:putKey(k)
if isIdentifier(k) then return self:puts(k) end
self:puts("[")
self:putValue(k)
self:puts("]")
end
function Inspector:putTable(t)
if t == inspect.KEY or t == inspect.METATABLE then
self:puts(tostring(t))
elseif self:alreadyVisited(t) then
self:puts('<table ', self:getId(t), '>')
elseif self.level >= self.depth then
self:puts('{...}')
else
if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end
local nonSequentialKeys, sequenceLength = getNonSequentialKeys(t)
local mt = getmetatable(t)
local toStringResult = getToStringResultSafely(t, mt)
self:puts('{')
self:down(function()
if toStringResult then
self:puts(' -- ', escape(toStringResult))
if sequenceLength >= 1 then self:tabify() end
end
local count = 0
for i=1, sequenceLength do
if count > 0 then self:puts(',') end
self:puts(' ')
self:putValue(t[i])
count = count + 1
end
for _,k in ipairs(nonSequentialKeys) do
if count > 0 then self:puts(',') end
self:tabify()
self:putKey(k)
self:puts(' = ')
self:putValue(t[k])
count = count + 1
end
if mt then
if count > 0 then self:puts(',') end
self:tabify()
self:puts('<metatable> = ')
self:putValue(mt)
end
end)
if #nonSequentialKeys > 0 or mt then -- result is multi-lined. Justify closing }
self:tabify()
elseif sequenceLength > 0 then -- array tables have one extra space before closing }
self:puts(' ')
end
self:puts('}')
end
end
function Inspector:putValue(v)
local tv = type(v)
if tv == 'string' then
self:puts(smartQuote(escape(v)))
elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or
tv == 'cdata' or tv == 'ctype' then
self:puts(tostring(v))
elseif tv == 'table' then
self:putTable(v)
else
self:puts('<',tv,' ',self:getId(v),'>')
end
end
-------------------------------------------------------------------
function inspect.inspect(root, options)
options = options or {}
local depth = options.depth or math.huge
local newline = options.newline or '\n'
local indent = options.indent or ' '
local process = options.process
if process then
root = processRecursive(process, root, {}, {})
end
local inspector = setmetatable({
depth = depth,
level = 0,
buffer = {},
ids = {},
maxIds = {},
newline = newline,
indent = indent,
tableAppearances = countTableAppearances(root)
}, Inspector_mt)
inspector:putValue(root)
return table.concat(inspector.buffer)
end
setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end })
return inspect

View File

@@ -1,55 +0,0 @@
--luacheck:ignore global math
local _sin = math.sin
local _cos = math.cos
math.sqrt2 = math.sqrt(2)
math.inv_sqrt2 = 1 / math.sqrt2
math.tau = 2 * math.pi
math.sin = function(x)
return math.floor(_sin(x) * 10000000 + 0.5) / 10000000
end
math.cos = function(x)
return math.floor(_cos(x) * 10000000 + 0.5) / 10000000
end
-- rounds number (num) to certain number of decimal places (idp)
math.round = function(num, idp)
local mult = 10 ^ (idp or 0)
return math.floor(num * mult + 0.5) / mult
end
math.clamp = function(num, min, max)
if num < min then
return min
elseif num > max then
return max
else
return num
end
end
--- Takes two points and calculates the slope of a line
-- @param x1, y1 numbers - coordinates of a point on a line
-- @param x2, y2 numbers - coordinates of a point on a line
-- @return number - the slope of the line
math.calculate_slope = function(x1, y1, x2, y2)
return math.abs((y2 - y1) / (x2 - x1))
end
--- Calculates the y-intercept of a line
-- @param x number - coordinates of point on line
-- @param y number - coordinates of point on line
-- @param slope number - the slope of a line
-- @return number - the y-intercept of a line
math.calculate_y_intercept = function(x, y, slope)
return y - (slope * x)
end
local deg_to_rad = math.tau / 360
math.degrees = function(angle)
return angle * deg_to_rad
end
return math

View File

@@ -1,154 +0,0 @@
local Global = require 'utils.global' --- @dep utils.global
local Game = require 'utils.game' --- @dep utils.game
local PlayerStats = require 'features.player_stats' --- @dep features.player_stats
local Command = require 'utils.command' --- @dep utils.command
local Ranks = require 'resources.ranks' --- @dep resources.ranks
local format = string.format
local abs = math.abs
local concat = table.concat
local Public = {}
local reward_token = {global.config.player_rewards.token} or {global.config.market.currency} or {'coin'}
Global.register(
{
reward_token = reward_token
},
function(tbl)
reward_token = tbl.reward_token
end
)
--- Returns the single or plural form of the token name
local function get_token_plural(quantity)
if quantity and quantity > 1 then
return concat({reward_token[1], 's'})
else
return reward_token[1]
end
end
--- Set the item to use for rewards
-- @param reward string - item name to use as reward
-- @return boolean true - indicating success
Public.set_reward = function(reward)
if global.config.player_rewards.enabled == false then
return false
end
reward_token[1] = reward
return true
end
--- Returns the name of the reward item
Public.get_reward = function()
return reward_token[1]
end
--- Gives reward tokens to the player
-- @param player <number|LuaPlayer>
-- @param amount <number> of reward tokens
-- @param message <string> an optional message to send to the affected player
-- @return <number> indicating how many were inserted or if operation failed
Public.give_reward = function(player, amount, message)
if global.config.player_rewards.enabled == false then
return 0
end
local player_index
if type(player) == 'number' then
player_index = player
player = Game.get_player_by_index(player)
else
player_index = player.index
end
local reward = {name = reward_token[1], count = amount}
if not player.can_insert(reward) then
return 0
end
if message then
player.print(message)
end
local coin_difference = player.insert(reward)
if reward_token[1] == 'coin' then
PlayerStats.change_coin_earned(player_index, coin_difference)
end
return coin_difference
end
--- Removes reward tokens from the player
-- @param player <number|LuaPlayer>
-- @param amount <number> of reward tokens
-- @param message <string> an optional message to send to the affected player
-- @return <number> indicating how many were removed or if operation failed
Public.remove_reward = function(player, amount, message)
if global.config.player_rewards.enabled == false then
return 0
end
local player_index
if type(player) == 'number' then
player_index = player
player = Game.get_player_by_index(player)
else
player_index = player.index
end
local unreward = {name = reward_token[1], count = amount}
if message then
player.print(message)
end
local coin_difference = player.remove_item(unreward)
if reward_token[1] == 'coin' then
PlayerStats.change_coin_earned(player_index, -coin_difference)
end
return coin_difference
end
Command.add(
'reward',
{
description = 'Gives a reward to a target player (removes if quantity is negative)',
arguments = {'target', 'quantity', 'reason'},
default_values = {reason = false},
required_rank = Ranks.admin,
capture_excess_arguments = true,
allowed_by_server = true,
allowed_by_player = true
},
function(args, player)
local player_name = 'server'
if player then
player_name = player.name
end
local target_name = args.target
local target = game.players[target_name]
if not target then
player.print('Target not found.')
return
end
local quantity = tonumber(args.quantity)
if quantity > 0 then
Public.give_reward(target, quantity)
local string = format('%s has rewarded %s with %s %s', player_name, target_name, quantity, get_token_plural(quantity))
if args.reason then
string = format('%s for %s', string, args.reason)
end
game.print(string)
elseif quantity < 0 then
quantity = abs(quantity)
Public.remove_reward(target, quantity)
local string = format('%s has punished %s by taking %s %s', player_name, target_name, quantity, get_token_plural(quantity))
if args.reason then
string = format('%s for %s', string, args.reason)
end
game.print(string)
else
Game.player_print(" A reward of 0 is neither a reward nor a punishment, it's just dumb. Try harder.")
end
end
)
return Public

View File

@@ -1,13 +0,0 @@
local Public = {}
local locale_string = {'', '[PRINT] ', nil}
local raw_print = print
function print(str)
locale_string[3] = str
log(locale_string)
end
Public.raw_print = raw_print
return Public

View File

@@ -1,58 +0,0 @@
--- A module to prevent recipes from being unlocked by research. Accessed via the public functions.
local Event = require 'utils.event' --- @dep utils.event
local Global = require 'utils.global' --- @dep utils.global
local Public = {}
local recipes = {}
Global.register(
{
recipes = recipes
},
function(tbl)
recipes = tbl.recipes
end
)
Event.add(
defines.events.on_research_finished,
function(event)
local p_force = game.forces.player
local r = event.research
for _, effect in pairs(r.effects) do
local recipe = effect.recipe
if recipe and recipes[recipe] then
p_force.recipes[recipe].enabled = false
end
end
end
)
Event.on_init(
function()
for recipe in pairs(recipes) do
game.forces.player.recipes[recipe].enabled = false
end
end
)
--- Locks recipes, preventing them from being enabled by research.
-- Does not check if they should be enabled/disabled by existing research.
-- @param tbl <table> an array of recipe strings
function Public.lock_recipes(tbl)
for i = 1, #tbl do
recipes[tbl[i]] = true
end
end
--- Unlocks recipes, allowing them to be enabled by research.
-- Does not check if they should be enabled/disabled by existing research.
-- @param tbl <table> an array of recipe strings
function Public.unlock_recipes(tbl)
for i = 1, #tbl do
recipes[tbl[i]] = nil
end
end
return Public

View File

@@ -1,178 +0,0 @@
local Global = require 'utils.global' --- @dep utils.global
local type = type
local error = error
local tonumber = tonumber
local tostring = tostring
local pairs = pairs
local format = string.format
--- Contains a set of callable that will attempt to sanitize and transform the input
local settings_type = {
fraction = function (input)
input = tonumber(input)
if input == nil then
return false, 'fraction setting type requires the input to be a valid number between 0 and 1.'
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, 'string setting type requires the input to be either a valid string or something that can be converted to a string.'
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, 'boolean setting type requires the input to be either a boolean, number or string that can be transformed to a boolean.'
end,
}
local settings = {}
local memory = {}
Global.register(memory, function (tbl) memory = tbl end)
local Public = {}
Public.types = {fraction = 'fraction', string = 'string', boolean = 'boolean'}
---Register a specific setting with a sensitization setting type.
---
--- Available setting types:
--- - fraction (number between 0 and 1) in either number or string form
--- - string a string or anything that can be cast to a string
--- - boolean, 1, 0, yes, no, true, false or an empty string for false
---
--- This function must be called in the control stage, i.e. not inside an event.
---
---@param name string
---@param setting_type string
---@param default mixed
function Public.register(name, setting_type, default)
if _LIFECYCLE ~= _STAGE.control then
error(format('You can only register setting names in the control stage, i.e. not inside events. Tried setting "%s" with type "%s".', name, setting_type), 2)
end
if settings[name] then
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)
end
local setting = {
default = default,
callback = callback,
}
settings[name] = setting
return setting
end
---Sets a setting to a specific value for a player.
---
---In order to get a setting value, it has to be registered via the "register" function.
---
---@param player_index number
---@param name string
---@param value mixed
function Public.set(player_index, name, value)
local setting = settings[name]
if not setting then
return error(format('Setting "%s" does not exist.', name), 2)
end
local success, sanitized_value = setting.callback(value)
if not success then
error(format('Setting "%s" failed: %s', name, sanitized_value), 2)
end
local player_settings = memory[player_index]
if not player_settings then
player_settings = {}
memory[player_index] = player_settings
end
player_settings[name] = sanitized_value
return sanitized_value
end
---Returns the value of a setting for this player.
---
---In order to set a setting value, it has to be registered via the "register" function.
---
---@param player_index number
---@param name string
function Public.get(player_index, name)
local setting = settings[name]
if not setting then
return error(format('Setting "%s" does not exist.', name), 2)
end
local player_settings = memory[player_index]
if not player_settings then
return setting.default
end
local player_setting = player_settings[name]
return player_setting ~= nil and player_setting or setting.default
end
---Returns a table of all settings for a given player in a key => value set-up
---@param player_index number
function Public.all(player_index)
local player_settings = memory[player_index] or {}
local output = {}
for name, data in pairs(settings) do
output[name] = player_settings[name] or data.default
end
return output
end
return Public

View File

@@ -1,8 +0,0 @@
local loaded = _G.package.loaded
local raw_require = require
function require(path)
return loaded[path] or error('Can only require files at runtime that have been required in the control stage.', 2)
end
return raw_require

View File

@@ -1,119 +0,0 @@
--- This module provides a classical mealy/moore state machine.
-- Each machine in constructed by calling new()
-- States and Transitions are lazily added to the machine as transition handlers and state tick handlers are registered.
-- However the state machine must be fully defined after init is done. Dynamic machine changes are currently unsupported
-- An example usage can be found here: map_gen\combined\tetris\control.lua
local Module = {}
local Debug = require 'utils.debug' --- @dep utils.debug
local in_state_callbacks = {}
local transaction_callbacks = {}
local max_stack_depth = 20
local machine_count = 0
local control_stage = _STAGE.control
--- Transitions the supplied machine into a given state and executes all transaction_callbacks
-- @param self StateMachine
-- @param new_state number/string The new state to transition to
function Module.transition(self, new_state)
Debug.print(string.format('Transitioning from state %d to state %d.', self.state, new_state))
local old_state = self.state
local stack_depth = self.stack_depth
self.stack_depth = stack_depth + 1
if stack_depth > max_stack_depth then
if _DEBUG then
error('[WARNING] Stack overflow at:' .. debug.traceback())
else
log('[WARNING] Stack overflow at:' .. debug.traceback())
end
end
local exit_callbacks = transaction_callbacks[self.id][old_state]
if exit_callbacks then
local entry_callbacks = exit_callbacks[new_state]
if entry_callbacks then
for i = 1, #entry_callbacks do
local callback = entry_callbacks[i]
if callback then
callback()
end
end
end
end
self.state = new_state
end
--- Is this machine in this state?
-- @param self StateMachine
-- @param state number/string
-- @return boolean
function Module.in_state(self, state)
return self.state == state
end
--- Invoke a machine tick. Will execute all in_state_callbacks of the given machine
-- @param self StateMachine the machine, whose handlers will be invoked
function Module.machine_tick(self)
local callbacks = in_state_callbacks[self.id][self.state]
if callbacks then
for i=1, #callbacks do
local callback = callbacks[i]
if callback then
callback()
end
end
end
self.stack_depth = 0
end
--- Register a handler that will be invoked by StateMachine.machine_tick
-- You may register multiple handlers for the same transition
-- NOTICE: This function will invoke an error if called after init. Dynamic machine changes are currently unsupported
-- @param self StateMachine the machine
-- @param state number/string The state, that the machine will be in, when callback is invoked
-- @param callback function
function Module.register_state_tick_callback(self, state, callback)
if _LIFECYCLE ~= control_stage then
error('Calling StateMachine.register_state_tick_callback after the control stage is unsupported due to desyncs.', 2)
end
in_state_callbacks[self.id][state] = in_state_callbacks[self.id][state] or {}
table.insert(in_state_callbacks[self.id][state], callback)
end
--- Register a handler that will be invoked by StateMachine.transition
-- You may register multiple handlers for the same transition
-- NOTICE: This function will invoke an error if called after init. Dynamic machine changes are currently unsupported
-- @param self StateMachine the machine
-- @param old number/string exiting state
-- @param new number/string entering state
-- @param callback function
function Module.register_transition_callback(self, old, new, callback)
if _LIFECYCLE ~= control_stage then
error('Calling StateMachine.register_transition_callback after the control stage is unsupported due to desyncs.', 2)
end
transaction_callbacks[self.id][old] = transaction_callbacks[self.id][old] or {}
transaction_callbacks[self.id][old][new] = transaction_callbacks[self.id][old][new] or {}
table.insert(transaction_callbacks[self.id][old][new], callback)
end
--- Constructs a new state machine
-- @param init_state number/string The starting state of the machine
-- @return StateMachine The constructed state machine object
function Module.new(init_state)
if _LIFECYCLE ~= control_stage then
error('Calling StateMachine.new after the control stage is unsupported due to desyncs.', 2)
end
machine_count = machine_count + 1
in_state_callbacks[machine_count] = {}
transaction_callbacks[machine_count] = {}
return {
state = init_state,
stack_depth = 0,
id = machine_count,
}
end
return Module

View File

@@ -1,265 +0,0 @@
--luacheck:ignore global table
local random = math.random
local floor = math.floor
local remove = table.remove
local tonumber = tonumber
local pairs = pairs
local table_size = table_size
--- Searches a table to remove a specific element without an index
-- @param t <table> to search
-- @param <any> table element to search for
function table.remove_element(t, element)
for k, v in pairs(t) do
if v == element then
remove(t, k)
break
end
end
end
--- Removes an item from an array in O(1) time.
-- The catch is that fast_remove doesn't guarantee to maintain the order of items in the array.
-- @param tbl <table> arrayed table
-- @param index <number> Must be >= 0. The case where index > #tbl is handled.
function table.fast_remove(tbl, index)
local count = #tbl
if index > count then
return
elseif index < count then
tbl[index] = tbl[count]
end
tbl[count] = nil
end
--- Adds the contents of table t2 to table t1
-- @param t1 <table> to insert into
-- @param t2 <table> to insert from
function table.add_all(t1, t2)
for k, v in pairs(t2) do
if tonumber(k) then
t1[#t1 + 1] = v
else
t1[k] = v
end
end
end
--- Checks if a table contains an element
-- @param t <table>
-- @param e <any> table element
-- @return <any> the index of the element or nil
function table.index_of(t, e)
for k, v in pairs(t) do
if v == e then
return k
end
end
return nil
end
--- Checks if the arrayed portion of a table contains an element
-- @param t <table>
-- @param e <any> table element
-- @return <number|nil> the index of the element or nil
function table.index_of_in_array(t, e)
for i = 1, #t do
if t[i] == e then
return i
end
end
return nil
end
local index_of = table.index_of
--- Checks if a table contains an element
-- @param t <table>
-- @param e <any> table element
-- @return <boolean> indicating success
function table.contains(t, e)
return index_of(t, e) and true or false
end
local index_of_in_array = table.index_of_in_array
--- Checks if the arrayed portion of a table contains an element
-- @param t <table>
-- @param e <any> table element
-- @return <boolean> indicating success
function table.array_contains(t, e)
return index_of_in_array(t, e) and true or false
end
--- Adds an element into a specific index position while shuffling the rest down
-- @param t <table> to add into
-- @param index <number> the position in the table to add to
-- @param element <any> to add to the table
function table.set(t, index, element)
local i = 1
for k in pairs(t) do
if i == index then
t[k] = element
return nil
end
i = i + 1
end
error('Index out of bounds', 2)
end
--- Chooses a random entry from a table
-- because this uses math.random, it cannot be used outside of events
-- @param t <table>
-- @param key <boolean> to indicate whether to return the key or value
-- @return <any> a random element of table t
function table.get_random_dictionary_entry(t, key)
local target_index = random(1, table_size(t))
local count = 1
for k, v in pairs(t) do
if target_index == count then
if key then
return k
else
return v
end
end
count = count + 1
end
end
--- Chooses a random entry from a weighted table
-- because this uses math.random, it cannot be used outside of events
-- @param weighted_table <table> of tables with items and their weights
-- @param item_index <number> of the index of items, defaults to 1
-- @param weight_index <number> of the index of the weights, defaults to 2
-- @return <any> table element
function table.get_random_weighted(weighted_table, item_index, weight_index)
local total_weight = 0
item_index = item_index or 1
weight_index = weight_index or 2
for _, w in pairs(weighted_table) do
total_weight = total_weight + w[weight_index]
end
local index = random() * total_weight
local weight_sum = 0
for _, w in pairs(weighted_table) do
weight_sum = weight_sum + w[weight_index]
if weight_sum >= index then
return w[item_index]
end
end
end
--- Creates a fisher-yates shuffle of a sequential number-indexed table
-- because this uses math.random, it cannot be used outside of events if no rng is supplied
-- from: http://www.sdknews.com/cross-platform/corona/tutorial-how-to-shuffle-table-items
-- @param t <table> to shuffle
-- @param rng <function> to provide random numbers
function table.shuffle_table(t, rng)
local rand = rng or math.random
local iterations = #t
if iterations == 0 then
error('Not a sequential table')
return
end
local j
for i = iterations, 2, -1 do
j = rand(i)
t[i], t[j] = t[j], t[i]
end
end
--- Clears all existing entries in a table
-- @param t <table> to clear
-- @param array <boolean> to indicate whether the table is an array or not
function table.clear_table(t, array)
if array then
for i = 1, #t do
t[i] = nil
end
else
for i in pairs(t) do
t[i] = nil
end
end
end
--[[
Returns the index where t[index] == target.
If there is no such index, returns a negative value such that bit32.bnot(value) is
the index that the value should be inserted to keep the list ordered.
t must be a list in ascending order for the return value to be valid.
Usage example:
local t = {1,3,5,7,9}
local x = 5
local index = table.binary_search(t, x)
if index < 0 then
game.print("value not found, smallest index where t[index] > x is: " .. bit32.bnot(index))
else
game.print("value found at index: " .. index)
end
]]
function table.binary_search(t, target)
--For some reason bit32.bnot doesn't return negative numbers so I'm using ~x = -1 - x instead.
local lower = 1
local upper = #t
if upper == 0 then
return -2 -- ~1
end
repeat
local mid = floor((lower + upper) * 0.5)
local value = t[mid]
if value == target then
return mid
elseif value < target then
lower = mid + 1
else
upper = mid - 1
end
until lower > upper
return -1 - lower -- ~lower
end
-- add table-related functions that exist in base factorio/util to the 'table' table
require 'util'
--- Similar to serpent.block, returns a string with a pretty representation of a table.
-- Notice: This method is not appropriate for saving/restoring tables. It is meant to be used by the programmer mainly while debugging a program.
-- @param table <table> the table to serialize
-- @param options <table> options are depth, newline, indent, process
-- depth sets the maximum depth that will be printed out. When the max depth is reached, inspect will stop parsing tables and just return {...}
-- process is a function which allow altering the passed object before transforming it into a string.
-- A typical way to use it would be to remove certain values so that they don't appear at all.
-- return <string> the prettied table
table.inspect = require 'utils.inspect' --- @dep utils.inspect
--- Takes a table and returns the number of entries in the table. (Slower than #table, faster than iterating via pairs)
table.size = table_size
--- Creates a deepcopy of a table. Metatables and LuaObjects inside the table are shallow copies.
-- Shallow copies meaning it copies the reference to the object instead of the object itself.
-- @param object <table> the object to copy
-- @return <table> the copied object
table.deep_copy = table.deepcopy
--- Merges multiple tables. Tables later in the list will overwrite entries from tables earlier in the list.
-- Ex. merge({{1, 2, 3}, {[2] = 0}, {[3] = 0}}) will return {1, 0, 0}
-- @param tables <table> takes a table of tables to merge
-- @return <table> a merged table
table.merge = util.merge
--- Determines if two tables are structurally equal.
-- Notice: tables that are LuaObjects or contain LuaObjects won't be compared correctly, use == operator for LuaObjects
-- @param tbl1 <table>
-- @param tbl2 <table>
-- @return <boolean>
table.equals = table.compare
return table

View File

@@ -1,152 +0,0 @@
--- source https://github.com/daurnimator/luatz/blob/master/luatz/timetable.lua
-- edited down to just what is needed.
local Public = {}
local floor = math.floor
local strformat = string.format
local function borrow(tens, units, base)
local frac = tens % 1
units = units + frac * base
tens = tens - frac
return tens, units
end
local function carry(tens, units, base)
if units >= base then
tens = tens + floor(units / base)
units = units % base
elseif units < 0 then
tens = tens + floor(units / base)
units = (base + units) % base
end
return tens, units
end
local function is_leap(y)
if (y % 4) ~= 0 then
return false
elseif (y % 100) ~= 0 then
return true
else
return (y % 400) == 0
end
end
local mon_lengths = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
-- Number of days in year until start of month; not corrected for leap years
local months_to_days_cumulative = {0}
for i = 2, 12 do
months_to_days_cumulative[i] = months_to_days_cumulative[i - 1] + mon_lengths[i - 1]
end
local function month_length(m, y)
if m == 2 then
return is_leap(y) and 29 or 28
else
return mon_lengths[m]
end
end
local function day_of_year(day, month, year)
local y_day = months_to_days_cumulative[month]
if month > 2 and is_leap(year) then
y_day = y_day + 1
end
return y_day + day
end
local function leap_years_since(year)
return floor(year / 4) - floor(year / 100) + floor(year / 400)
end
local leap_years_since_1970 = leap_years_since(1970)
local function normalise(year, month, day, hour, min, sec)
-- `month` and `day` start from 1, need -1 and +1 so it works modulo
month, day = month - 1, day - 1
-- Convert everything (except seconds) to an integer
-- by propagating fractional components down.
year, month = borrow(year, month, 12)
-- Carry from month to year first, so we get month length correct in next line around leap years
year, month = carry(year, month, 12)
month, day = borrow(month, day, month_length(floor(month + 1), year))
day, hour = borrow(day, hour, 24)
hour, min = borrow(hour, min, 60)
min, sec = borrow(min, sec, 60)
-- Propagate out of range values up
-- e.g. if `min` is 70, `hour` increments by 1 and `min` becomes 10
-- This has to happen for all columns after borrowing, as lower radix's may be pushed out of range
min, sec = carry(min, sec, 60) -- TODO: consider leap seconds?
hour, min = carry(hour, min, 60)
day, hour = carry(day, hour, 24)
-- Ensure `day` is not underflowed
-- Add a whole year of days at a time, this is later resolved by adding months
-- TODO[OPTIMIZE]: This could be slow if `day` is far out of range
while day < 0 do
month = month - 1
if month < 0 then
year = year - 1
month = 11
end
day = day + month_length(month + 1, year)
end
year, month = carry(year, month, 12)
-- TODO[OPTIMIZE]: This could potentially be slow if `day` is very large
while true do
local i = month_length(month + 1, year)
if day < i then
break
end
day = day - i
month = month + 1
if month >= 12 then
month = 0
year = year + 1
end
end
-- Now we can place `day` and `month` back in their normal ranges
-- e.g. month as 1-12 instead of 0-11
month, day = month + 1, day + 1
return {year = year, month = month, day = day, hour = hour, min = min, sec = sec}
end
--- Converts unix epoch timestamp into table {year: number, month: number, day: number, hour: number, min: number, sec: number}
-- @param seconds<number> unix epoch timestamp
-- @return {year: number, month: number, day: number, hour: number, min: number, sec: number}
function Public.to_timetable(seconds)
return normalise(1970, 1, 1, 0, 0, seconds)
end
--- Converts timetable into unix epoch timestamp
-- @param timetable<table> {year: number, month: number, day: number, hour: number, min: number, sec: number}
-- @return number
function Public.from_timetable(timetable)
local tt = normalise(timetable.year, timetable.month, timetable.day, timetable.hour, timetable.min, timetable.sec)
local year, month, day, hour, min, sec = tt.year, tt.month, tt.day, tt.hour, tt.min, tt.sec
local days_since_epoch =
day_of_year(day, month, year) + 365 * (year - 1970) + -- Each leap year adds one day
(leap_years_since(year - 1) - leap_years_since_1970) -
1
return days_since_epoch * (60 * 60 * 24) + hour * (60 * 60) + min * 60 + sec
end
--- Converts unix epoch timestamp into human readable string.
-- @param seconds<type> unix epoch timestamp
-- @return string
function Public.to_string(seconds)
local tt = normalise(1970, 1, 1, 0, 0, seconds)
return strformat('%04u-%02u-%02u %02u:%02u:%02d', tt.year, tt.month, tt.day, tt.hour, tt.min, tt.sec)
end
return Public