Move files to exp_legacy

This commit is contained in:
Cooldude2606
2024-09-23 15:55:28 +01:00
parent 446e87b610
commit 65145b5d34
266 changed files with 73 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
--- This contains a list of all files that will be loaded and the order they are loaded in;
-- to stop a file from loading add "--" in front of it, remove the "--" to have the file be loaded;
-- config files should be loaded after all modules are loaded;
-- core files should be required by modules and not be present in this list;
-- @config File-Loader
return {
--'example.file_not_loaded',
'modules.factorio-control', -- base factorio free play scenario
'expcore.player_data', -- must be loaded first to register event handlers
--- Game Commands
'modules.commands.debug',
'modules.commands.me',
'modules.commands.kill',
'modules.commands.admin-chat',
'modules.commands.admin-markers',
'modules.commands.teleport',
'modules.commands.cheat-mode',
'modules.commands.ratio',
'modules.commands.interface',
'modules.commands.help',
'modules.commands.roles',
'modules.commands.rainbow',
'modules.commands.clear-inventory',
'modules.commands.jail',
'modules.commands.repair',
'modules.commands.reports',
'modules.commands.spawn',
'modules.commands.warnings',
'modules.commands.find',
'modules.commands.home',
'modules.commands.connect',
'modules.commands.last-location',
'modules.commands.protection',
'modules.commands.spectate',
'modules.commands.search',
'modules.commands.bot-queue',
'modules.commands.speed',
'modules.commands.pollution',
'modules.commands.train',
'modules.commands.friendly-fire',
'modules.commands.research',
'modules.commands.vlayer',
'modules.commands.enemy',
'modules.commands.waterfill',
'modules.commands.artillery',
'modules.commands.surface-clearing',
--- Addons
'modules.addons.chat-popups',
'modules.addons.damage-popups',
'modules.addons.death-logger',
'modules.addons.advanced-start',
'modules.addons.spawn-area',
'modules.addons.compilatron',
'modules.addons.scorched-earth',
'modules.addons.pollution-grading',
'modules.addons.station-auto-name',
'modules.addons.discord-alerts',
'modules.addons.chat-reply',
'modules.addons.tree-decon',
'modules.addons.afk-kick',
'modules.addons.report-jail',
'modules.addons.protection-jail',
'modules.addons.deconlog',
'modules.addons.nukeprotect',
'modules.addons.inserter',
'modules.addons.miner',
'modules.addons.lawnmower',
'modules.addons.logging',
-- Control
'modules.control.vlayer',
--- Data
'modules.data.statistics',
'modules.data.player-colours',
'modules.data.greetings',
'modules.data.quickbar',
'modules.data.alt-view',
'modules.data.tag',
-- 'modules.data.bonus',
'modules.data.personal-logistic',
'modules.data.language',
--- GUI
'modules.gui.readme',
'modules.gui.rocket-info',
'modules.gui.science-info',
'modules.gui.autofill',
'modules.gui.warp-list',
'modules.gui.task-list',
'modules.gui.player-list',
'modules.gui.server-ups',
'modules.gui.bonus',
'modules.gui.vlayer',
'modules.gui.research',
'modules.gui.module',
'modules.gui.landfill',
'modules.gui.production',
'modules.gui.playerdata',
'modules.gui.surveillance',
'modules.graftorio.require', -- graftorio
'modules.gui.toolbar', -- must be loaded last to register toolbar handlers
--- Config Files
'config.expcore.command_auth_admin', -- commands tagged with admin_only are blocked for non admins
'config.expcore.command_auth_roles', -- commands must be allowed via the role config
'config.expcore.command_runtime_disable', -- allows commands to be enabled and disabled during runtime
'config.expcore.permission_groups', -- loads some predefined permission groups
'config.expcore.roles', -- loads some predefined roles
}

View File

@@ -0,0 +1,130 @@
--- This file is used to setup the map starting settings and the items players will start with
-- @config Advanced-Start
--- These are called factories because they return another function
-- use these as a simple methods of adding new items
-- they will do most of the work for you
-- ['item-name'] = factory(params)
-- luacheck:ignore 212/amount_made 212/items_made 212/player
-- Use these to adjust for ticks ie game.tick < 5*minutes
-- luacheck:ignore 211/seconds 211/minutes 211/hours
local seconds, minutes, hours = 60, 3600, 216000
--- Use to make a split point for the number of items given based on time
-- ['stone-furnace']=cutoff_time(5*minutes, 4,0) -- before 5 minutes give four items after 5 minutes give none
local function cutoff_time(time, before, after)
return function(amount_made, items_made, player)
if game.tick < time then
return before
else
return after
end
end
end
--- Use to make a split point for the number of items given based on amount made
-- ['firearm-magazine']=cutoff_amount_made(100, 10, 0) -- give 10 items until 100 items have been made
local function cutoff_amount_made(amount, before, after)
return function(amount_made, items_made, player)
if amount_made < amount then
return before
else
return after
end
end
end
--- Same as above but will not give any items if x amount has been made of another item, useful for tiers
-- ['light-armor']=cutoff_amount_made_unless(5, 0,1,'heavy-armor',5) -- give light armor once 5 have been made unless 5 heavy armor has been made
local function cutoff_amount_made_unless(amount, before, after, second_item, second_amount)
return function(amount_made, items_made, player)
if items_made(second_item) < second_amount then
if amount_made < amount then
return before
else
return after
end
else
return 0
end
end
end
-- Use for mass production items where you want the amount to change based on the amount already made
-- ['iron-plate']=scale_amount_made(5*minutes, 10, 10) -- for first 5 minutes give 10 items then after apply a factor of 10
local function scale_amount_made(amount, before, scalar)
return function(amount_made, items_made, player)
if amount_made < amount then
return before
else
return (amount_made * scalar) / ((game.tick / minutes) ^ 2)
end
end
end
--[[
Common values
game.tick is the amount of time the game has been on for
amount_made is the amount of that item which has been made
items_made('item-name') will return the amount of any item made
player is the player who just spawned
hours, minutes, seconds are the number of ticks in each unit of time
]]
return {
skip_intro=true, --- @setting skip_intro skips the intro given in the default factorio free play scenario
skip_victory=true, --- @setting skip_victory will skip the victory screen when a rocket is launched
disable_base_game_silo_script=true, --- @setting disable_base_game_silo_script will not load the silo script at all
research_queue_from_start=true, --- @setting research_queue_from_start when true the research queue is useable from the start
friendly_fire=false, --- @setting friendly_fire weather players will be able to attack each other on the same force
enemy_expansion=false, --- @setting enemy_expansion a catch all for in case the map settings file fails to load
chart_radius=10*32, --- @setting chart_radius the number of tiles that will be charted when the map starts
items = { --- @setting items items and there condition for being given
-- ['item-name'] = function(amount_made, production_stats, player) return <Number> end -- 0 means no items given
-- Plates
['iron-plate']=scale_amount_made(100, 10, 10),
['copper-plate']=scale_amount_made(100, 0, 8),
['steel-plate']=scale_amount_made(100, 0, 4),
-- Secondary Items
['electronic-circuit']=scale_amount_made(1000, 0, 6),
['iron-gear-wheel']=scale_amount_made(1000, 0, 6),
-- Starting Items
['burner-mining-drill']=cutoff_time(10*minutes, 4, 0),
['stone-furnace']=cutoff_time(10*minutes, 4, 0),
-- Armor
['light-armor']=cutoff_amount_made_unless(5, 0,1,'heavy-armor',5),
['heavy-armor']=cutoff_amount_made(5, 0,1),
-- Weapon
['pistol']=cutoff_amount_made_unless(0, 1, 1,'submachine-gun',5),
['submachine-gun']=cutoff_amount_made(5, 0, 1),
-- Ammo
['firearm-magazine']=cutoff_amount_made_unless(100, 10, 0,'piercing-rounds-magazine', 100),
['piercing-rounds-magazine']=cutoff_amount_made(100, 0, 10),
--[[
['construction-robot']=scale_amount_made(1, 10, 1)
]]
},
armor = {
enable=false,
main = 'modular-armor',
item = {
{
equipment='solar-panel-equipment',
count=16
},
{
equipment='belt-immunity-equipment',
count=1
},
{
equipment='battery-equipment',
count=2
},
{
equipment='personal-roboport-equipment',
count=1
},
}
}
}

View File

@@ -0,0 +1,9 @@
return {
admin_as_active = true, --- @setting admin_as_active When true admins will be treated as active regardless of afk time
trust_as_active = true, --- @setting trust_as_active When true trusted players (by playtime) will be treated as active regardless of afk time
active_role = 'Veteran', --- @setting active_role When not nil a player with this role will be treated as active regardless of afk time
afk_time = 3600*10, --- @setting afk_time The time in ticks that must pass for a player to be considered afk
kick_time = 3600*30, --- @setting kick_time The time in ticks that must pass without any active players for all players to be kicked
trust_time = 3600*60*10, --- @setting trust_time The time in ticks that a player must be online for to count as trusted
update_time = 3600*30, --- @setting update_time How often in ticks the script checks for active players
}

View File

@@ -0,0 +1,320 @@
--- Lists all bonuses which can be used, name followed by min max
-- @config Bonuses
return {
--[[
TODO
force bonus
quick health regeneration
Base point is equal to the amount of standard value in each parameter.
CMMS CRS CCS CISB CHB CRDB PBR
STD 30 90 32 20 16 12 60
= 260
MAX 60 180 64 40 32 24 120
= 480
]]
pts = {
base = 260
},
gui_display_width = {
half = 150,
label = 70,
slider = 180,
count = 50
},
conversion = {
['cmms'] = 'character_mining_speed_modifier',
['crs'] = 'character_running_speed_modifier',
['ccs'] = 'character_crafting_speed_modifier',
['cisb'] = 'character_inventory_slots_bonus',
['chb'] = 'character_health_bonus',
['crdb'] = 'character_reach_distance_bonus',
--[[
['cpdb'] = 'character_item_pickup_distance_bonus'
]]
},
player_special_bonus_rate = 300,
player_special_bonus = {
['personal_battery_recharge'] = {
-- 1 MW
value = 6,
max = 12,
scale = 1,
cost_scale = 4,
cost = 40,
is_percentage = false
}
},
player_bonus = {
['character_mining_speed_modifier'] = {
value = 3,
max = 6,
scale = 0.5,
cost_scale = 1,
cost = 10,
is_percentage = true
},
['character_running_speed_modifier'] = {
value = 1.5,
max = 3,
scale = 0.25,
cost_scale = 1,
cost = 60,
is_percentage = true
},
['character_crafting_speed_modifier'] = {
value = 8,
max = 16,
scale = 1,
cost_scale = 1,
cost = 4,
is_percentage = true
},
['character_inventory_slots_bonus'] = {
value = 100,
max = 200,
scale = 10,
cost_scale = 10,
cost = 2,
is_percentage = false
},
['character_health_bonus'] = {
value = 200,
max = 400,
scale = 50,
cost_scale = 50,
cost = 4,
is_percentage = false
},
['character_reach_distance_bonus'] = {
value = 12,
max = 24,
scale = 2,
cost_scale = 1,
cost = 1,
is_percentage = false,
combined_bonus = {
'character_resource_reach_distance_bonus',
'character_build_distance_bonus'
}
},
--[[
['character_item_pickup_distance_bonus'] = {
value = 0,
max = 20,
scale = 1,
cost_scale = 1,
cost = 1,
is_percentage = false
},
['character_loot_pickup_distance_bonus'] = {
value = 0,
max = 20,
scale = 1,
cost_scale = 1,
cost = 1,
is_percentage = false
},
['character_item_drop_distance_bonus'] = {
value = 0,
max = 20,
scale = 1,
cost_scale = 1,
cost = 1,
is_percentage = false
}
]]
},
force_bonus = {
--[[
['character_mining_speed_modifier'] = {
value = 0,
max = 6,
scale = 0.5,
cost_scale = 1,
cost = 10,
is_percentage = true
},
['character_running_speed_modifier'] = {
value = 0,
max = 3,
scale = 0.25,
cost_scale = 1,
cost = 40,
is_percentage = true
},
['character_crafting_speed_modifier'] = {
value = 0,
max = 16,
scale = 1,
cost_scale = 1,
cost = 4,
is_percentage = true
},
['character_inventory_slots_bonus'] = {
value = 0,
max = 200,
scale = 10,
cost_scale = 100,
cost = 2,
is_percentage = false
},
['character_health_bonus'] = {
value = 0,
max = 400,
scale = 50,
cost = 4,
is_percentage = false
},
['character_reach_distance_bonus'] = {
value = 0,
max = 24,
scale = 2,
cost_scale = 1,
cost = 1,
is_percentage = false,
combined_bonus = {
'character_resource_reach_distance_bonus',
'character_build_distance_bonus'
}
},
['worker_robots_speed_modifier'] = {
value = 0,
max = 0,
scale = 0,
cost_scale = 1,
cost = 1,
is_percentage = false
},
]]
['worker_robots_battery_modifier'] = {
value = 1,
max = 1,
scale = 1,
cost_scale = 1,
cost = 1,
is_percentage = false
},
['worker_robots_storage_bonus'] = {
value = 1,
max = 1,
scale = 1,
cost_scale = 1,
cost = 1,
is_percentage = false
},
['following_robots_lifetime_modifier'] = {
value = 1,
max = 1,
scale = 1,
cost_scale = 1,
cost = 1,
is_percentage = false
},
--[[
['character_item_pickup_distance_bonus'] = {
value = 0,
max = 20,
scale = 1,
cost_scale = 1,
cost = 1,
is_percentage = false
},
['character_loot_pickup_distance_bonus'] = {
value = 0,
max = 20,
scale = 1,
cost_scale = 1,
cost = 1,
is_percentage = false
},
['character_item_drop_distance_bonus'] = {
value = 0,
max = 20,
scale = 1,
cost_scale = 1,
cost = 1,
is_percentage = false
},
['character_trash_slot_count'] = {
value = 0,
max = 0,
scale = 0,
cost_scale = 1,
cost = 1,
is_percentage = false
},
['mining_drill_productivity_bonus'] = {
value = 0,
max = 0,
scale = 0,
cost_scale = 1,
cost = 1,
is_percentage = false
},
['train_braking_force_bonus'] = {
value = 0,
max = 0,
scale = 0,
cost_scale = 1,
cost = 1,
is_percentage = false
},
['laboratory_speed_modifier'] = {
value = 0,
max = 0,
scale = 0,
cost_scale = 1,
cost = 1,
is_percentage = false
},
['laboratory_productivity_bonus'] = {
value = 0,
max = 0,
scale = 0,
cost_scale = 1,
cost = 1,
is_percentage = false
},
['inserter_stack_size_bonus'] = {
value = 0,
max = 0,
scale = 0,
cost_scale = 1,
cost = 1,
is_percentage = false
},
['stack_inserter_capacity_bonus'] = {
value = 0,
max = 0,
scale = 0,
cost_scale = 1,
cost = 1,
is_percentage = false
},
['artillery_range_modifier'] = {
value = 0,
max = 0,
scale = 0,
cost_scale = 1,
cost = 1,
is_percentage = false
}
]]
},
surface_bonus = {
--[[
['solar_power_multiplier'] = {
value = 1,
max = 1000,
scale = 1,
cost_scale = 1,
cost = 1,
is_percentage = false
}
]]
}
}

View File

@@ -0,0 +1,119 @@
--- This file defines the different triggers for the chat bot
-- @config Chat-Reply
local Async = require 'expcore.async'
local format_time = _C.format_time --- @dep expcore.common
-- eg Async(async_message, is_command or player, message)
local async_message = Async.register(function(player, message)
if player == true then game.print(message) else player.print(message) end
end)
-- luacheck:ignore 212/player 212/is_command
return {
allow_command_prefix_for_messages = true, --- @setting allow_command_prefix_for_messages when true any message trigger will print to all player when prefixed
messages = { --- @setting messages will trigger when ever the word is said
['discord'] = {'info.discord'},
['expgaming'] = {'info.website'},
['website'] = {'info.website'},
['status'] = {'info.status'},
['github'] = {'info.github'},
['patreon'] = {'info.patreon'},
['donate'] = {'info.patreon'},
['command'] = {'info.custom-commands'},
['commands'] = {'info.custom-commands'},
['softmod'] = {'info.softmod'},
['script'] = {'info.softmod'},
['loop'] = {'chat-bot.loops'},
['rhd'] = {'info.lhd'},
['lhd'] = {'info.lhd'},
['roundabout'] = {'chat-bot.loops'},
['roundabouts'] = {'chat-bot.loops'},
['redmew'] = {'info.redmew'},
['afk'] = function(player, _is_command)
local max = player
for _, next_player in pairs(game.connected_players) do
if max.afk_time < next_player.afk_time then
max = next_player
end
end
return {'chat-bot.afk', max.name, format_time(max.afk_time, {minutes = true, seconds = true, long = true})}
end,
['players'] = function(_player, _is_command)
return {'chat-bot.players', #game.players}
end,
['online'] = function(_player, _is_command)
return {'chat-bot.players-online', #game.connected_players}
end,
['r!verify'] = function(player, _is_command)
return {'chat-bot.verify', player.name}
end,
},
command_admin_only = false, --- @setting command_admin_only when true will only allow chat commands for admins
command_permission = 'command/chat-bot', --- @setting command_permission the permission used to allow command prefixes
command_prefix = '!', --- @setting command_prefix prefix used for commands below and to print to all players (if enabled above)
commands = { --- @setting commands will trigger only when command prefix is given
['dev'] = {'chat-bot.not-real-dev'},
['blame'] = function(player, _is_command)
local names = {'Cooldude2606', 'arty714', 'badgamernl', 'mark9064', 'aldldl', 'Drahc_pro', player.name}
for _, next_player in pairs(game.connected_players) do
names[#names + 1] = next_player.name
end
return {'chat-bot.blame', table.get_random_dictionary_entry(names)}
end,
['magic'] = {'chat-bot.magic'},
['aids'] = {'chat-bot.aids'},
['riot'] = {'chat-bot.riot'},
['lenny'] = {'chat-bot.lenny'},
['hodor'] = function(_player, _is_command)
local options = {'?', '.', '!', '!!!'}
return {'chat-bot.hodor', table.get_random_dictionary_entry(options)}
end,
['evolution'] = function(_player, _is_command)
return {'chat-bot.current-evolution', string.format('%.2f', game.forces['enemy'].evolution_factor)}
end,
['makepopcorn'] = function(player, _is_command)
local timeout = math.floor(180*(math.random()+0.5))
Async(async_message, true, {'chat-bot.reply', {'chat-bot.get-popcorn-1'}})
Async.wait(timeout, async_message, true, {'chat-bot.reply', {'chat-bot.get-popcorn-2', player.name}})
end,
['passsomesnaps'] = function(player, _is_command)
local timeout = math.floor(180*(math.random()+0.5))
Async(async_message, player, {'chat-bot.reply', {'chat-bot.get-snaps-1'}})
Async.wait(timeout, async_message, true, {'chat-bot.reply', {'chat-bot.get-snaps-2', player.name}})
Async.wait(timeout*(math.random()+0.5), async_message, true, {'chat-bot.reply', {'chat-bot.get-snaps-3', player.name}})
end,
['makecocktail'] = function(player, _is_command)
local timeout = math.floor(180*(math.random()+0.5))
Async(async_message, true, {'chat-bot.reply', {'chat-bot.get-cocktail-1'}})
Async.wait(timeout, async_message, true, {'chat-bot.reply', {'chat-bot.get-cocktail-2', player.name}})
Async.wait(timeout*(math.random()+0.5), async_message, true, {'chat-bot.reply', {'chat-bot.get-cocktail-3', player.name}})
end,
['makecoffee'] = function(player, _is_command)
local timeout = math.floor(180*(math.random()+0.5))
Async(async_message, true, {'chat-bot.reply', {'chat-bot.make-coffee-1'}})
Async.wait(timeout, async_message, true, {'chat-bot.reply', {'chat-bot.make-coffee-2', player.name}})
end,
['orderpizza'] = function(player, _is_command)
local timeout = math.floor(180*(math.random()+0.5))
Async(async_message, true, {'chat-bot.reply', {'chat-bot.order-pizza-1'}})
Async.wait(timeout, async_message, true, {'chat-bot.reply', {'chat-bot.order-pizza-2', player.name}})
Async.wait(timeout*(math.random()+0.5), async_message, true, {'chat-bot.reply', {'chat-bot.order-pizza-3', player.name}})
end,
['maketea'] = function(player, _is_command)
local timeout = math.floor(180*(math.random()+0.5))
Async(async_message, true, {'chat-bot.reply', {'chat-bot.make-tea-1'}})
Async.wait(timeout, async_message, true, {'chat-bot.reply', {'chat-bot.make-tea-2', player.name}})
end,
['meadplease'] = function(player, _is_command)
local timeout = math.floor(180*(math.random()+0.5))
Async(async_message, true, {'chat-bot.reply', {'chat-bot.get-mead-1'}})
Async.wait(timeout, async_message, true, {'chat-bot.reply', {'chat-bot.get-mead-2', player.name}})
end,
['passabeer'] = function(player, _is_command)
local timeout = math.floor(180*(math.random()+0.5))
Async(async_message, true, {'chat-bot.reply', {'chat-bot.get-beer-1'}})
Async.wait(timeout, async_message, true, {'chat-bot.reply', {'chat-bot.get-beer-2', player.name}})
end
}
}

View File

@@ -0,0 +1,23 @@
--- Config file for the compliatrons including where they spawn and what messages they show
-- @config Compilatron
return {
message_cycle=60*15, --- @setting message_cycle 15 seconds default, how often (in ticks) the messages will cycle
locations={ --- @setting locations defines the spawn locations for all compilatrons
['Spawn']={x=0,y=0}
},
messages={ --- @setting messages the messages that each one will say, must be same name as its location
['Spawn']={
{'info.website'},
{'info.read-readme'},
{'info.discord'},
{'info.softmod'},
{'info.redmew'},
{'info.custom-commands'},
{'info.status'},
{'info.lhd'},
{'info.github'},
{'info.patreon'},
}
}
}

View File

@@ -0,0 +1,16 @@
--- This config controls what happens when a player dies mostly about map markers and item collection;
-- allow_teleport_to_body_command and allow_collect_bodies_command can be over ridden if command_auth_runtime_disable is present;
-- if not present then the commands will not be loaded into the game
-- @config Death-Logger
return {
--WIP_allow_teleport_to_body_command=false, -- allows use of /return-to-body which teleports you to your last death
--WIP_allow_collect_bodies_command=false, -- allows use of /collect-body which returns all your items to you and removes the body
use_chests_as_bodies=false, --- @setting use_chests_as_bodies weather items should be moved into a chest when a player dies
auto_collect_bodies=true, --- @setting auto_collect_bodies enables items being returned to the spawn point in chests upon corpse expiring
show_map_markers=true, --- @setting show_map_markers shows markers on the map where bodies are
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
}

View File

@@ -0,0 +1,11 @@
--- This config controls whether actions such as deconning by players without sufficient permissions is logged or not
-- @config Deconlog
return {
decon_area = true, ---@setting decon_area whether to log when an area is being deconstructed
built_entity = true, ---@setting built_entity whether to log when an entity is built
mined_entity = true, ---@setting mined_entity whether to log when an entity is mined
fired_rocket = true, ---@setting fired_nuke whether to log when a rocket is fired
fired_explosive_rocket = true, ---@setting fired_nuke whether to log when a explosive rocket is fired
fired_nuke = true, ---@setting fired_nuke whether to log when a nuke is fired
}

View File

@@ -0,0 +1,25 @@
--- Config file used to enable and disable different push messages for discord
-- @config Discord-Alerts
return {
show_playtime=true,
entity_protection=true,
player_reports=true,
player_warnings=true,
player_bans=true,
player_mutes=true,
player_kicks=true,
player_promotes=false,
player_jail=true,
['config']=true,
['purge']=true,
['c']=true,
['command']=true,
['silent-command']=true,
['measured-command']=true,
['banlist']=true,
['permissions']=true,
['editor']=true,
['cheat']=true,
['open']=false
}

View File

@@ -0,0 +1,19 @@
--- This is a very simple config file which adds a admin only auth function;
-- not much to change here its more so it can be enabled and disabled from ./config/file_loader.lua;
-- either way you can change the requirements to be "admin" if you wanted to
-- @config Commands-Auth-Admin
local Commands = require 'expcore.commands' --- @dep expcore.commands
-- luacheck:ignore 212/command
Commands.add_authenticator(function(player, command, tags, reject)
if tags.admin_only then
if player.admin then
return true
else
return reject{'command-auth.admin-only'}
end
else
return true
end
end)

View File

@@ -0,0 +1,14 @@
--- This will make commands only work if the role has been allowed it in the role config
-- @config Commands-Auth-Roles
local Commands = require 'expcore.commands' --- @dep expcore.commands
local Roles = require 'expcore.roles' --- @dep expcore.roles
-- luacheck:ignore 212/tags
Commands.add_authenticator(function(player, command, tags, reject)
if Roles.player_allowed(player,'command/'..command) then
return true
else
return reject()
end
end)

View File

@@ -0,0 +1,15 @@
--- This will make commands only work when a valid color from the presets has been selected
-- @config Commands-Color-Parse
local Commands = require 'expcore.commands' --- @dep expcore.commands
local Colours = require 'utils.color_presets' --- @dep utils.color_presets
Commands.add_parse('color',function(input, _, reject)
if not input then return end
local color = Colours[input]
if not color then
return reject{'expcore-commands.reject-color'}
else
return input
end
end)

View File

@@ -0,0 +1,142 @@
--[[-- This file contains some common command param parse functions;
this file is less of a config and more of a requirement but you may wish to change how some behave;
as such you need to be confident with lua but you edit this config file;
use Commands.add_parse('name',function(input, player, reject) end) to add a parse;
see ./expcore/commands.lua for more details
@config Commands-Parse
@usage Adds Parses:
boolean
string-options - options: array
string-max-length - max_length: number
number
integer
number-range - range_min: number, range_max: number
integer-range - range_min: number, range_max: number
player
player-online
player-alive
force
surface
]]
local Commands = require 'expcore.commands' --- @dep expcore.commands
-- luacheck:ignore 212/player
Commands.add_parse('boolean',function(input, player)
if not input then return end -- nil check
input = input:lower()
if input == 'yes'
or input == 'y'
or input == 'true'
or input == '1' then
return true
else
return false
end
end)
Commands.add_parse('string-options',function(input, player, reject, options)
if not input then return end -- nil check
local option = _C.auto_complete(options, input)
return option or reject{'expcore-commands.reject-string-options', table.concat(options, ', ')}
end)
Commands.add_parse('string-max-length',function(input, player, reject, max_length)
if not input then return end -- nil check
local length = input:len()
if length > max_length then
return reject{'expcore-commands.reject-string-max-length',max_length}
else
return input
end
end)
Commands.add_parse('number',function(input, player, reject)
if not input then return end -- nil check
local number = tonumber(input)
if not number then
return reject{'expcore-commands.reject-number'}
else
return number
end
end)
Commands.add_parse('integer',function(input, player, reject)
if not input then return end -- nil check
local number = tonumber(input)
if not number then
return reject{'expcore-commands.reject-number'}
else
return math.floor(number)
end
end)
Commands.add_parse('number-range',function(input, player, reject, range_min, range_max)
local number = Commands.parse('number',input, player, reject)
if not number then return end -- nil check
if number < range_min or number > range_max then
return reject{'expcore-commands.reject-number-range',range_min, range_max}
else
return number
end
end)
Commands.add_parse('integer-range',function(input, player, reject, range_min, range_max)
local number = Commands.parse('integer',input, player, reject)
if not number then return end -- nil check
if number < range_min or number > range_max then
return reject{'expcore-commands.reject-number-range',range_min, range_max}
else
return number
end
end)
Commands.add_parse('player',function(input, player, reject)
if not input then return end -- nil check
local input_player = game.players[input]
if not input_player then
return reject{'expcore-commands.reject-player',input}
else
return input_player
end
end)
Commands.add_parse('player-online',function(input, player, reject)
local input_player = Commands.parse('player',input, player, reject)
if not input_player then return end -- nil check
if not input_player.connected then
return reject{'expcore-commands.reject-player-online'}
else
return input_player
end
end)
Commands.add_parse('player-alive',function(input, player, reject)
local input_player = Commands.parse('player-online',input, player, reject)
if not input_player then return end -- nil check
if not input_player.character or not input_player.character.health or input_player.character.health <= 0 then
return reject{'expcore-commands.reject-player-alive'}
else
return input_player
end
end)
Commands.add_parse('force',function(input, player, reject)
if not input then return end -- nil check
local force = game.forces[input]
if not force then
return reject{'expcore-commands.reject-force'}
else
return force
end
end)
Commands.add_parse('surface',function(input, player, reject)
if not input then return end
local surface = game.surfaces[input]
if not surface then
return reject{'expcore-commands.reject-surface'}
else
return surface
end
end)

View File

@@ -0,0 +1,54 @@
--[[-- Adds some parse functions that can be used with the role system
@config Commands-Parse-Roles
@usage Adds Parses:
role
player-role
player-role-online
player-role-alive
]]
local Commands = require 'expcore.commands' --- @dep expcore.commands
local Roles = require 'expcore.roles' --- @dep expcore.roles
local auto_complete = _C.auto_complete --- @dep expcore.common
require 'config.expcore.command_general_parse'
-- luacheck:ignore 212/player
Commands.add_parse('role',function(input, player, reject)
if not input then return end
local roles = Roles.config.order
local rev_roles = {}
for i=#roles, 1,-1 do
table.insert(rev_roles, roles[i])
end
local role = auto_complete(rev_roles, input)
role = Roles.get_role_by_name(role)
if not role then
return reject{'expcore-role.reject-role'}
else
return role
end
end)
Commands.add_parse('player-role',function(input, player, reject)
local input_player = Commands.parse('player',input, player, reject)
if not input_player then return end -- nil check
local player_highest = Roles.get_player_highest_role(player)
local input_player_highest = Roles.get_player_highest_role(input_player)
if player_highest.index < input_player_highest.index then
return input_player
else
return reject{'expcore-roles.reject-player-role'}
end
end)
Commands.add_parse('player-role-online',function(input, player, reject)
local input_player = Commands.parse('player-role',input, player, reject)
if not input_player then return end -- nil check
return Commands.parse('player-online',input_player.name, player, reject)
end)
Commands.add_parse('player-role-alive',function(input, player, reject)
local input_player = Commands.parse('player-role',input, player, reject)
if not input_player then return end -- nil check
return Commands.parse('player-alive',input_player.name, player, reject)
end)

View File

@@ -0,0 +1,32 @@
--- This config for command auth allows commands to be globally enabled and disabled during runtime;
-- this config adds Commands.disable and Commands.enable to enable and disable commands for all users
-- @config Commands-Auth-Runtime-Disable
local Commands = require 'expcore.commands' --- @dep expcore.commands
local Global = require 'utils.global' --- @dep utils.global
local disabled_commands = {}
Global.register(disabled_commands, function(tbl)
disabled_commands = tbl
end)
--- Stops a command from be used by any one
-- @tparam string command_name the name of the command to disable
function Commands.disable(command_name)
disabled_commands[command_name] = true
end
--- Allows a command to be used again after disable was used
-- @tparam string command_name the name of the command to enable
function Commands.enable(command_name)
disabled_commands[command_name] = nil
end
-- luacheck:ignore 212/player 212/tags
Commands.add_authenticator(function(player, command, tags, reject)
if disabled_commands[command] then
return reject{'command-auth.command-disabled'}
else
return true
end
end)

View File

@@ -0,0 +1,141 @@
--- Use this file to add new permission groups to the game;
-- start with Permission_Groups.new_group('name');
-- then use either :allow_all() or :disallow_all() to set the default for non specified actions;
-- then use :allow{} and :disallow{} to specify certain actions to allow/disallow
-- @config Permission-Groups
--local Event = require 'utils.event' -- @dep utils.event
local Permission_Groups = require 'expcore.permission_groups' --- @dep expcore.permission_groups
Permission_Groups.new_group('Admin')
:allow_all()
:disallow{
'add_permission_group', -- admin
'delete_permission_group',
'edit_permission_group',
'import_permissions_string',
'map_editor_action',
'toggle_map_editor',
'change_multiplayer_config',
'set_heat_interface_mode',
'set_heat_interface_temperature',
'set_infinity_container_filter_item',
'set_infinity_container_remove_unfiltered_items',
'set_infinity_pipe_filter'
}
Permission_Groups.new_group('Trusted')
:allow_all()
:disallow{
'add_permission_group', -- admin
'delete_permission_group',
'edit_permission_group',
'import_permissions_string',
'map_editor_action',
'toggle_map_editor',
'change_multiplayer_config',
'set_heat_interface_mode',
'set_heat_interface_temperature',
'set_infinity_container_filter_item',
'set_infinity_container_remove_unfiltered_items',
'set_infinity_pipe_filter',
'admin_action' -- trusted
}
Permission_Groups.new_group('Standard')
:allow_all()
:disallow{
'add_permission_group', -- admin
'delete_permission_group',
'edit_permission_group',
'import_permissions_string',
'map_editor_action',
'toggle_map_editor',
'change_multiplayer_config',
'set_heat_interface_mode',
'set_heat_interface_temperature',
'set_infinity_container_filter_item',
'set_infinity_container_remove_unfiltered_items',
'set_infinity_pipe_filter',
'admin_action', -- trusted
'change_programmable_speaker_alert_parameters', -- standard
'drop_item',
'set_auto_launch_rocket'
}
Permission_Groups.new_group('Guest')
:allow_all()
:disallow{
'add_permission_group', -- admin
'delete_permission_group',
'edit_permission_group',
'import_permissions_string',
'map_editor_action',
'toggle_map_editor',
'change_multiplayer_config',
'set_heat_interface_mode',
'set_heat_interface_temperature',
'set_infinity_container_filter_item',
'set_infinity_container_remove_unfiltered_items',
'set_infinity_pipe_filter',
'admin_action', -- trusted
'change_programmable_speaker_alert_parameters', -- standard
'drop_item',
'set_auto_launch_rocket',
'change_programmable_speaker_parameters', -- guest
'change_train_stop_station',
--'deconstruct',
'remove_cables',
'remove_train_station',
'reset_assembling_machine',
'rotate_entity',
'use_artillery_remote',
'launch_rocket',
'cancel_research',
'activate_cut',
'flush_opened_entity_fluid',
'flush_opened_entity_specific_fluid'
}
Permission_Groups.new_group('Restricted')
:disallow_all()
:allow('write_to_console')
--[[ These events are used until a role system is added to make it easier for our admins
local trusted_time = 60*60*60*10 -- 10 hour
local standard_time = 60*60*60*3 -- 3 hour
local function assign_group(player)
local current_group_name = player.permission_group and player.permission_group.name or 'None'
if player.admin then
Permission_Groups.set_player_group(player,'Admin')
elseif player.online_time > trusted_time or current_group_name == 'Trusted' then
Permission_Groups.set_player_group(player,'Trusted')
elseif player.online_time > standard_time or current_group_name == 'Standard' then
Permission_Groups.set_player_group(player,'Standard')
else
Permission_Groups.set_player_group(player,'Guest')
end
end
Event.add(defines.events.on_player_joined_game,function(event)
local player = game.players[event.player_index]
assign_group(player)
end)
Event.add(defines.events.on_player_promoted,function(event)
local player = game.players[event.player_index]
assign_group(player)
end)
Event.add(defines.events.on_player_demoted,function(event)
local player = game.players[event.player_index]
assign_group(player)
end)
local check_interval = 60*60*15 -- 15 minutes
Event.on_nth_tick(check_interval,function(event)
for _,player in pairs(game.connected_players) do
assign_group(player)
end
end)]]

View File

@@ -0,0 +1,367 @@
--- This is the main config file for the role system; file includes defines for roles and role flags and default values
-- @config Roles
local Roles = require 'expcore.roles' --- @dep expcore.roles
local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data
local Statistics = PlayerData.Statistics
--- Role flags that will run when a player changes roles
Roles.define_flag_trigger('is_admin',function(player,state)
player.admin = state
end)
Roles.define_flag_trigger('is_spectator',function(player,state)
player.spectator = state
end)
Roles.define_flag_trigger('is_jail',function(player,state)
if player.character then
player.character.active = not state
end
end)
--- Admin Roles
Roles.new_role('System','SYS')
:set_permission_group('Default', true)
:set_flag('is_admin')
:set_flag('is_spectator')
:set_flag('report-immune')
:set_flag('instant-respawn')
:set_allow_all()
Roles.new_role('Senior Administrator','SAdmin')
:set_permission_group('Admin')
:set_custom_color{r=233,g=63,b=233}
:set_flag('is_admin')
:set_flag('is_spectator')
:set_flag('report-immune')
:set_flag('instant-respawn')
:set_parent('Administrator')
:allow{
'command/interface',
'command/debug',
'command/toggle-cheat-mode',
'command/research-all'
}
Roles.new_role('Administrator','Admin')
:set_permission_group('Admin')
:set_custom_color{r=233,g=63,b=233}
:set_flag('is_admin')
:set_flag('is_spectator')
:set_flag('report-immune')
:set_flag('instant-respawn')
:set_parent('Moderator')
:allow{
'gui/warp-list/bypass-proximity',
'gui/warp-list/bypass-cooldown',
'command/connect-all',
'command/collectdata'
}
Roles.new_role('Moderator','Mod')
:set_permission_group('Admin')
:set_custom_color{r=0,g=170,b=0}
:set_flag('is_admin')
:set_flag('is_spectator')
:set_flag('report-immune')
:set_flag('instant-respawn')
:set_parent('Trainee')
:allow{
'command/assign-role',
'command/unassign-role',
'command/repair',
'command/kill/always',
'command/clear-tag/always',
'command/go-to-spawn/always',
'command/clear-reports',
'command/clear-warnings',
'command/clear-inventory',
-- 'command/bonus',
'gui/bonus',
'command/home',
'command/home-set',
'command/home-get',
'command/return',
'command/connect-player',
'gui/rocket-info/toggle-active',
'gui/rocket-info/remote_launch',
'command/toggle-friendly-fire',
'command/toggle-always-day',
'fast-tree-decon'
}
Roles.new_role('Trainee','TrMod')
:set_permission_group('Admin')
:set_custom_color{r=0,g=170,b=0}
:set_flag('is_admin')
:set_flag('is_spectator')
:set_flag('report-immune')
:set_parent('Veteran')
:allow{
'command/admin-chat',
'command/admin-marker',
'command/goto',
'command/teleport',
'command/bring',
'command/give-warning',
'command/get-warnings',
'command/get-reports',
'command/protect-entity',
'command/protect-area',
'command/jail',
'command/unjail',
'command/kick',
'command/ban',
'command/spectate',
'command/follow',
'command/search',
'command/search-amount',
'command/search-recent',
'command/search-online',
'command/personal-battery-recharge',
'command/pollution-off',
'command/pollution-clear',
'command/bot-queue-get',
'command/bot-queue-set',
'command/game-speed',
'command/kill-biters',
'command/remove-biters',
'gui/playerdata'
}
--- Trusted Roles
Roles.new_role('Board Member','Board')
:set_permission_group('Trusted')
:set_custom_color{r=247,g=246,b=54}
:set_flag('is_spectator')
:set_flag('report-immune')
:set_flag('instant-respawn')
:set_parent('Sponsor')
:allow{
'command/goto',
'command/repair',
'command/spectate',
'command/follow',
'gui/playerdata'
}
Roles.new_role('Senior Backer','Backer')
:set_permission_group('Trusted')
:set_custom_color{r=238,g=172,b=44}
:set_flag('is_spectator')
:set_flag('report-immune')
:set_flag('instant-respawn')
:set_parent('Sponsor')
:allow{
}
Roles.new_role('Sponsor','Spon')
:set_permission_group('Trusted')
:set_custom_color{r=238,g=172,b=44}
:set_flag('is_spectator')
:set_flag('report-immune')
:set_flag('instant-respawn')
:set_parent('Supporter')
:allow{
'gui/rocket-info/toggle-active',
'gui/rocket-info/remote_launch',
-- 'command/bonus',
'gui/bonus',
'command/home',
'command/home-set',
'command/home-get',
'command/return',
'fast-tree-decon'
}
Roles.new_role('Supporter','Sup')
:set_permission_group('Trusted')
:set_custom_color{r=230,g=99,b=34}
:set_flag('is_spectator')
:set_parent('Veteran')
:allow{
'command/tag-color',
'command/jail',
'command/unjail',
'command/join-message',
'command/join-message-clear'
}
Roles.new_role('Partner','Part')
:set_permission_group('Trusted')
:set_custom_color{r=140,g=120,b=200}
:set_flag('is_spectator')
:set_parent('Veteran')
:allow{
'command/jail',
'command/unjail'
}
local hours10, hours250 = 10*216000, 250*60
Roles.new_role('Veteran','Vet')
:set_permission_group('Trusted')
:set_custom_color{r=140,g=120,b=200}
:set_parent('Member')
:allow{
'command/chat-bot',
'command/last-location'
}
:set_auto_assign_condition(function(player)
if player.online_time >= hours10 then
return true
else
local stats = Statistics:get(player, {})
local playTime, afkTime, mapCount = stats.Playtime or 0, stats.AfkTime or 0, stats.MapsPlayed or 0
return playTime - afkTime >= hours250 and mapCount >= 25
end
end)
--- Standard User Roles
Roles.new_role('Member','Mem')
:set_permission_group('Standard')
:set_custom_color{r=24,g=172,b=188}
:set_flag('deconlog-bypass')
:set_parent('Regular')
:allow{
'gui/task-list/add',
'gui/task-list/edit',
'gui/warp-list/add',
'gui/warp-list/edit',
'command/save-quickbar',
'gui/vlayer-edit',
'command/vlayer-info',
'command/personal-logistic',
'command/auto-research',
'command/set-trains-to-automatic',
'command/lawnmower',
'command/waterfill',
'command/artillery-target-remote',
'command/clear-item-on-ground',
'command/clear-blueprint',
'gui/surveillance'
}
local hours3, hours15 = 3*216000, 15*60
Roles.new_role('Regular','Reg')
:set_permission_group('Standard')
:set_custom_color{r=79,g=155,b=163}
:set_parent('Guest')
:allow{
'command/kill',
'command/rainbow',
'command/go-to-spawn',
'command/me',
'standard-decon',
'bypass-entity-protection',
'bypass-nukeprotect'
}
:set_auto_assign_condition(function(player)
if player.online_time >= hours3 then
return true
else
local stats = Statistics:get(player, {})
local playTime, afkTime, mapCount = stats.Playtime or 0, stats.AfkTime or 0, stats.MapsPlayed or 0
return playTime - afkTime >= hours15 and mapCount >= 5
end
end)
--- Guest/Default role
local default = Roles.new_role('Guest','')
:set_permission_group('Guest')
:set_custom_color{r=185,g=187,b=160}
:allow{
'command/tag',
'command/tag-clear',
'command/search-help',
'command/list-roles',
'command/find-on-map',
'command/report',
'command/ratio',
'command/server-ups',
'command/save-data',
'command/preference',
'command/set-preference',
'command/connect',
'gui/player-list',
'gui/rocket-info',
'gui/science-info',
'gui/task-list',
'gui/warp-list',
'gui/readme',
'gui/vlayer',
'gui/research',
'gui/autofill',
'gui/module',
'gui/landfill',
'gui/production'
}
--- Jail role
Roles.new_role('Jail')
:set_permission_group('Restricted')
:set_custom_color{r=50,g=50,b=50}
:set_block_auto_assign(true)
:set_flag('defer_role_changes')
:disallow(default.allowed)
--- System defaults which are required to be set
Roles.set_root('System')
Roles.set_default('Guest')
Roles.define_role_order{
'System', -- Best to keep root at top
'Senior Administrator',
'Administrator',
'Moderator',
'Trainee',
'Board Member',
'Senior Backer',
'Sponsor',
'Supporter',
'Partner',
'Veteran',
'Member',
'Regular',
'Jail',
'Guest' -- Default must be last if you want to apply restrictions to other roles
}
Roles.override_player_roles{
['PHIDIAS0303']={'Moderator', 'Board Member', 'Member'},
['aldldl']={'Administrator', 'Moderator','Member'},
['arty714']={'Senior Administrator', 'Moderator', 'Member'},
['Cooldude2606']={'Senior Administrator', 'Moderator', 'Member'},
['Drahc_pro']={'Administrator', 'Moderator', 'Member'},
['mark9064']={'Administrator', 'Moderator','Member'},
['7h3w1z4rd']={'Moderator','Member'},
['FlipHalfling90']={'Moderator','Member'},
['hamsterbryan']={'Moderator','Member'},
['HunterOfGames']={'Moderator','Member'},
['NextIdea']={'Moderator','Member'},
['TheKernel32']={'Moderator','Member'},
['TheKernel64']={'Moderator','Member'},
['tovernaar123']={'Moderator','Member'},
['UUBlueFire']={'Moderator','Member'},
['AssemblyStorm']={'Moderator', 'Member'},
['banakeg']={'Moderator','Member'},
['connormkii']={'Moderator', 'Member'},
['cydes']={'Moderator','Member'},
['darklich14']={'Moderator','Member'},
['facere']={'Moderator','Member'},
['freek18']={'Moderator','Member'},
['Gizan']={'Moderator','Member'},
['LoicB']={'Moderator','Member'},
['M74132']={'Moderator','Member'},
['mafisch3']={'Moderator','Member'},
['maplesyrup01']={'Moderator','Member'},
['ookl']={'Moderator','Member'},
['Phoenix27833']={'Moderator','Member'},
['porelos']={'Moderator','Member'},
['Ruuyji']={'Moderator','Member'},
['samy115']={'Moderator','Member'},
['SilentLog']={'Moderator','Member'},
['Tcheko']={'Moderator','Member'},
['thadius856']={'Moderator','Member'},
['whoami32']={'Moderator','Member'},
['Windbomb']={'Moderator','Member'},
['XenoCyber']={'Moderator','Member'}
}

View File

@@ -0,0 +1,7 @@
return {
modules = {
["forcestats"] = true,
["logistorage"] = false,
["other"] = true,
}
}

View File

@@ -0,0 +1,130 @@
--- This file contains all the different settings for the autofill system and gui
-- @config Autofill
local table = require 'overrides.table' -- @dep overrides.table
local config = {
-- General config
icon = 'item/piercing-rounds-magazine', -- @setting icon that will be used for the toolbar
categories = {
ammo = 'ammo',
fuel = 'fuel',
shell = 'shell'
},
entities = {
car = 'car',
tank = 'tank',
spidertron = 'spidertron',
locomotive = 'locomotive',
gun_turret = 'gun-turret',
burner_mining_drill = 'burner-mining-drill',
stone_furnace = 'stone-furnace',
steel_furnace = 'steel-furnace'
},
default_entities = {}
}
local default_categories = {
{
category = config.categories.ammo,
entity = {config.entities.car, config.entities.tank, config.entities.gun_turret},
inv = {defines.inventory.car_ammo, defines.inventory.turret_ammo},
items = {
{ name = 'uranium-rounds-magazine', amount = 10, enabled = false },
{ name = 'piercing-rounds-magazine', amount = 10, enabled = false },
{ name = 'firearm-magazine', amount = 10, enabled = false },
}
},
{
category = config.categories.ammo,
entity = {config.entities.tank},
inv = {defines.inventory.car_ammo},
items = {
{ name = 'flamethrower-ammo', amount = 10, enabled = false },
}
},
{
category = config.categories.shell,
entity = {config.entities.tank},
inv = {defines.inventory.car_ammo},
items = {
{ name = 'cannon-shell', amount = 10, enabled = false },
{ name = 'explosive-cannon-shell', amount = 10, enabled = false },
{ name = 'uranium-cannon-shell', amount = 10, enabled = false },
{ name = 'explosive-uranium-cannon-shell', amount = 10, enabled = false },
}
},
{
category = config.categories.ammo,
entity = {config.entities.spidertron},
inv = {defines.inventory.car_ammo},
items = {
{ name = 'rocket', amount = 10, enabled = false },
{ name = 'explosive-rocket', amount = 10, enabled = false },
{ name = 'atomic-bomb', amount = 10, enabled = false },
}
},
{
category = config.categories.fuel,
entity = {config.entities.car, config.entities.tank, config.entities.locomotive, config.entities.burner_mining_drill, config.entities.stone_furnace, config.entities.steel_furnace},
inv = {defines.inventory.fuel},
items = {
{ name = 'nuclear-fuel', amount = 10, enabled = false },
{ name = 'rocket-fuel', amount = 10, enabled = false },
{ name = 'solid-fuel', amount = 10, enabled = false },
{ name = 'coal', amount = 10, enabled = false },
}
}
}
local function get_items_by_inv(entity, inv)
local items = entity.items
for _, category in pairs(default_categories) do
if table.contains(category.entity, entity.entity) then
if table.contains(category.inv, inv) then
for _, item in pairs(category.items) do
items[item.name] = {
entity = entity.entity,
category = category.category,
inv = inv,
name = item.name,
amount = item.amount,
enabled = item.enabled
}
end
end
end
end
return items
end
local function generate_default_setting(entity_name, inv, enabled)
if not config.default_entities[entity_name] then
config.default_entities[entity_name] = {
entity = entity_name,
enabled = enabled,
items = {}
}
end
get_items_by_inv(config.default_entities[entity_name], inv)
end
generate_default_setting(config.entities.car, defines.inventory.fuel, true)
generate_default_setting(config.entities.car, defines.inventory.car_ammo, true)
generate_default_setting(config.entities.locomotive, defines.inventory.fuel, true)
generate_default_setting(config.entities.tank, defines.inventory.fuel, true)
generate_default_setting(config.entities.tank, defines.inventory.car_ammo, true)
generate_default_setting(config.entities.spidertron, defines.inventory.car_ammo, true)
generate_default_setting(config.entities.gun_turret, defines.inventory.turret_ammo, true)
generate_default_setting(config.entities.burner_mining_drill, defines.inventory.fuel, true)
generate_default_setting(config.entities.stone_furnace, defines.inventory.fuel, true)
generate_default_setting(config.entities.steel_furnace, defines.inventory.fuel, true)
return config

View File

@@ -0,0 +1,205 @@
--- Config for the different action buttons that show on the player list;
-- each button has the button define(s) given along side an auth function, and optional reason callback;
-- if a reason callback is used then Store.set(action_name_store,player.name,'BUTTON_NAME') should be called during on_click;
-- buttons can be removed from the gui by commenting them out of the config at the bottom of this file;
-- the key used for the name of the button is the permission name used by the role system;
-- @config Player-List
local Gui = require 'expcore.gui' --- @dep expcore.gui
local Roles = require 'expcore.roles' --- @dep expcore.roles
local Reports = require 'modules.control.reports' --- @dep modules.control.reports
local Warnings = require 'modules.control.warnings' --- @dep modules.control.warnings
local Jail = require 'modules.control.jail' --- @dep modules.control.jail
local Colors = require 'utils.color_presets' --- @dep utils.color_presets
local format_chat_player_name = _C.format_chat_player_name --- @dep expcore.common
local SelectedPlayer, SelectedAction
local function set_datastores(player, action)
SelectedPlayer, SelectedAction = player, action
end
-- auth that will only allow when on player's of lower roles
local function auth_lower_role(player,selected_player_name)
local player_highest = Roles.get_player_highest_role(player)
local action_player_highest = Roles.get_player_highest_role(selected_player_name)
if player_highest.index < action_player_highest.index then
return true
end
end
-- gets the action player and a coloured name for the action to be used on
local function get_action_player_name(player)
local selected_player_name = SelectedPlayer:get(player)
local selected_player = game.players[selected_player_name]
local selected_player_color = format_chat_player_name(selected_player)
return selected_player_name, selected_player_color
end
-- teleports one player to another
local function teleport(from_player,to_player)
local surface = to_player.surface
local position = surface.find_non_colliding_position('character',to_player.position,32,1)
if not position then return false end -- return false if no new position
if from_player.driving then from_player.driving = false end -- kicks a player out a vehicle if in one
from_player.teleport(position,surface)
return true
end
local function new_button(sprite,tooltip)
return Gui.element{
type = 'sprite-button',
style = 'tool_button',
sprite = sprite,
tooltip = tooltip
}:style{
padding = -1,
height = 28,
width = 28
}
end
--- Teleports the user to the action player
-- @element goto_player
local goto_player = new_button('utility/export',{'player-list.goto-player'})
:on_click(function(player)
local selected_player_name = get_action_player_name(player)
local selected_player = game.players[selected_player_name]
if not player.character or not selected_player.character then
player.print({'expcore-commands.reject-player-alive'},Colors.orange_red)
else
teleport(player,selected_player)
end
end)
--- Teleports the action player to the user
-- @element bring_player
local bring_player = new_button('utility/import',{'player-list.bring-player'})
:on_click(function(player)
local selected_player_name = get_action_player_name(player)
local selected_player = game.players[selected_player_name]
if not player.character or not selected_player.character then
player.print({'expcore-commands.reject-player-alive'},Colors.orange_red)
else
teleport(selected_player,player)
end
end)
--- Reports the action player, requires a reason to be given
-- @element report_player
local report_player = new_button('utility/spawn_flag',{'player-list.report-player'})
:on_click(function(player)
local selected_player_name = get_action_player_name(player)
if Reports.is_reported(selected_player_name,player.name) then
player.print({'expcom-report.already-reported'},Colors.orange_red)
else
SelectedAction:set(player, 'command/report')
end
end)
local function report_player_callback(player,reason)
local selected_player_name, selected_player_color = get_action_player_name(player)
local by_player_name_color = format_chat_player_name(player)
game.print{'expcom-report.non-admin', selected_player_color,reason}
Roles.print_to_roles_higher('Trainee',{'expcom-report.admin', selected_player_color,by_player_name_color,reason})
Reports.report_player(selected_player_name,player.name,reason)
end
--- Gives the action player a warning, requires a reason
-- @element warn_player
local warn_player = new_button('utility/spawn_flag',{'player-list.warn-player'})
:on_click(function(player)
SelectedAction:set(player, 'command/give-warning')
end)
local function warn_player_callback(player,reason)
local selected_player_name, selected_player_color = get_action_player_name(player)
local by_player_name_color = format_chat_player_name(player)
game.print{'expcom-warnings.received', selected_player_color,by_player_name_color,reason}
Warnings.add_warning(selected_player_name,player.name,reason)
end
--- Jails the action player, requires a reason
-- @element jail_player
local jail_player = new_button('utility/multiplayer_waiting_icon',{'player-list.jail-player'})
:on_click(function(player)
local selected_player_name, selected_player_color = get_action_player_name(player)
if Jail.is_jailed(selected_player_name) then
player.print({'expcom-jail.already-jailed', selected_player_color},Colors.orange_red)
else
SelectedAction:set(player, 'command/jail')
end
end)
local function jail_player_callback(player,reason)
local selected_player_name, selected_player_color = get_action_player_name(player)
local by_player_name_color = format_chat_player_name(player)
game.print{'expcom-jail.give', selected_player_color,by_player_name_color,reason}
Jail.jail_player(selected_player_name,player.name,reason)
end
--- Kicks the action player, requires a reason
-- @element kick_player
local kick_player = new_button('utility/warning_icon',{'player-list.kick-player'})
:on_click(function(player)
SelectedAction:set(player, 'command/kick')
end)
local function kick_player_callback(player,reason)
local selected_player = get_action_player_name(player)
game.kick_player(selected_player,reason)
end
--- Bans the action player, requires a reason
-- @element ban_player
local ban_player = new_button('utility/danger_icon',{'player-list.ban-player'})
:on_click(function(player)
SelectedAction:set(player, 'command/ban')
end)
local function ban_player_callback(player,reason)
local selected_player = get_action_player_name(player)
game.ban_player(selected_player,reason)
end
return {
set_datastores = set_datastores,
buttons = {
['command/teleport'] = {
auth=function(player,selected_player)
return player.name ~= selected_player.name
end, -- cant teleport to your self
goto_player,
bring_player
},
['command/report'] = {
auth=function(player,selected_player)
if player == selected_player then return false end
if not Roles.player_allowed(player,'command/give-warning') then
return not Roles.player_has_flag(selected_player,'report-immune')
end
end, -- can report any player that isn't immune and you aren't able to give warnings
reason_callback=report_player_callback,
report_player
},
['command/give-warning'] = {
auth=auth_lower_role, -- warn a lower user, replaces report
reason_callback=warn_player_callback,
warn_player
},
['command/jail'] = {
auth=auth_lower_role,
reason_callback=jail_player_callback,
jail_player
},
['command/kick'] = {
auth=auth_lower_role,
reason_callback=kick_player_callback,
kick_player
},
['command/ban'] = {
auth=auth_lower_role,
reason_callback=ban_player_callback,
ban_player
}
}
}

View File

@@ -0,0 +1,35 @@
--- This file controls what will show in each section of the rocket info gui
-- @config Rockets
return {
stats = { --- @setting stats The data that will show in the stats section
show_stats=true, --- @setting show_stats false will hide this section all together
show_first_rocket = true, --- @setting show_first_rocket false will not show when the first rocket was launched
show_last_rocket = true, --- @setting show_last_rocket false will not show when the last rocket was launched
show_fastest_rocket = true, --- @setting show_fastest_rocket false will not show the time taken for the fastest rocket
show_total_rockets = true, --- @setting show_total_rockets false will not show the total number of rockets launched
show_game_avg = true, --- @setting show_game_avg false will hide the avg across the entire map time
rolling_avg = { --- @setting rolling_avg each number will be one statistic; 5 means the avg time taken for the last 5 rockets
5,10,25
}
},
milestones = { --- @setting milestones each number will be one statistic; 5 means the time that the 5th rocket was launched
show_milestones=true, --- @setting show_milestones false will hide this section all together
1,2,5,
10,20,50,
100,200,500,
1000,1500,2000,2500,
3000,3500,4000,4500,
5000
},
progress = { --- @setting progress The data and buttons in the build progress section
show_progress = true, --- @setting show_progress false will hide this section altogether
allow_zoom_to_map = true, --- @setting allow_zoom_to_map false will disable the zoom to map feature
allow_remote_launch = true, --- @setting allow_remote_launch false removes the remote launch button for all players
remote_launch_admins_only = false, --- @setting remote_launch_admins_only true will remove the remote launch button for all non (game) admins
remote_launch_role_permission = 'gui/rocket-info/remote_launch', --- @setting remote_launch_role_permission value used by custom permission system to allow or disallow the button
allow_toggle_active = true, --- @setting allow_toggle_active false removes the remote toggle auto launch button for all players
toggle_active_admins_only = false, --- @setting toggle_active_admins_only true will remove the toggle auto launch button for all non (game) admins
toggle_active_role_permission = 'gui/rocket-info/toggle-active' --- @setting toggle_active_role_permission value used by custom permission system to allow or disallow the button
}
}

View File

@@ -0,0 +1,16 @@
--- Config file for the science info gui
-- @config Science
return {
-- list of all science packs to be shown in the gui
show_eta = true, --- @setting show_eta when true the eta for research completion will be shown
color_cutoff = 0.8, --- @setting color_cutoff the amount that production can fall before the text changes color
color_flux = 0.1, --- @setting color_flux the amount of fluctuation allowed in production before the icon changes color
'automation-science-pack',
'logistic-science-pack',
'military-science-pack',
'chemical-science-pack',
'production-science-pack',
'utility-science-pack',
'space-science-pack',
}

View File

@@ -0,0 +1,13 @@
--- Config file for the tasks gui
-- @config Tasks
return {
-- Adding tasks
allow_add_task = 'all', --- @setting allow_add_task dictates who is allowed to add new tasks; values: all, admin, expcore.roles, none
expcore_roles_allow_add_task = 'gui/task-list/add', --- @setting expcore_roles_allow_add_task if expcore.roles is used then this is the required permission
-- Editing tasks
allow_edit_task = 'expcore.roles', --- @setting allow_edit_task dictates who is allowed to edit existing tasks; values: all, admin, expcore.roles, none
expcore_roles_allow_edit_task = 'gui/task-list/edit', --- @setting expcore_roles_allow_edit_task if expcore.roles is used then this is the required permission
user_can_edit_own_tasks = true --- @settings if true then the user who made the task can edit it regardless of the allow_edit_task setting
}

View File

@@ -0,0 +1,52 @@
--- This file contains all the different settings for the warp system and gui
-- @config Warps
return {
-- General config
update_smoothing = 10, --- @setting update_smoothing the amount of smoothing applied to updates to the cooldown timer, higher is better, max is 60
minimum_distance = 100, --- @setting minimum_distance the minimum distance that is allowed between warps on the same force
default_icon = {type = 'item', name = 'discharge-defense-equipment'}, --- @setting default_icon the default icon that will be used for warps
-- Warp cooldowns
bypass_warp_cooldown = 'expcore.roles', --- @setting bypass_warp_cooldown dictates who the warp cooldown is applied to; values: all, admin, expcore.roles, none
expcore_roles_bypass_warp_cooldown = 'gui/warp-list/bypass-cooldown', --- @setting expcore_roles_bypass_warp_cooldown if expcore.roles is used then this is the required permission
cooldown_duration = 60, --- @setting cooldown_duration the duration of the warp cooldown in seconds
-- Warp proximity
bypass_warp_proximity = 'expcore.roles', --- @setting bypass_warp_proximity dictates who the warp proximity is applied to; values: all, admin, expcore.roles, none
expcore_roles_bypass_warp_proximity = 'gui/warp-list/bypass-proximity', --- @setting expcore_roles_bypass_warp_proximity if expcore.roles is used then this is the required permission
standard_proximity_radius = 4, --- @setting standard_proximity_radius the minimum distance a player is allowed to be to a warp in order to use it
spawn_proximity_radius = 20, --- @setting spawn_proximity_radius the minimum distance a player is allowed to be from they spawn point to use warps
-- Adding warps
allow_add_warp = 'expcore.roles', --- @setting allow_add_warp dictates who is allowed to add warps; values: all, admin, expcore.roles, none
expcore_roles_allow_add_warp = 'gui/warp-list/add', --- @setting expcore_roles_allow_add_warp if expcore.roles is used then this is the required permission
-- Editing warps
allow_edit_warp = 'expcore.roles', --- @setting allow_edit_warp dictates who is allowed to edit warps; values: all, admin, expcore.roles, none
expcore_roles_allow_edit_warp = 'gui/warp-list/edit', --- @setting expcore_roles_allow_edit_warp if expcore.roles is used then this is the required permission
user_can_edit_own_warps = false, --- @settings user_can_edit_own_warps if true then the user who made the warp can edit it regardless of the allow_edit_warp setting
-- Warp area generation
entities = { --- @setting entities The entities which are created for warp areas
{'small-lamp', -4, -2}, {'small-lamp', -2, -4}, {'medium-electric-pole',-3,-3}, -- Top left corner
{'small-lamp', 3, -2}, {'small-lamp', 1, -4}, {'medium-electric-pole',2,-3}, -- Top right corner
{'small-lamp', 3, 1}, {'small-lamp', 1, 3}, {'medium-electric-pole',2,2}, -- Bottom right corner
{'small-lamp', -4, 1}, {'small-lamp', -2, 3}, {'medium-electric-pole',-3,2}, -- Bottom left corner
},
tiles = { --- @setting tiles The tiles which are created for warp areas
{'black-refined-concrete',-4,-2}, {'black-refined-concrete',-4,-1}, {'black-refined-concrete',-4,0}, {'black-refined-concrete',-4,1},
{'black-refined-concrete',-3,-3}, {'purple-refined-concrete',-3,-2}, {'purple-refined-concrete',-3,-1}, {'purple-refined-concrete',-3,0},
{'purple-refined-concrete',-3,1}, {'black-refined-concrete',-3,2}, {'black-refined-concrete',-2,-4}, {'purple-refined-concrete',-2,-3},
{'purple-refined-concrete',-2,-2}, {'purple-refined-concrete',-2,-1}, {'purple-refined-concrete',-2,0}, {'purple-refined-concrete',-2,1},
{'purple-refined-concrete',-2,2}, {'black-refined-concrete',-2,3}, {'black-refined-concrete',-1,-4}, {'purple-refined-concrete',-1,-3},
{'purple-refined-concrete',-1,-2}, {'purple-refined-concrete',-1,-1}, {'purple-refined-concrete',-1,0}, {'purple-refined-concrete',-1,1},
{'purple-refined-concrete',-1,2}, {'black-refined-concrete',-1,3}, {'black-refined-concrete',0,-4}, {'purple-refined-concrete',0,-3},
{'purple-refined-concrete',0,-2}, {'purple-refined-concrete',0,-1}, {'purple-refined-concrete',0,0}, {'purple-refined-concrete',0,1},
{'purple-refined-concrete',0,2}, {'black-refined-concrete',0,3}, {'black-refined-concrete',1,-4}, {'purple-refined-concrete',1,-3},
{'purple-refined-concrete',1,-2}, {'purple-refined-concrete',1,-1}, {'purple-refined-concrete',1,0}, {'purple-refined-concrete',1,1},
{'purple-refined-concrete',1,2}, {'black-refined-concrete',1,3}, {'black-refined-concrete',2,-3}, {'purple-refined-concrete',2,-2},
{'purple-refined-concrete',2,-1}, {'purple-refined-concrete',2,0}, {'purple-refined-concrete',2,1}, {'black-refined-concrete',2,2},
{'black-refined-concrete',3,-2}, {'black-refined-concrete',3,-1}, {'black-refined-concrete',3,0}, {'black-refined-concrete',3,1}
}
}

View File

@@ -0,0 +1,9 @@
--- Config to control when players items are removed, this is a list of event names that will trigger inventory clear
-- @config inventory_clear
local events = defines.events
return {
events.on_player_banned,
events.on_player_kicked,
--events.on_player_left_game
}

View File

@@ -0,0 +1,8 @@
return {
Cooldude2606 = 'Lua lets you set metatables on numbers, did you know that? Cooldude2606 knows this.',
samy115 = 'Tremble in fear as the banhammer is now here, its owner: samy115',
XenoCyber = '"Fire Fire Fire" oops wrong game, have no fear XenoCyber is here',
HunterOfGames = 'Unable to support HunterOfGames. You must construct additional miners.',
ookl = 'ookl says: "Pineapples are amazing, hello everyone!"',
arty714 = 'Arty\'s Potato made it!'
}

View File

@@ -0,0 +1,6 @@
--- Settings for lawnmower
-- @config lawnmower
return {
destroy_decoratives = false
}

View File

@@ -0,0 +1,30 @@
--- Settings for logging
-- @config logging
return {
file_name = 'log/logging.log',
rocket_launch_display = {
[1] = true,
[2] = true,
[5] = true,
[10] = true,
[20] = true,
[50] = true,
[100] = true,
[200] = true
},
rocket_launch_display_rate = 500,
disconnect_reason = {
[defines.disconnect_reason.quit] = ' left the game',
[defines.disconnect_reason.dropped] = ' was dropped from the game',
[defines.disconnect_reason.reconnect] = ' is reconnecting',
[defines.disconnect_reason.wrong_input] = ' was having a wrong input',
[defines.disconnect_reason.desync_limit_reached] = ' had desync limit reached',
[defines.disconnect_reason.cannot_keep_up] = ' cannot keep up',
[defines.disconnect_reason.afk] = ' was afk',
[defines.disconnect_reason.kicked] = ' was kicked',
[defines.disconnect_reason.kicked_and_deleted] = ' was kicked and deleted',
[defines.disconnect_reason.banned] = ' was banned',
[defines.disconnect_reason.switching_servers] = ' is switching servers'
}
}

View File

@@ -0,0 +1,7 @@
--- Settings for miner
-- @config miner
return {
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
}

View File

@@ -0,0 +1,98 @@
return {
-- type of machine to handle together
default_module_row_count = 9,
module_slot_max = 4,
copy_paste_module = true,
copy_paste_rotation = false,
machine = {
['electric-mining-drill'] = {
['module'] = 'effectivity-module',
['prod'] = true
},
['pumpjack'] = {
['module'] = 'effectivity-module',
['prod'] = true
},
['assembling-machine-2'] = {
['module'] = 'productivity-module',
['prod'] = true
},
['assembling-machine-3'] = {
['module'] = 'productivity-module-3',
['prod'] = true
},
['electric-furnace'] = {
['module'] = 'productivity-module-3',
['prod'] = true
},
['beacon'] = {
['module'] = 'speed-module-3',
['prod'] = false
},
['oil-refinery'] = {
['module'] = 'productivity-module-3',
['prod'] = true
},
['chemical-plant'] = {
['module'] = 'productivity-module-3',
['prod'] = true
},
['centrifuge'] = {
['module'] = 'productivity-module-3',
['prod'] = true
},
['lab'] = {
['module'] = 'productivity-module-3',
['prod'] = true
},
['rocket-silo'] = {
['module'] = 'productivity-module-3',
['prod'] = true
}
},
module_allowed = {
['advanced-circuit'] = true,
['automation-science-pack'] = true,
['battery'] = true,
['chemical-science-pack'] = true,
['copper-cable'] = true,
['copper-plate'] = true,
['electric-engine-unit'] = true,
['electronic-circuit'] = true,
['empty-barrel'] = true,
['engine-unit'] = true,
['explosives'] = true,
['flying-robot-frame'] = true,
['iron-gear-wheel'] = true,
['iron-plate'] = true,
['iron-stick'] = true,
['logistic-science-pack'] = true,
['low-density-structure'] = true,
['lubricant'] = true,
['military-science-pack'] = true,
['nuclear-fuel'] = true,
['plastic-bar'] = true,
['processing-unit'] = true,
['production-science-pack'] = true,
['rocket-control-unit'] = true,
['rocket-fuel'] = true,
['rocket-part'] = true,
['steel-plate'] = true,
['stone-brick'] = true,
['sulfur'] = true,
['sulfuric-acid'] = true,
['uranium-fuel-cell'] = true,
['utility-science-pack'] = true,
['basic-oil-processing'] = true,
['advanced-oil-processing'] = true,
['coal-liquefaction'] = true,
['heavy-oil-cracking'] = true,
['light-oil-cracking'] = true,
['solid-fuel-from-light-oil'] = true,
['solid-fuel-from-petroleum-gas'] = true,
['solid-fuel-from-heavy-oil'] = true,
['uranium-processing'] = true,
['nuclear-fuel-reprocessing'] = true,
['kovarex-enrichment-process'] = true
}
}

View File

@@ -0,0 +1,34 @@
return {
inventories = {
{
inventory = defines.inventory.character_ammo,
event = defines.events.on_player_ammo_inventory_changed,
items = {
["atomic-bomb"] = true
},
},
{
inventory = defines.inventory.character_armor,
event = defines.events.on_player_armor_inventory_changed,
items = {},
},
{
inventory = defines.inventory.character_guns,
event = defines.events.on_player_gun_inventory_changed,
items = {},
},
{
inventory = defines.inventory.character_main,
event = defines.events.on_player_main_inventory_changed,
items = {
["atomic-bomb"] = true
},
},
},
ignore_permisison = "bypass-nukeprotect", -- @setting ignore_permisison The permission that nukeprotect will ignore
ignore_admins = true, -- @setting ignore_admins Ignore admins, true by default. Allows usage outside of the roles module
disable_nuke_research = false, -- @setting disable_nuke_research Disable the nuke research, true by default
disable_nuke_research_names = {
["atomic-bomb"] = true
} -- @setting disable_nuke_research_names The names of the researches to disabled
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
--- This controls how pollution is viewed on the map
-- @config Pollution-Grading
return {
reference_point = {x=0,y=0}, --- @setting reference_point where pollution is read from
max_scalar = 0.5, --- @setting max_scalar the scale between true max and max
min_scalar = 0.17, --- @setting min_scalar the scale between the lowest max and min
update_delay = 15 --- @setting update_delay time in minutes between view updates
}

View File

@@ -0,0 +1,10 @@
--- A combination of config settings for different popup values like chat and damage
-- @config Popup-Messages
return {
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
}

View File

@@ -0,0 +1,33 @@
--- Preset colours that players get when they join the server, if not in the list then will be given a random colour (which isnt disallowed)
-- @config Preset-Player-Colours
return {
players={ --- @setting players list of all players and the colour in rgb256 that they will recive upon joining
PHIDIAS0303={r=255,g=255,b=255},
BADgamerNL={r=255,g=20,b=147},
arty714={r=150,g=68,b=161},
Cooldude2606={r=57,g=192,b=207},
mark9064={r=99,g=0,b=255},
eissturm={r=25,g=25,b=112},
Sakama={r=20,g=213,b=80},
freek18={r=50,g=0,b=255},
aldldl={r=0,g=131,b=255},
NAD4X4={r=135,g=206,b=250},
cydes={r=82,g=249,b=155},
UUBlueFire={r=0,g=204,b=255},
CmonMate497={r=103,g=224,b=194},
s4sh={r=255,g=120,b=0},
ArPiiX={r=0,g=255,b=0},
NextIdea={r=255,g=255,b=255},
hamsterbryan={r=0,g=255,b=0},
XenoCyber={r=0,g=128,b=255}
},
disallow = { --- @setting disallow colours which will not given to players; the value does not matter it is only the key which is checked
black = {r = 0, g = 0, b = 0},
white = {r = 255, g = 255, b = 255},
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

@@ -0,0 +1,6 @@
--- Preset quickbar items that players can load
-- @config Preset-Player-Quickbar
return {
dangerarea = {"transport-belt", "underground-belt", "splitter", "pipe", "pipe-to-ground", "inserter", "fast-inserter", "long-handed-inserter", "stack-inserter", "roboport", "small-electric-pole", "medium-electric-pole", "big-electric-pole", "substation", nil, "rail", "rail-signal", "rail-chain-signal", "landfill", "cliff-explosives", "fast-transport-belt", "fast-underground-belt", "fast-splitter", "pipe", "pipe-to-ground", "fast-inserter", "long-handed-inserter", "stack-inserter", "stack-filter-inserter", "roboport", [81] = "red-wire", [82] = "green-wire", [83] = "arithmetic-combinator", [84] = "decider-combinator", [85] = "constant-combinator", [86] = "power-switch", [91] = "logistic-chest-active-provider", [92] = "logistic-chest-passive-provider", [93] = "logistic-chest-storage", [94] = "logistic-chest-buffer", [95] = "logistic-chest-requester", [96] = "roboport"}
}

View File

@@ -0,0 +1,19 @@
return {
ignore_admins = true, --- @setting ignore_admins If admins are ignored by the protection filter
ignore_permission = 'bypass-entity-protection', --- @setting ignore_permission Players with this permission will be ignored by the protection filter, leave nil if expcore.roles is not used
repeat_count = 5, --- @setting repeat_count Number of protected entities that must be removed within repeat_lifetime in order to trigger repeated removal protection
repeat_lifetime = 3600*20, --- @setting repeat_lifetime The length of time, in ticks, that protected removals will be remembered for
refresh_rate = 3600*5, --- @setting refresh_rate How often the age of protected removals are checked against repeat_lifetime
always_protected_names = { --- @setting always_protected_names Names of entities which are always protected
},
always_protected_types = { --- @setting always_protected_types Types of entities which are always protected
'boiler', 'generator', 'offshore-pump', 'power-switch', 'reactor', 'rocket-silo'
},
always_trigger_repeat_names = { --- @setting always_trigger_repeat_names Names of entities which always trigger repeated removal protection
},
always_trigger_repeat_types = { --- @setting always_trigger_repeat_types Types of entities which always trigger repeated removal protection
'reactor', 'rocket-silo'
}
}

View File

@@ -0,0 +1,16 @@
--- Config file for the repair command
-- @config Repair
return {
disallow = { --- @setting disallow items in this list will never be repaired
['loader']=true,
['fast-loader']=true,
['express-loader']=true,
['electric-energy-interface']=true,
['infinity-chest']=true
},
max_range=50, --- @setting max_range the max range that can be used with the repair command
allow_blueprint_repair=false, --- @setting allow_blueprint_repair when true will allow blueprints (things not destroyed by biters) to be build instantly using the repair command
allow_ghost_revive=true, --- @setting allow_ghost_revive when true will allow ghosts (things destroyed by biters) to be build instantly using the repair command
allow_heal_entities=true --- @setting allow_heal_entities when true will heal entities to full health that are within range
}

View File

@@ -0,0 +1,81 @@
--- Res Settings
-- @config Research
return {
enabled = true,
pollution_ageing_by_research = false,
queue_amount = 3,
-- this enable 20 more inventory for each mining productivity level up to 4
bonus_inventory = {
enabled = true,
name = 'character_inventory_slots_bonus',
rate = 5,
limit = 20
},
file_name = 'log/research.log',
milestone = {
['automation'] = 600,
['logistics'] = 300,
['steel-processing'] = 300,
['logistic-science-pack'] = 300,
['electronics'] = 300,
['fast-inserter'] = 300,
['steel-axe'] = 300,
['automation-2'] = 300,
['advanced-material-processing'] = 300,
['engine'] = 300,
['fluid-handling'] = 300,
['oil-processing'] = 300,
['sulfur-processing'] = 300,
['plastics'] = 300,
['advanced-electronics'] = 300,
['chemical-science-pack'] = 300,
['modules'] = 300,
['logistics-2'] = 300,
['railway'] = 300,
['research-speed-1'] = 300,
['research-speed-2'] = 300,
['battery'] = 300,
['concrete'] = 300,
['flammables'] = 300,
['low-density-structure'] = 300,
['advanced-material-processing-2'] = 300,
['productivity-module'] = 300,
['production-science-pack'] = 300,
['advanced-electronics-2'] = 300,
['advanced-oil-processing'] = 300,
['electric-engine'] = 300,
['robotics'] = 300,
['construction-robotics'] = 300,
['worker-robots-speed-1'] = 300,
['worker-robots-speed-2'] = 300,
['utility-science-pack'] = 300,
['productivity-module-2'] = 300,
['speed-module-2'] = 300,
['rocket-fuel'] = 300,
['effect-transmission'] = 300,
['productivity-module-3'] = 300,
['rocket-control-unit'] = 300,
['speed-module-3'] = 300,
['rocket-silo'] = 300,
['space-science-pack'] = 300,
},
inf_res = {
-- Mining Productivity
['mining-productivity-4'] = 4,
-- Robot Speed
['worker-robots-speed-6'] = 6,
-- Laser Damage
['energy-weapons-damage-7'] = 7,
-- Explosive Damage
['stronger-explosives-7'] = 7,
-- Bullet Damage
['physical-projectile-damage-7'] = 7,
-- Flame Damage
['refined-flammables-7'] = 7,
-- Artillery Range
['artillery-shell-range-1'] = 1,
-- Artillery Speed
['artillery-shell-speed-1'] = 1
}
}

View File

@@ -0,0 +1,116 @@
--- This file controls the placement/degrading of tiles as players build and walk
-- @config Scorched-Earth
return {
weakness_value=70, --- @setting weakness_value lower value will make tiles more likely to degrade
strengths={ --- @setting strengths this decides how "strong" a tile is, bigger number means less likely to degrade
-- debug: /interface require('modules.addons.worn-paths')(player.name,true)
-- note: tiles are effected by the tiles around them, so player paths will not degrade as fast when made wider
-- note: values are relative to the tile with the highest value, recommended to keep highest tile as a "nice" number
-- note: tiles not in list will never degrade under any conditions (which is why some are omitted such as water)
["refined-concrete"]=100,
["refined-hazard-concrete-left"]=100,
["refined-hazard-concrete-right"]=100,
["concrete"]=90,
["hazard-concrete-left"]=90,
["hazard-concrete-right"]=90,
["stone-path"]=80,
["red-desert-0"]=80,
["dry-dirt"]=50,
-- grass four (main grass tiles)
["grass-1"]=50,
["grass-2"]=40,
["grass-3"]=30,
["grass-4"]=25,
-- red three (main red tiles)
["red-desert-1"]=40,
["red-desert-2"]=30,
["red-desert-3"]=25,
-- sand three (main sand tiles)
["sand-1"]=40,
["sand-2"]=30,
["sand-3"]=25,
-- dirt 3 (main dirt tiles)
["dirt-1"]=40,
["dirt-2"]=30,
["dirt-3"]=25,
-- last three/four (all sets of three merge here)
["dirt-4"]=25,
["dirt-5"]=30,
["dirt-6"]=40,
--["dirt-7"]=0, -- last tile, nothing to degrade to
-- land fill chain
-- ["landfill"]=50,
--["water-shallow"]=90,
--["water-mud"]=0, -- last tile, nothing to degrade to
},
degrade_order={ --- @setting degrade_order when a tile degrades it will turn into the next tile given here
["refined-concrete"]='concrete',
["refined-hazard-concrete-left"]='hazard-concrete-left',
["refined-hazard-concrete-right"]='hazard-concrete-right',
["concrete"]='stone-path',
["hazard-concrete-left"]='stone-path',
["hazard-concrete-right"]='stone-path',
["stone-path"]='dry-dirt',
["red-desert-0"]='dry-dirt',
["dry-dirt"]='dirt-4',
-- grass four (main grass tiles)
["grass-1"]='grass-2',
["grass-2"]='grass-3',
["grass-3"]='grass-4',
["grass-4"]='dirt-4',
-- red three (main red tiles)
["red-desert-1"]='red-desert-2',
["red-desert-2"]='red-desert-3',
["red-desert-3"]='dirt-4',
-- sand three (main sand tiles)
["sand-1"]='sand-2',
["sand-2"]='sand-3',
["sand-3"]='dirt-4',
-- dirt 3 (main dirt tiles)
["dirt-1"]='dirt-2',
["dirt-2"]='dirt-3',
["dirt-3"]='dirt-4',
-- last three/four (all sets of three merge here)
["dirt-4"]='dirt-5',
["dirt-5"]='dirt-6',
["dirt-6"]='dirt-7',
--["dirt-7"]=0, -- last tile, nothing to degrade to
-- land fill chain
-- ["landfill"]='grass-2', -- 'water-shallow'
--["water-shallow"]='water-mud',
--["water-mud"]=0, -- last tile, nothing to degrade to
},
entities={ --- @setting entities entities in this list will degrade the tiles under them when they are placed
['stone-furnace']=true,
['steel-furnace']=true,
['electric-furnace']=true,
['assembling-machine-1']=true,
['assembling-machine-2']=true,
['assembling-machine-3']=true,
['beacon']=true,
['centrifuge']=true,
['chemical-plant']=true,
['oil-refinery']=true,
['storage-tank']=true,
['nuclear-reactor']=true,
['steam-engine']=true,
['steam-turbine']=true,
['boiler']=true,
['heat-exchanger']=true,
['stone-wall']=true,
['gate']=true,
['gun-turret']=true,
['laser-turret']=true,
['flamethrower-turret']=true,
['radar']=true,
['lab']=true,
['big-electric-pole']=true,
['substation']=true,
['rocket-silo']=true,
['pumpjack']=true,
['electric-mining-drill']=true,
['roboport']=true,
['accumulator']=true
}
}

View File

@@ -0,0 +1,254 @@
--- Used to config the spawn generation settings yes there is alot here i know just ignore the long tables at the end (they were generated with a command)
-- @config Spawn-Area
return {
spawn_area = { --- @setting 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
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
},
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
refill_time = 60*60*5, --- @setting refill_time The time in ticks between each refill of the turrets, only change if having lag issues
offset = {x=0, y=0}, --- @setting offset The position offset to apply to turrets
locations = { --- @setting locations The locations of all turrets, this list can change during runtime
{surface=1,position={x=-3,y=-3}},
{surface=1,position={x=3,y=-3}},
{surface=1,position={x=-3,y=3}},
{surface=1,position={x=3,y=3}}
}
},
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
{-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}
-- 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
{-8,-9}, {-8,-10}, {-9,-8}, {-9,-9}, {-9,-10}, {-10,-8}, {-10,-9}, {-10,-10}, -- Top Left
{-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}
{'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},
{'iron-chest',-6,0},{'iron-chest',-6,3},{'small-lamp',-6,9},{'stone-wall',-6,11},{'stone-wall',-5,-8},{'small-lamp',-5,-1},{'iron-chest',-5,0},{'iron-chest',-5,3},{'small-lamp',-5,4},{'stone-wall',-5,11},
{'stone-wall',-4,-8},{'small-electric-pole',-4,-5},{'iron-chest',-4,0},{'iron-chest',-4,3},{'small-electric-pole',-4,8},{'stone-wall',-4,11},{'stone-wall',-3,-8},{'small-lamp',-3,-6},{'small-lamp',-3,-3},{'small-lamp',-3,6},
{'small-lamp',-3,9},{'stone-wall',-3,11},{'stone-wall',-2,-8},{'iron-chest',-2,-6},{'iron-chest',-2,-5},{'iron-chest',-2,-4},{'iron-chest',-2,-3},{'iron-chest',-2,-2},{'iron-chest',-2,5},{'iron-chest',-2,6},
{'iron-chest',-2,7},{'iron-chest',-2,8},{'iron-chest',-2,9},{'stone-wall',-2,11},{'stone-wall',1,-8},{'iron-chest',1,-6},
{'iron-chest',1,-5},{'iron-chest',1,-4},{'iron-chest',1,-3},{'iron-chest',1,-2},{'iron-chest',1,5},{'iron-chest',1,6},{'iron-chest',1,7},{'iron-chest',1,8},{'iron-chest',1,9},{'stone-wall',1,11},
{'stone-wall',2,-8},{'small-lamp',2,-6},{'small-lamp',2,-3},{'small-lamp',2,6},{'small-lamp',2,9},{'stone-wall',2,11},{'stone-wall',3,-8},{'small-electric-pole',3,-5},{'iron-chest',3,0},{'iron-chest',3,3},
{'small-electric-pole',3,8},{'stone-wall',3,11},{'stone-wall',4,-8},{'small-lamp',4,-1},{'iron-chest',4,0},{'iron-chest',4,3},{'small-lamp',4,4},{'stone-wall',4,11},{'stone-wall',5,-8},{'small-lamp',5,-6},
{'iron-chest',5,0},{'iron-chest',5,3},{'small-lamp',5,9},{'stone-wall',5,11},{'stone-wall',6,-8},{'small-electric-pole',6,-2},{'iron-chest',6,0},{'iron-chest',6,3},{'small-electric-pole',6,5},{'stone-wall',6,11},
{'small-lamp',7,-4},{'small-lamp',7,-1},{'iron-chest',7,0},{'iron-chest',7,3},{'small-lamp',7,4},{'small-lamp',7,7},{'stone-wall',9,-5},
{'stone-wall',9,-4},{'stone-wall',9,-3},{'stone-wall',9,-2},{'stone-wall',9,-1},{'stone-wall',9,0},{'stone-wall',9,3},{'stone-wall',9,4},{'stone-wall',9,5},{'stone-wall',9,6},{'stone-wall',9,7},
{'stone-wall',9,8}
}
},
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}
{-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},
{-44,20},{-44,21},{-43,-19},{-43,-18},{-43,-17},{-43,-1},{-43,0},{-43,1},{-43,2},{-43,3},{-43,4},{-43,20},{-43,21},{-43,22},{-42,-19},{-42,-18},{-42,-1},{-42,0},{-42,1},{-42,2},{-42,3},{-42,4},{-42,21},{-42,22},{-41,-25},{-41,-24},{-41,-19},{-41,-18},{-41,-13},{-41,-12},
{-41,-11},{-41,-10},{-41,-5},{-41,-4},{-41,7},{-41,8},{-41,13},{-41,14},{-41,15},{-41,16},{-41,21},{-41,22},{-41,27},{-41,28},{-40,-26},{-40,-25},{-40,-24},{-40,-20},{-40,-19},{-40,-18},{-40,-13},{-40,-12},{-40,-11},{-40,-10},{-40,-5},{-40,-4},{-40,7},{-40,8},{-40,13},{-40,14},
{-40,15},{-40,16},{-40,21},{-40,22},{-40,23},{-40,27},{-40,28},{-40,29},{-39,-27},{-39,-26},{-39,-25},{-39,-24},{-39,-21},{-39,-20},{-39,-19},{-39,-13},{-39,-12},{-39,-5},{-39,-4},{-39,-3},{-39,-2},{-39,-1},{-39,0},{-39,1},{-39,2},{-39,3},{-39,4},{-39,5},{-39,6},{-39,7},
{-39,8},{-39,15},{-39,16},{-39,22},{-39,23},{-39,24},{-39,27},{-39,28},{-39,29},{-39,30},{-38,-27},{-38,-26},{-38,-25},{-38,-24},{-38,-21},{-38,-20},{-38,-13},{-38,-12},{-38,-5},{-38,-4},{-38,-3},{-38,-2},{-38,-1},{-38,0},{-38,1},{-38,2},{-38,3},{-38,4},{-38,5},{-38,6},
{-38,7},{-38,8},{-38,15},{-38,16},{-38,23},{-38,24},{-38,27},{-38,28},{-38,29},{-38,30},{-37,-17},{-37,-16},{-37,-13},{-37,-12},{-37,-11},{-37,-10},{-37,-4},{-37,-3},{-37,-2},{-37,-1},{-37,0},{-37,3},{-37,4},{-37,5},{-37,6},{-37,7},{-37,13},{-37,14},{-37,15},{-37,16},
{-37,19},{-37,20},{-36,-17},{-36,-16},{-36,-13},{-36,-12},{-36,-11},{-36,-10},{-36,-9},{-36,-3},{-36,-2},{-36,-1},{-36,0},{-36,3},{-36,4},{-36,5},{-36,6},{-36,12},{-36,13},{-36,14},{-36,15},{-36,16},{-36,19},{-36,20},{-35,-29},{-35,-28},{-35,-23},{-35,-22},{-35,-17},{-35,-16},
{-35,-12},{-35,-11},{-35,-10},{-35,-9},{-35,-8},{-35,11},{-35,12},{-35,13},{-35,14},{-35,15},{-35,19},{-35,20},{-35,25},{-35,26},{-35,31},{-35,32},{-34,-30},{-34,-29},{-34,-28},{-34,-23},{-34,-22},{-34,-17},{-34,-16},{-34,-15},{-34,-11},{-34,-10},{-34,-9},{-34,-8},{-34,11},{-34,12},
{-34,13},{-34,14},{-34,18},{-34,19},{-34,20},{-34,25},{-34,26},{-34,31},{-34,32},{-34,33},{-33,-31},{-33,-30},{-33,-29},{-33,-28},{-33,-23},{-33,-22},{-33,-16},{-33,-15},{-33,-14},{-33,-5},{-33,-4},{-33,-1},{-33,0},{-33,3},{-33,4},{-33,7},{-33,8},{-33,17},{-33,18},{-33,19},
{-33,25},{-33,26},{-33,31},{-33,32},{-33,33},{-33,34},{-32,-32},{-32,-31},{-32,-30},{-32,-29},{-32,-28},{-32,-27},{-32,-23},{-32,-22},{-32,-21},{-32,-15},{-32,-14},{-32,-6},{-32,-5},{-32,-4},{-32,-1},{-32,0},{-32,3},{-32,4},{-32,7},{-32,8},{-32,9},{-32,17},{-32,18},{-32,24},
{-32,25},{-32,26},{-32,30},{-32,31},{-32,32},{-32,33},{-32,34},{-32,35},{-31,-33},{-31,-32},{-31,-31},{-31,-30},{-31,-29},{-31,-28},{-31,-27},{-31,-26},{-31,-22},{-31,-21},{-31,-20},{-31,-19},{-31,-18},{-31,-11},{-31,-10},{-31,-9},{-31,-8},{-31,-7},{-31,-6},{-31,-5},{-31,-1},{-31,0},
{-31,1},{-31,2},{-31,3},{-31,4},{-31,8},{-31,9},{-31,10},{-31,11},{-31,12},{-31,13},{-31,14},{-31,21},{-31,22},{-31,23},{-31,24},{-31,25},{-31,29},{-31,30},{-31,31},{-31,32},{-31,33},{-31,34},{-31,35},{-31,36},{-30,-33},{-30,-32},{-30,-31},{-30,-30},{-30,-29},{-30,-28},
{-30,-27},{-30,-26},{-30,-21},{-30,-20},{-30,-19},{-30,-18},{-30,-11},{-30,-10},{-30,-9},{-30,-8},{-30,-7},{-30,-6},{-30,-1},{-30,0},{-30,1},{-30,2},{-30,3},{-30,4},{-30,9},{-30,10},{-30,11},{-30,12},{-30,13},{-30,14},{-30,21},{-30,22},{-30,23},{-30,24},{-30,29},{-30,30},
{-30,31},{-30,32},{-30,33},{-30,34},{-30,35},{-30,36},{-29,-37},{-29,-36},{-29,-30},{-29,-29},{-29,-28},{-29,-27},{-29,-26},{-29,-15},{-29,-14},{-29,-10},{-29,-9},{-29,-8},{-29,-7},{-29,10},{-29,11},{-29,12},{-29,13},{-29,17},{-29,18},{-29,29},{-29,30},{-29,31},{-29,32},{-29,33},
{-29,39},{-29,40},{-28,-38},{-28,-37},{-28,-36},{-28,-29},{-28,-28},{-28,-27},{-28,-26},{-28,-16},{-28,-15},{-28,-14},{-28,-9},{-28,-8},{-28,11},{-28,12},{-28,17},{-28,18},{-28,19},{-28,29},{-28,30},{-28,31},{-28,32},{-28,39},{-28,40},{-28,41},{-27,-39},{-27,-38},{-27,-37},{-27,-36},
{-27,-23},{-27,-22},{-27,-19},{-27,-18},{-27,-17},{-27,-16},{-27,-15},{-27,-5},{-27,-4},{-27,-1},{-27,0},{-27,1},{-27,2},{-27,3},{-27,4},{-27,7},{-27,8},{-27,18},{-27,19},{-27,20},{-27,21},{-27,22},{-27,25},{-27,26},{-27,39},{-27,40},{-27,41},{-27,42},{-26,-39},{-26,-38},
{-26,-37},{-26,-36},{-26,-24},{-26,-23},{-26,-22},{-26,-19},{-26,-18},{-26,-17},{-26,-16},{-26,-6},{-26,-5},{-26,-4},{-26,-1},{-26,0},{-26,1},{-26,2},{-26,3},{-26,4},{-26,7},{-26,8},{-26,9},{-26,19},{-26,20},{-26,21},{-26,22},{-26,25},{-26,26},{-26,27},{-26,39},{-26,40},
{-26,41},{-26,42},{-25,-33},{-25,-32},{-25,-31},{-25,-30},{-25,-25},{-25,-24},{-25,-23},{-25,-22},{-25,-19},{-25,-18},{-25,-17},{-25,-9},{-25,-8},{-25,-7},{-25,-6},{-25,-5},{-25,-4},{-25,-1},{-25,0},{-25,1},{-25,2},{-25,3},{-25,4},{-25,7},{-25,8},{-25,9},{-25,10},{-25,11},
{-25,12},{-25,20},{-25,21},{-25,22},{-25,25},{-25,26},{-25,27},{-25,28},{-25,33},{-25,34},{-25,35},{-25,36},{-24,-33},{-24,-32},{-24,-31},{-24,-30},{-24,-29},{-24,-25},{-24,-24},{-24,-23},{-24,-22},{-24,-19},{-24,-18},{-24,-9},{-24,-8},{-24,-7},{-24,-6},{-24,-5},{-24,-4},{-24,-1},
{-24,0},{-24,1},{-24,2},{-24,3},{-24,4},{-24,7},{-24,8},{-24,9},{-24,10},{-24,11},{-24,12},{-24,21},{-24,22},{-24,25},{-24,26},{-24,27},{-24,28},{-24,32},{-24,33},{-24,34},{-24,35},{-24,36},{-23,-37},{-23,-36},{-23,-30},{-23,-29},{-23,-28},{-23,-19},{-23,-18},{-23,-15},
{-23,-14},{-23,-9},{-23,-8},{-23,-7},{-23,-6},{-23,-5},{-23,0},{-23,1},{-23,2},{-23,3},{-23,8},{-23,9},{-23,10},{-23,11},{-23,12},{-23,17},{-23,18},{-23,21},{-23,22},{-23,31},{-23,32},{-23,33},{-23,39},{-23,40},{-22,-38},{-22,-37},{-22,-36},{-22,-29},{-22,-28},{-22,-19},
{-22,-18},{-22,-15},{-22,-14},{-22,-13},{-22,-9},{-22,-8},{-22,-7},{-22,-6},{-22,1},{-22,2},{-22,9},{-22,10},{-22,11},{-22,12},{-22,16},{-22,17},{-22,18},{-22,21},{-22,22},{-22,31},{-22,32},{-22,39},{-22,40},{-22,41},{-21,-41},{-21,-40},{-21,-39},{-21,-38},{-21,-37},{-21,-29},
{-21,-28},{-21,-25},{-21,-24},{-21,-23},{-21,-22},{-21,-21},{-21,-20},{-21,-19},{-21,-18},{-21,-15},{-21,-14},{-21,-13},{-21,-12},{-21,-3},{-21,-2},{-21,5},{-21,6},{-21,15},{-21,16},{-21,17},{-21,18},{-21,21},{-21,22},{-21,23},{-21,24},{-21,25},{-21,26},{-21,27},{-21,28},{-21,31},
{-21,32},{-21,40},{-21,41},{-21,42},{-21,43},{-21,44},{-20,-42},{-20,-41},{-20,-40},{-20,-39},{-20,-38},{-20,-29},{-20,-28},{-20,-25},{-20,-24},{-20,-23},{-20,-22},{-20,-21},{-20,-20},{-20,-19},{-20,-18},{-20,-15},{-20,-14},{-20,-13},{-20,-12},{-20,-3},{-20,-2},{-20,-1},{-20,4},{-20,5},
{-20,6},{-20,15},{-20,16},{-20,17},{-20,18},{-20,21},{-20,22},{-20,23},{-20,24},{-20,25},{-20,26},{-20,27},{-20,28},{-20,31},{-20,32},{-20,41},{-20,42},{-20,43},{-20,44},{-20,45},{-19,-43},{-19,-42},{-19,-41},{-19,-35},{-19,-34},{-19,-33},{-19,-32},{-19,-25},{-19,-24},{-19,-23},
{-19,-15},{-19,-14},{-19,-13},{-19,-9},{-19,-8},{-19,-7},{-19,-6},{-19,-2},{-19,-1},{-19,0},{-19,1},{-19,2},{-19,3},{-19,4},{-19,5},{-19,9},{-19,10},{-19,11},{-19,12},{-19,16},{-19,17},{-19,18},{-19,26},{-19,27},{-19,28},{-19,35},{-19,36},{-19,37},{-19,38},{-19,44},
{-19,45},{-19,46},{-18,-43},{-18,-42},{-18,-35},{-18,-34},{-18,-33},{-18,-32},{-18,-31},{-18,-26},{-18,-25},{-18,-24},{-18,-15},{-18,-14},{-18,-10},{-18,-9},{-18,-8},{-18,-7},{-18,-6},{-18,-1},{-18,0},{-18,1},{-18,2},{-18,3},{-18,4},{-18,9},{-18,10},{-18,11},{-18,12},{-18,13},
{-18,17},{-18,18},{-18,27},{-18,28},{-18,29},{-18,34},{-18,35},{-18,36},{-18,37},{-18,38},{-18,45},{-18,46},{-17,-43},{-17,-42},{-17,-32},{-17,-31},{-17,-30},{-17,-27},{-17,-26},{-17,-25},{-17,-21},{-17,-20},{-17,-19},{-17,-18},{-17,-17},{-17,-16},{-17,-15},{-17,-14},{-17,-11},{-17,-10},
{-17,-9},{-17,-8},{-17,-7},{-17,-6},{-17,0},{-17,1},{-17,2},{-17,3},{-17,9},{-17,10},{-17,11},{-17,12},{-17,13},{-17,14},{-17,17},{-17,18},{-17,19},{-17,20},{-17,21},{-17,22},{-17,23},{-17,24},{-17,28},{-17,29},{-17,30},{-17,33},{-17,34},{-17,35},{-17,45},{-17,46},
{-16,-43},{-16,-42},{-16,-31},{-16,-30},{-16,-27},{-16,-26},{-16,-21},{-16,-20},{-16,-19},{-16,-18},{-16,-17},{-16,-16},{-16,-15},{-16,-14},{-16,-11},{-16,-10},{-16,-9},{-16,-8},{-16,-7},{-16,-6},{-16,1},{-16,2},{-16,9},{-16,10},{-16,11},{-16,12},{-16,13},{-16,14},{-16,17},{-16,18},
{-16,19},{-16,20},{-16,21},{-16,22},{-16,23},{-16,24},{-16,29},{-16,30},{-16,33},{-16,34},{-16,45},{-16,46},{-15,-43},{-15,-42},{-15,-39},{-15,-38},{-15,-37},{-15,-36},{-15,-35},{-15,-34},{-15,-20},{-15,-19},{-15,-18},{-15,-17},{-15,-10},{-15,-9},{-15,-8},{-15,-7},{-15,-3},{-15,-2},
{-15,1},{-15,2},{-15,5},{-15,6},{-15,10},{-15,11},{-15,12},{-15,13},{-15,20},{-15,21},{-15,22},{-15,23},{-15,37},{-15,38},{-15,39},{-15,40},{-15,41},{-15,42},{-15,45},{-15,46},{-14,-43},{-14,-42},{-14,-39},{-14,-38},{-14,-37},{-14,-36},{-14,-35},{-14,-34},{-14,-33},{-14,-19},
{-14,-18},{-14,-9},{-14,-8},{-14,-4},{-14,-3},{-14,-2},{-14,1},{-14,2},{-14,5},{-14,6},{-14,7},{-14,11},{-14,12},{-14,21},{-14,22},{-14,36},{-14,37},{-14,38},{-14,39},{-14,40},{-14,41},{-14,42},{-14,45},{-14,46},{-13,-39},{-13,-38},{-13,-35},{-13,-34},{-13,-33},{-13,-32},
{-13,-29},{-13,-28},{-13,-15},{-13,-14},{-13,-5},{-13,-4},{-13,-3},{-13,-2},{-13,5},{-13,6},{-13,7},{-13,8},{-13,17},{-13,18},{-13,31},{-13,32},{-13,35},{-13,36},{-13,37},{-13,38},{-13,41},{-13,42},{-12,-39},{-12,-38},{-12,-35},{-12,-34},{-12,-33},{-12,-32},{-12,-29},{-12,-28},
{-12,-27},{-12,-16},{-12,-15},{-12,-14},{-12,-13},{-12,-5},{-12,-4},{-12,-3},{-12,-2},{-12,5},{-12,6},{-12,7},{-12,8},{-12,16},{-12,17},{-12,18},{-12,19},{-12,30},{-12,31},{-12,32},{-12,35},{-12,36},{-12,37},{-12,38},{-12,41},{-12,42},{-11,-43},{-11,-42},{-11,-34},{-11,-33},
{-11,-32},{-11,-29},{-11,-28},{-11,-27},{-11,-26},{-11,-23},{-11,-22},{-11,-21},{-11,-20},{-11,-17},{-11,-16},{-11,-15},{-11,-14},{-11,-13},{-11,-12},{-11,-9},{-11,-8},{-11,1},{-11,2},{-11,11},{-11,12},{-11,15},{-11,16},{-11,17},{-11,18},{-11,19},{-11,20},{-11,23},{-11,24},{-11,25},
{-11,26},{-11,29},{-11,30},{-11,31},{-11,32},{-11,35},{-11,36},{-11,37},{-11,45},{-11,46},{-10,-44},{-10,-43},{-10,-42},{-10,-33},{-10,-32},{-10,-29},{-10,-28},{-10,-27},{-10,-26},{-10,-23},{-10,-22},{-10,-21},{-10,-20},{-10,-17},{-10,-16},{-10,-15},{-10,-14},{-10,-13},{-10,-12},{-10,-9},
{-10,-8},{-10,-7},{-10,0},{-10,1},{-10,2},{-10,3},{-10,10},{-10,11},{-10,12},{-10,15},{-10,16},{-10,17},{-10,18},{-10,19},{-10,20},{-10,23},{-10,24},{-10,25},{-10,26},{-10,29},{-10,30},{-10,31},{-10,32},{-10,35},{-10,36},{-10,45},{-10,46},{-10,47},{-9,-45},{-9,-44},
{-9,-43},{-9,-29},{-9,-28},{-9,-27},{-9,-23},{-9,-22},{-9,-21},{-9,-20},{-9,-17},{-9,-16},{-9,-15},{-9,-14},{-9,-13},{-9,-8},{-9,-7},{-9,-6},{-9,-5},{-9,-1},{-9,0},{-9,1},{-9,2},{-9,3},{-9,4},{-9,8},{-9,9},{-9,10},{-9,11},{-9,16},{-9,17},{-9,18},
{-9,19},{-9,20},{-9,23},{-9,24},{-9,25},{-9,26},{-9,30},{-9,31},{-9,32},{-9,46},{-9,47},{-9,48},{-8,-45},{-8,-44},{-8,-30},{-8,-29},{-8,-28},{-8,-24},{-8,-23},{-8,-22},{-8,-21},{-8,-20},{-8,-17},{-8,-16},{-8,-15},{-8,-14},{-8,-7},{-8,-6},{-8,-5},{-8,-4},
{-8,-1},{-8,0},{-8,1},{-8,2},{-8,3},{-8,4},{-8,7},{-8,8},{-8,9},{-8,10},{-8,17},{-8,18},{-8,19},{-8,20},{-8,23},{-8,24},{-8,25},{-8,26},{-8,27},{-8,31},{-8,32},{-8,33},{-8,47},{-8,48},{-7,-45},{-7,-44},{-7,-39},{-7,-38},{-7,-37},{-7,-36},
{-7,-31},{-7,-30},{-7,-29},{-7,-25},{-7,-24},{-7,-23},{-7,-22},{-7,-21},{-7,-11},{-7,-10},{-7,-7},{-7,-6},{-7,-5},{-7,-4},{-7,7},{-7,8},{-7,9},{-7,10},{-7,13},{-7,14},{-7,24},{-7,25},{-7,26},{-7,27},{-7,28},{-7,32},{-7,33},{-7,34},{-7,39},{-7,40},
{-7,41},{-7,42},{-7,47},{-7,48},{-6,-46},{-6,-45},{-6,-44},{-6,-39},{-6,-38},{-6,-37},{-6,-36},{-6,-35},{-6,-31},{-6,-30},{-6,-25},{-6,-24},{-6,-23},{-6,-22},{-6,-12},{-6,-11},{-6,-10},{-6,-6},{-6,-5},{-6,8},{-6,9},{-6,13},{-6,14},{-6,15},{-6,25},{-6,26},
{-6,27},{-6,28},{-6,33},{-6,34},{-6,38},{-6,39},{-6,40},{-6,41},{-6,42},{-6,47},{-6,48},{-6,49},{-5,-47},{-5,-46},{-5,-45},{-5,-44},{-5,-37},{-5,-36},{-5,-35},{-5,-34},{-5,-19},{-5,-18},{-5,-13},{-5,-12},{-5,-11},{-5,-10},{-5,-1},{-5,0},{-5,1},{-5,2},
{-5,3},{-5,4},{-5,13},{-5,14},{-5,15},{-5,16},{-5,21},{-5,22},{-5,37},{-5,38},{-5,39},{-5,40},{-5,47},{-5,48},{-5,49},{-5,50},{-4,-47},{-4,-46},{-4,-45},{-4,-44},{-4,-43},{-4,-37},{-4,-36},{-4,-35},{-4,-34},{-4,-19},{-4,-18},{-4,-17},{-4,-13},{-4,-12},
{-4,-11},{-4,-10},{-4,-2},{-4,-1},{-4,0},{-4,1},{-4,2},{-4,3},{-4,4},{-4,5},{-4,13},{-4,14},{-4,15},{-4,16},{-4,20},{-4,21},{-4,22},{-4,37},{-4,38},{-4,39},{-4,40},{-4,46},{-4,47},{-4,48},{-4,49},{-4,50},{-3,-44},{-3,-43},{-3,-42},{-3,-41},
{-3,-40},{-3,-37},{-3,-36},{-3,-35},{-3,-34},{-3,-31},{-3,-30},{-3,-29},{-3,-28},{-3,-25},{-3,-24},{-3,-23},{-3,-22},{-3,-18},{-3,-17},{-3,-16},{-3,-7},{-3,-6},{-3,-3},{-3,-2},{-3,-1},{-3,0},{-3,3},{-3,4},{-3,5},{-3,6},{-3,9},{-3,10},{-3,19},{-3,20},
{-3,21},{-3,25},{-3,26},{-3,27},{-3,28},{-3,31},{-3,32},{-3,33},{-3,34},{-3,37},{-3,38},{-3,39},{-3,40},{-3,43},{-3,44},{-3,45},{-3,46},{-3,47},{-2,-43},{-2,-42},{-2,-41},{-2,-40},{-2,-37},{-2,-36},{-2,-35},{-2,-34},{-2,-31},{-2,-30},{-2,-29},{-2,-28},
{-2,-25},{-2,-24},{-2,-23},{-2,-22},{-2,-21},{-2,-17},{-2,-16},{-2,-15},{-2,-8},{-2,-7},{-2,-6},{-2,-3},{-2,-2},{-2,-1},{-2,0},{-2,3},{-2,4},{-2,5},{-2,6},{-2,9},{-2,10},{-2,11},{-2,18},{-2,19},{-2,20},{-2,24},{-2,25},{-2,26},{-2,27},{-2,28},
{-2,31},{-2,32},{-2,33},{-2,34},{-2,37},{-2,38},{-2,39},{-2,40},{-2,43},{-2,44},{-2,45},{-2,46},{-1,-47},{-1,-46},{-1,-43},{-1,-42},{-1,-41},{-1,-40},{-1,-37},{-1,-36},{-1,-29},{-1,-28},{-1,-25},{-1,-24},{-1,-23},{-1,-22},{-1,-21},{-1,-20},{-1,-17},{-1,-16},
{-1,-15},{-1,-14},{-1,-13},{-1,-12},{-1,-9},{-1,-8},{-1,-7},{-1,-6},{-1,-3},{-1,-2},{-1,5},{-1,6},{-1,9},{-1,10},{-1,11},{-1,12},{-1,15},{-1,16},{-1,17},{-1,18},{-1,19},{-1,20},{-1,23},{-1,24},{-1,25},{-1,26},{-1,27},{-1,28},{-1,31},{-1,32},
{-1,39},{-1,40},{-1,43},{-1,44},{-1,45},{-1,46},{-1,49},{-1,50},{0,-47},{0,-46},{0,-43},{0,-42},{0,-41},{0,-40},{0,-37},{0,-36},{0,-29},{0,-28},{0,-25},{0,-24},{0,-23},{0,-22},{0,-21},{0,-20},{0,-17},{0,-16},{0,-15},{0,-14},{0,-13},{0,-12},
{0,-9},{0,-8},{0,-7},{0,-6},{0,-3},{0,-2},{0,5},{0,6},{0,9},{0,10},{0,11},{0,12},{0,15},{0,16},{0,17},{0,18},{0,19},{0,20},{0,23},{0,24},{0,25},{0,26},{0,27},{0,28},{0,31},{0,32},{0,39},{0,40},{0,43},{0,44},
{0,45},{0,46},{0,49},{0,50},{1,-43},{1,-42},{1,-41},{1,-40},{1,-37},{1,-36},{1,-35},{1,-34},{1,-31},{1,-30},{1,-29},{1,-28},{1,-25},{1,-24},{1,-23},{1,-22},{1,-21},{1,-17},{1,-16},{1,-15},{1,-8},{1,-7},{1,-6},{1,-3},{1,-2},{1,-1},
{1,0},{1,3},{1,4},{1,5},{1,6},{1,9},{1,10},{1,11},{1,18},{1,19},{1,20},{1,24},{1,25},{1,26},{1,27},{1,28},{1,31},{1,32},{1,33},{1,34},{1,37},{1,38},{1,39},{1,40},{1,43},{1,44},{1,45},{1,46},{2,-44},{2,-43},
{2,-42},{2,-41},{2,-40},{2,-37},{2,-36},{2,-35},{2,-34},{2,-31},{2,-30},{2,-29},{2,-28},{2,-25},{2,-24},{2,-23},{2,-22},{2,-18},{2,-17},{2,-16},{2,-7},{2,-6},{2,-3},{2,-2},{2,-1},{2,0},{2,3},{2,4},{2,5},{2,6},{2,9},{2,10},
{2,19},{2,20},{2,21},{2,25},{2,26},{2,27},{2,28},{2,31},{2,32},{2,33},{2,34},{2,37},{2,38},{2,39},{2,40},{2,43},{2,44},{2,45},{2,46},{2,47},{3,-47},{3,-46},{3,-45},{3,-44},{3,-43},{3,-37},{3,-36},{3,-35},{3,-34},{3,-19},
{3,-18},{3,-17},{3,-13},{3,-12},{3,-11},{3,-10},{3,-2},{3,-1},{3,0},{3,1},{3,2},{3,3},{3,4},{3,5},{3,13},{3,14},{3,15},{3,16},{3,20},{3,21},{3,22},{3,37},{3,38},{3,39},{3,40},{3,46},{3,47},{3,48},{3,49},{3,50},
{4,-47},{4,-46},{4,-45},{4,-44},{4,-37},{4,-36},{4,-35},{4,-34},{4,-19},{4,-18},{4,-13},{4,-12},{4,-11},{4,-10},{4,-1},{4,0},{4,1},{4,2},{4,3},{4,4},{4,13},{4,14},{4,15},{4,16},{4,21},{4,22},{4,37},{4,38},{4,39},{4,40},
{4,47},{4,48},{4,49},{4,50},{5,-46},{5,-45},{5,-44},{5,-39},{5,-38},{5,-37},{5,-36},{5,-35},{5,-31},{5,-30},{5,-25},{5,-24},{5,-23},{5,-22},{5,-12},{5,-11},{5,-10},{5,-6},{5,-5},{5,8},{5,9},{5,13},{5,14},{5,15},{5,25},{5,26},
{5,27},{5,28},{5,33},{5,34},{5,38},{5,39},{5,40},{5,41},{5,42},{5,47},{5,48},{5,49},{6,-45},{6,-44},{6,-39},{6,-38},{6,-37},{6,-36},{6,-31},{6,-30},{6,-29},{6,-25},{6,-24},{6,-23},{6,-22},{6,-21},{6,-11},{6,-10},{6,-7},{6,-6},
{6,-5},{6,-4},{6,7},{6,8},{6,9},{6,10},{6,13},{6,14},{6,24},{6,25},{6,26},{6,27},{6,28},{6,32},{6,33},{6,34},{6,39},{6,40},{6,41},{6,42},{6,47},{6,48},{7,-45},{7,-44},{7,-30},{7,-29},{7,-28},{7,-24},{7,-23},{7,-22},
{7,-21},{7,-20},{7,-17},{7,-16},{7,-15},{7,-14},{7,-7},{7,-6},{7,-5},{7,-4},{7,-1},{7,0},{7,1},{7,2},{7,3},{7,4},{7,7},{7,8},{7,9},{7,10},{7,17},{7,18},{7,19},{7,20},{7,23},{7,24},{7,25},{7,26},{7,27},{7,31},
{7,32},{7,33},{7,47},{7,48},{8,-45},{8,-44},{8,-43},{8,-29},{8,-28},{8,-27},{8,-23},{8,-22},{8,-21},{8,-20},{8,-17},{8,-16},{8,-15},{8,-14},{8,-13},{8,-8},{8,-7},{8,-6},{8,-5},{8,-1},{8,0},{8,1},{8,2},{8,3},{8,4},{8,8},
{8,9},{8,10},{8,11},{8,16},{8,17},{8,18},{8,19},{8,20},{8,23},{8,24},{8,25},{8,26},{8,30},{8,31},{8,32},{8,46},{8,47},{8,48},{9,-44},{9,-43},{9,-42},{9,-33},{9,-32},{9,-29},{9,-28},{9,-27},{9,-26},{9,-23},{9,-22},{9,-21},
{9,-20},{9,-17},{9,-16},{9,-15},{9,-14},{9,-13},{9,-12},{9,-9},{9,-8},{9,-7},{9,0},{9,1},{9,2},{9,3},{9,10},{9,11},{9,12},{9,15},{9,16},{9,17},{9,18},{9,19},{9,20},{9,23},{9,24},{9,25},{9,26},{9,29},{9,30},{9,31},
{9,32},{9,35},{9,36},{9,45},{9,46},{9,47},{10,-43},{10,-42},{10,-34},{10,-33},{10,-32},{10,-29},{10,-28},{10,-27},{10,-26},{10,-23},{10,-22},{10,-21},{10,-20},{10,-17},{10,-16},{10,-15},{10,-14},{10,-13},{10,-12},{10,-9},{10,-8},{10,1},{10,2},{10,11},
{10,12},{10,15},{10,16},{10,17},{10,18},{10,19},{10,20},{10,23},{10,24},{10,25},{10,26},{10,29},{10,30},{10,31},{10,32},{10,35},{10,36},{10,37},{10,45},{10,46},{11,-39},{11,-38},{11,-35},{11,-34},{11,-33},{11,-32},{11,-29},{11,-28},{11,-27},{11,-16},
{11,-15},{11,-14},{11,-13},{11,-5},{11,-4},{11,-3},{11,-2},{11,5},{11,6},{11,7},{11,8},{11,16},{11,17},{11,18},{11,19},{11,30},{11,31},{11,32},{11,35},{11,36},{11,37},{11,38},{11,41},{11,42},{12,-39},{12,-38},{12,-35},{12,-34},{12,-33},{12,-32},
{12,-29},{12,-28},{12,-15},{12,-14},{12,-5},{12,-4},{12,-3},{12,-2},{12,5},{12,6},{12,7},{12,8},{12,17},{12,18},{12,31},{12,32},{12,35},{12,36},{12,37},{12,38},{12,41},{12,42},{13,-43},{13,-42},{13,-39},{13,-38},{13,-37},{13,-36},{13,-35},{13,-34},
{13,-33},{13,-19},{13,-18},{13,-9},{13,-8},{13,-4},{13,-3},{13,-2},{13,1},{13,2},{13,5},{13,6},{13,7},{13,11},{13,12},{13,21},{13,22},{13,36},{13,37},{13,38},{13,39},{13,40},{13,41},{13,42},{13,45},{13,46},{14,-43},{14,-42},{14,-39},{14,-38},
{14,-37},{14,-36},{14,-35},{14,-34},{14,-20},{14,-19},{14,-18},{14,-17},{14,-10},{14,-9},{14,-8},{14,-7},{14,-3},{14,-2},{14,1},{14,2},{14,5},{14,6},{14,10},{14,11},{14,12},{14,13},{14,20},{14,21},{14,22},{14,23},{14,37},{14,38},{14,39},{14,40},
{14,41},{14,42},{14,45},{14,46},{15,-43},{15,-42},{15,-31},{15,-30},{15,-27},{15,-26},{15,-21},{15,-20},{15,-19},{15,-18},{15,-17},{15,-16},{15,-15},{15,-14},{15,-11},{15,-10},{15,-9},{15,-8},{15,-7},{15,-6},{15,1},{15,2},{15,9},{15,10},{15,11},{15,12},
{15,13},{15,14},{15,17},{15,18},{15,19},{15,20},{15,21},{15,22},{15,23},{15,24},{15,29},{15,30},{15,33},{15,34},{15,45},{15,46},{16,-43},{16,-42},{16,-32},{16,-31},{16,-30},{16,-27},{16,-26},{16,-25},{16,-21},{16,-20},{16,-19},{16,-18},{16,-17},{16,-16},
{16,-15},{16,-14},{16,-11},{16,-10},{16,-9},{16,-8},{16,-7},{16,-6},{16,0},{16,1},{16,2},{16,3},{16,9},{16,10},{16,11},{16,12},{16,13},{16,14},{16,17},{16,18},{16,19},{16,20},{16,21},{16,22},{16,23},{16,24},{16,28},{16,29},{16,30},{16,33},
{16,34},{16,35},{16,45},{16,46},{17,-43},{17,-42},{17,-35},{17,-34},{17,-33},{17,-32},{17,-31},{17,-26},{17,-25},{17,-24},{17,-15},{17,-14},{17,-10},{17,-9},{17,-8},{17,-7},{17,-6},{17,-1},{17,0},{17,1},{17,2},{17,3},{17,4},{17,9},{17,10},{17,11},
{17,12},{17,13},{17,17},{17,18},{17,27},{17,28},{17,29},{17,34},{17,35},{17,36},{17,37},{17,38},{17,45},{17,46},{18,-43},{18,-42},{18,-41},{18,-35},{18,-34},{18,-33},{18,-32},{18,-25},{18,-24},{18,-23},{18,-15},{18,-14},{18,-13},{18,-9},{18,-8},{18,-7},
{18,-6},{18,-2},{18,-1},{18,0},{18,1},{18,2},{18,3},{18,4},{18,5},{18,9},{18,10},{18,11},{18,12},{18,16},{18,17},{18,18},{18,26},{18,27},{18,28},{18,35},{18,36},{18,37},{18,38},{18,44},{18,45},{18,46},{19,-42},{19,-41},{19,-40},{19,-39},
{19,-38},{19,-29},{19,-28},{19,-25},{19,-24},{19,-23},{19,-22},{19,-21},{19,-20},{19,-19},{19,-18},{19,-15},{19,-14},{19,-13},{19,-12},{19,-3},{19,-2},{19,-1},{19,4},{19,5},{19,6},{19,15},{19,16},{19,17},{19,18},{19,21},{19,22},{19,23},{19,24},{19,25},
{19,26},{19,27},{19,28},{19,31},{19,32},{19,41},{19,42},{19,43},{19,44},{19,45},{20,-41},{20,-40},{20,-39},{20,-38},{20,-37},{20,-29},{20,-28},{20,-25},{20,-24},{20,-23},{20,-22},{20,-21},{20,-20},{20,-19},{20,-18},{20,-15},{20,-14},{20,-13},{20,-12},{20,-3},
{20,-2},{20,5},{20,6},{20,15},{20,16},{20,17},{20,18},{20,21},{20,22},{20,23},{20,24},{20,25},{20,26},{20,27},{20,28},{20,31},{20,32},{20,40},{20,41},{20,42},{20,43},{20,44},{21,-38},{21,-37},{21,-36},{21,-29},{21,-28},{21,-19},{21,-18},{21,-15},
{21,-14},{21,-13},{21,-9},{21,-8},{21,-7},{21,-6},{21,1},{21,2},{21,9},{21,10},{21,11},{21,12},{21,16},{21,17},{21,18},{21,21},{21,22},{21,31},{21,32},{21,39},{21,40},{21,41},{22,-37},{22,-36},{22,-30},{22,-29},{22,-28},{22,-19},{22,-18},{22,-15},
{22,-14},{22,-9},{22,-8},{22,-7},{22,-6},{22,-5},{22,0},{22,1},{22,2},{22,3},{22,8},{22,9},{22,10},{22,11},{22,12},{22,17},{22,18},{22,21},{22,22},{22,31},{22,32},{22,33},{22,39},{22,40},{23,-33},{23,-32},{23,-31},{23,-30},{23,-29},{23,-25},
{23,-24},{23,-23},{23,-22},{23,-19},{23,-18},{23,-9},{23,-8},{23,-7},{23,-6},{23,-5},{23,-4},{23,-1},{23,0},{23,1},{23,2},{23,3},{23,4},{23,7},{23,8},{23,9},{23,10},{23,11},{23,12},{23,21},{23,22},{23,25},{23,26},{23,27},{23,28},{23,32},
{23,33},{23,34},{23,35},{23,36},{24,-33},{24,-32},{24,-31},{24,-30},{24,-25},{24,-24},{24,-23},{24,-22},{24,-19},{24,-18},{24,-17},{24,-9},{24,-8},{24,-7},{24,-6},{24,-5},{24,-4},{24,-1},{24,0},{24,1},{24,2},{24,3},{24,4},{24,7},{24,8},{24,9},
{24,10},{24,11},{24,12},{24,20},{24,21},{24,22},{24,25},{24,26},{24,27},{24,28},{24,33},{24,34},{24,35},{24,36},{25,-39},{25,-38},{25,-37},{25,-36},{25,-24},{25,-23},{25,-22},{25,-19},{25,-18},{25,-17},{25,-16},{25,-6},{25,-5},{25,-4},{25,-1},{25,0},
{25,1},{25,2},{25,3},{25,4},{25,7},{25,8},{25,9},{25,19},{25,20},{25,21},{25,22},{25,25},{25,26},{25,27},{25,39},{25,40},{25,41},{25,42},{26,-39},{26,-38},{26,-37},{26,-36},{26,-23},{26,-22},{26,-19},{26,-18},{26,-17},{26,-16},{26,-15},{26,-5},
{26,-4},{26,-1},{26,0},{26,1},{26,2},{26,3},{26,4},{26,7},{26,8},{26,18},{26,19},{26,20},{26,21},{26,22},{26,25},{26,26},{26,39},{26,40},{26,41},{26,42},{27,-38},{27,-37},{27,-36},{27,-29},{27,-28},{27,-27},{27,-26},{27,-16},{27,-15},{27,-14},
{27,-9},{27,-8},{27,11},{27,12},{27,17},{27,18},{27,19},{27,29},{27,30},{27,31},{27,32},{27,39},{27,40},{27,41},{28,-37},{28,-36},{28,-30},{28,-29},{28,-28},{28,-27},{28,-26},{28,-15},{28,-14},{28,-10},{28,-9},{28,-8},{28,-7},{28,10},{28,11},{28,12},
{28,13},{28,17},{28,18},{28,29},{28,30},{28,31},{28,32},{28,33},{28,39},{28,40},{29,-33},{29,-32},{29,-31},{29,-30},{29,-29},{29,-28},{29,-27},{29,-26},{29,-21},{29,-20},{29,-19},{29,-18},{29,-11},{29,-10},{29,-9},{29,-8},{29,-7},{29,-6},{29,-1},{29,0},
{29,1},{29,2},{29,3},{29,4},{29,9},{29,10},{29,11},{29,12},{29,13},{29,14},{29,21},{29,22},{29,23},{29,24},{29,29},{29,30},{29,31},{29,32},{29,33},{29,34},{29,35},{29,36},{30,-33},{30,-32},{30,-31},{30,-30},{30,-29},{30,-28},{30,-27},{30,-26},
{30,-22},{30,-21},{30,-20},{30,-19},{30,-18},{30,-11},{30,-10},{30,-9},{30,-8},{30,-7},{30,-6},{30,-5},{30,-1},{30,0},{30,1},{30,2},{30,3},{30,4},{30,8},{30,9},{30,10},{30,11},{30,12},{30,13},{30,14},{30,21},{30,22},{30,23},{30,24},{30,25},
{30,29},{30,30},{30,31},{30,32},{30,33},{30,34},{30,35},{30,36},{31,-32},{31,-31},{31,-30},{31,-29},{31,-28},{31,-27},{31,-23},{31,-22},{31,-21},{31,-15},{31,-14},{31,-6},{31,-5},{31,-4},{31,-1},{31,0},{31,3},{31,4},{31,7},{31,8},{31,9},{31,17},
{31,18},{31,24},{31,25},{31,26},{31,30},{31,31},{31,32},{31,33},{31,34},{31,35},{32,-31},{32,-30},{32,-29},{32,-28},{32,-23},{32,-22},{32,-16},{32,-15},{32,-14},{32,-5},{32,-4},{32,-1},{32,0},{32,3},{32,4},{32,7},{32,8},{32,17},{32,18},{32,19},
{32,25},{32,26},{32,31},{32,32},{32,33},{32,34},{33,-30},{33,-29},{33,-28},{33,-23},{33,-22},{33,-17},{33,-16},{33,-15},{33,-11},{33,-10},{33,-9},{33,-8},{33,11},{33,12},{33,13},{33,14},{33,18},{33,19},{33,20},{33,25},{33,26},{33,31},{33,32},{33,33},
{34,-29},{34,-28},{34,-23},{34,-22},{34,-17},{34,-16},{34,-12},{34,-11},{34,-10},{34,-9},{34,-8},{34,11},{34,12},{34,13},{34,14},{34,15},{34,19},{34,20},{34,25},{34,26},{34,31},{34,32},{35,-17},{35,-16},{35,-13},{35,-12},{35,-11},{35,-10},{35,-9},{35,-3},
{35,-2},{35,-1},{35,0},{35,3},{35,4},{35,5},{35,6},{35,12},{35,13},{35,14},{35,15},{35,16},{35,19},{35,20},{36,-17},{36,-16},{36,-13},{36,-12},{36,-11},{36,-10},{36,-4},{36,-3},{36,-2},{36,-1},{36,0},{36,3},{36,4},{36,5},{36,6},{36,7},
{36,13},{36,14},{36,15},{36,16},{36,19},{36,20},{37,-27},{37,-26},{37,-25},{37,-24},{37,-21},{37,-20},{37,-13},{37,-12},{37,-5},{37,-4},{37,-3},{37,-2},{37,-1},{37,0},{37,1},{37,2},{37,3},{37,4},{37,5},{37,6},{37,7},{37,8},{37,15},{37,16},
{37,23},{37,24},{37,27},{37,28},{37,29},{37,30},{38,-27},{38,-26},{38,-25},{38,-24},{38,-21},{38,-20},{38,-19},{38,-13},{38,-12},{38,-5},{38,-4},{38,-3},{38,-2},{38,-1},{38,0},{38,1},{38,2},{38,3},{38,4},{38,5},{38,6},{38,7},{38,8},{38,15},
{38,16},{38,22},{38,23},{38,24},{38,27},{38,28},{38,29},{38,30},{39,-26},{39,-25},{39,-24},{39,-20},{39,-19},{39,-18},{39,-13},{39,-12},{39,-11},{39,-10},{39,-5},{39,-4},{39,7},{39,8},{39,13},{39,14},{39,15},{39,16},{39,21},{39,22},{39,23},{39,27},
{39,28},{39,29},{40,-25},{40,-24},{40,-19},{40,-18},{40,-13},{40,-12},{40,-11},{40,-10},{40,-5},{40,-4},{40,7},{40,8},{40,13},{40,14},{40,15},{40,16},{40,21},{40,22},{40,27},{40,28},{41,-19},{41,-18},{41,-1},{41,0},{41,1},{41,2},{41,3},{41,4},
{41,21},{41,22},{42,-19},{42,-18},{42,-17},{42,-1},{42,0},{42,1},{42,2},{42,3},{42,4},{42,20},{42,21},{42,22},{43,-18},{43,-17},{43,-16},{43,-15},{43,-14},{43,-13},{43,-12},{43,-9},{43,-8},{43,-1},{43,0},{43,1},{43,2},{43,3},{43,4},{43,11},
{43,12},{43,15},{43,16},{43,17},{43,18},{43,19},{43,20},{43,21},{44,-17},{44,-16},{44,-15},{44,-14},{44,-13},{44,-12},{44,-9},{44,-8},{44,-7},{44,-2},{44,-1},{44,0},{44,1},{44,2},{44,3},{44,4},{44,5},{44,10},{44,11},{44,12},{44,15},{44,16},
{44,17},{44,18},{44,19},{44,20},{45,-8},{45,-7},{45,-6},{45,-5},{45,-4},{45,-3},{45,-2},{45,-1},{45,4},{45,5},{45,6},{45,7},{45,8},{45,9},{45,10},{45,11},{46,-7},{46,-6},{46,-5},{46,-4},{46,-3},{46,-2},{46,5},{46,6},{46,7},{46,8},
{46,9},{46,10},{47,-4},{47,-3},{47,-2},{47,1},{47,2},{47,5},{47,6},{47,7},{48,-3},{48,-2},{48,1},{48,2},{48,5},{48,6}
}
},
resource_tiles = {
enabled = false,
resources = {
{
enabled = false,
name = 'iron-ore',
amount = 4000,
size = {26, 27},
-- offset = {-64,-32}
offset = {-64,-64}
},
{
enabled = false,
name = 'copper-ore',
amount = 4000,
size = {26, 27},
-- offset = {-64, 0}
offset = {64, -64}
},
{
enabled = false,
name = 'stone',
amount = 4000,
size = {22, 20},
-- offset = {-64, 32}
offset = {-64, 64}
},
{
enabled = false,
name = 'coal',
amount = 4000,
size = {22, 20},
-- offset = {-64, -64}
offset = {64, 64}
},
{
enabled = false,
name = 'uranium-ore',
amount = 4000,
size = {22, 20},
-- offset = {-64, -96}
offset = {0, 64}
}
}
},
resource_patches = {
enabled = false,
resources = {
{
enabled = false,
name = 'crude-oil',
num_patches = 4,
amount = 4000000,
-- offset = {-80, -12},
offset = {-12, 64},
-- offset_next = {0, 6}
offset_next = {6, 0}
}
}
},
resource_refill_nearby = {
enabled = false,
range = 128,
resources_name = {
'iron-ore',
'copper-ore',
'stone',
'coal',
'uranium-ore'
},
amount = {2500, 4000}
}
}

View File

@@ -0,0 +1,11 @@
return {
--[[
__icon__
__item_name__
__backer_name__
__direction__
__x__
__y__
]]
station_name = '[L] __icon__'
}

View File

@@ -0,0 +1,46 @@
--- A list of all tracked statistics and the events which trigger them
-- @config Statistics
local e = defines.events -- order as per lua api as it was easier just to go down the list
return {
MapsPlayed = true, --- @setting MapsPlayed If the number of maps which a player has played should be tracked
Playtime = true, --- @setting Playtime If playtime is tracked for a player, play time measured in minutes
AfkTime = true, --- @setting AfkTime If afk time is tracked for a player, play time measured in minutes, afk is once a player does nothing for 5 minutes
DistanceTravelled = true, --- @setting DistanceTravelled If distance Travelled is checked, only counts if not afk
MachinesRemoved = true, --- @setting MachinesRemoved If removed machines are tracked, includes marked for decon and player mined entity
TreesDestroyed = true, --- @setting OreMined If ore mined is tracked for a player, includes marked for decon and player mined entity but only trees
OreMined = true, --- @setting OreMined If ore mined is tracked for a player, includes player mined entity but only ore
DamageDealt = true, --- @setting DamageDealt If damage dealt is tracked for a player, includes any damage to entities not on the same force or neutral
Kills = true, --- @setting Kills If kills are tracked for a player, includes all kills not on same force or neutral
RocketsLaunched = true, --- @setting RocketsLaunched If the number of rockets launched should be tracked, done for all players on the force
ResearchCompleted = true, --- @setting ResearchCompleted If the number of researches completed should be tracked, done for all players on the force
counters = { --- @setting counters Simple statistics that just go up by one each time an event happens
MachinesBuilt = e.on_built_entity,
MapTagsMade = e.on_chart_tag_added,
ChatMessages = e.on_console_chat,
CommandsUsed = e.on_console_command,
ItemsPickedUp = e.on_picked_up_item,
TilesBuilt = e.on_player_built_tile,
ItemsCrafted = e.on_player_crafted_item,
DeconstructionPlannerUsed = e.on_player_deconstructed_area,
Deaths = e.on_player_died,
JoinCount = e.on_player_joined_game,
TilesRemoved = e.on_player_mined_tile,
CapsulesUsed = e.on_player_used_capsule,
EntityRepaired= e.on_player_repaired_entity
},
display_order = { --- @setting display_order The order that the statistics should be shown in when in a gui or command
'Playtime', 'AfkTime',
'MapsPlayed', 'JoinCount',
'ChatMessages', 'CommandsUsed',
'RocketsLaunched', 'ResearchCompleted',
'MachinesBuilt', 'MachinesRemoved',
'TilesBuilt', 'TilesRemoved',
'TreesDestroyed', 'OreMined',
'ItemsCrafted', 'ItemsPickedUp',
'Kills', 'Deaths',
'DamageDealt', 'DistanceTravelled',
'CapsulesUsed', 'EntityRepaired',
'DeconstructionPlannerUsed', 'MapTagsMade'
}
}

View File

@@ -0,0 +1,174 @@
--- Settings for vlayer including the allowed items, the update frequency, and some cheats
-- @config Vlayer
return {
update_tick_storage = 60, --- @setting update_tick_storage The number of ticks between each update of the storage interfaces
update_tick_energy = 10, --- @setting update_tick_energy The number of ticks between each update of the energy and circuit interfaces
update_tick_gui = 60, --- @setting update_tick_gui The number of ticks between each update of the gui
unlimited_capacity = false, --- @setting unlimited_capacity When true the vlayer has an unlimited energy capacity, accumulators are not required
unlimited_surface_area = false, --- @setting unlimited_surface_area When true the vlayer has an unlimited surface area, landfill is not required
modded_auto_downgrade = false, --- @setting modded_auto_downgrade When true modded items will be converted into their base game equivalent, original items can not be recovered
mimic_surface = 'nauvis', --- @setting mimic_surface Surface name/index the vlayer will copy its settings from, use nil to use the settings below
surface = { --- @setting surface When mimic_surface is nil these settings will be used instead, see LuaSurface for details
always_day = false,
solar_power_multiplier = 1,
min_brightness = 0.15,
ticks_per_day = 25000,
daytime = 0,
dusk = 0.25,
evening = 0.45,
morning = 0.55,
dawn = 0.75
},
interface_limit = { --- @setting interface_limit Sets the limit for the number of vlayer interfaces that can be created
energy = 1, -- >1 allows for disconnected power networks to receive power
circuit = 10, -- No caveats
storage_input = 10, -- No caveats
storage_output = 1 -- >0 allows for item teleportation (allowed_items only)
},
allowed_items = { --- @setting allowed_items List of all items allowed in vlayer storage and their properties
--[[
Allowed properties:
starting_value = 0: The amount of the item placed into the vlayer on game start, ignores area requirements
required_area = 0: When greater than 0 the items properties are not applied unless their is sufficient surplus surface area
production = 0: The energy production of the item in MW, used for solar panels
discharge = 0: The energy discharge of the item in MW, used for accumulators
capacity = 0: The energy capacity of the item in MJ, used for accumulators
surface_area = 0: The surface area provided by the item, used for landfill
]]
['solar-panel'] = {
starting_value = 0,
required_area = 9,
production = 0.06 -- MW
},
['accumulator'] = {
starting_value = 0,
required_area = 4,
discharge = 0.3, -- MW
capacity = 5 -- MJ
},
['landfill'] = {
starting_value = 0,
required_area = 0,
surface_area = 6 -- Tiles
},
['wood'] = {
starting_value = 0,
required_area = 0,
surface_area = 0,
fuel_value = 2, -- MJ
power = true -- turn all wood to power to reduce trash
},
['coal'] = {
starting_value = 0,
required_area = 0,
surface_area = 0,
fuel_value = 4, -- MJ
power = false -- turn all coal to power to reduce trash
}
--[[
['iron-ore'] = {
starting_value = 0,
required_area = 0,
surface_area = 0
},
['copper-ore'] = {
starting_value = 0,
required_area = 0,
surface_area = 0
},
['coal'] = {
starting_value = 0,
required_area = 0,
surface_area = 0
},
['stone'] = {
starting_value = 0,
required_area = 0,
surface_area = 0
},
['uranium-ore'] = {
starting_value = 0,
required_area = 0,
surface_area = 0
},
]]
},
modded_items = { --- @setting modded_items List of all modded items allowed in vlayer storage and their base game equivalent
['solar-panel-2'] = {
starting_value = 0,
base_game_equivalent = 'solar-panel',
multiplier = 4
},
['solar-panel-3'] = {
starting_value = 0,
base_game_equivalent = 'solar-panel',
multiplier = 16
},
['solar-panel-4'] = {
starting_value = 0,
base_game_equivalent = 'solar-panel',
multiplier = 64
},
['solar-panel-5'] = {
starting_value = 0,
base_game_equivalent = 'solar-panel',
multiplier = 256
},
['solar-panel-6'] = {
starting_value = 0,
base_game_equivalent = 'solar-panel',
multiplier = 1024
},
['solar-panel-7'] = {
starting_value = 0,
base_game_equivalent = 'solar-panel',
multiplier = 4096
},
['solar-panel-8'] = {
starting_value = 0,
base_game_equivalent = 'solar-panel',
multiplier = 16384
},
['accumulator-2'] = {
starting_value = 0,
base_game_equivalent = 'accumulator',
multiplier = 4
},
['accumulator-3'] = {
starting_value = 0,
base_game_equivalent = 'accumulator',
multiplier = 16
},
['accumulator-4'] = {
starting_value = 0,
base_game_equivalent = 'accumulator',
multiplier = 64
},
['accumulator-5'] = {
starting_value = 0,
base_game_equivalent = 'accumulator',
multiplier = 256
},
['accumulator-6'] = {
starting_value = 0,
base_game_equivalent = 'accumulator',
multiplier = 1024
},
['accumulator-7'] = {
starting_value = 0,
base_game_equivalent = 'accumulator',
multiplier = 4096
},
['accumulator-8'] = {
starting_value = 0,
base_game_equivalent = 'accumulator',
multiplier = 16384
},
}
}

View File

@@ -0,0 +1,21 @@
--- Config file for the warning system, this is very similar to reports but is for the use of moderators rather than normal users.
-- @config Warnings
return {
actions = { --- @setting actions what actions are taking at number of warnings
-- if a localized string is used then __1__ will by_player_name and __2__ will be the current warning count (auto inserted)
{'warnings.received',''},
{'warnings.received',''},
{'warnings.received',{'warnings.pre-kick'}},
function(player,by_player_name,number_of_warnings)
game.kick_player(player,{'warnings.received',by_player_name,number_of_warnings,{'warnings.kick'}})
end,
{'warnings.received',{'warnings.pre-pre-ban'}},
{'warnings.received',{'warnings.pre-ban'}},
function(player,by_player_name,number_of_warnings)
game.ban_player(player,{'warnings.received',by_player_name,number_of_warnings,{'warnings.ban',{'links.website'}}})
end
},
script_warning_cool_down=30, --- @setting script_warning_cool_down time for a script warning (given by script) to be removed (in minutes)
script_warning_limit=5 --- @setting script_warning_limit the number of script warnings (given by script) that are allowed before full warnings are given
}

View File

@@ -0,0 +1,57 @@
--- Please go to ./config if you want to change settings, each file is commented with what it does
-- if it is not in ./config then you should not attempt to change it unless you know what you are doing
-- all files which are loaded (including the config files) are present in ./config/file_loader.lua
-- this file is the landing point for all scenarios please DO NOT edit directly, further comments are to aid development
log('[START] -----| Explosive Gaming Scenario Loader |-----')
log('[INFO] Setting up lua environment')
-- Require the global overrides
require 'overrides.stages' -- Data stages used in factorio, often used to test for runtime
require 'overrides.print' -- Overrides the _G.print function
require 'overrides.math' -- Omitting the math library is a very bad idea
require 'overrides.table' -- Adds alot more functions to the table module
global.version = require 'overrides.version' -- The current version for exp gaming scenario
inspect = require 'overrides.inspect' -- Used to covert any value into human readable string
Debug = require 'overrides.debug' -- Global Debug module
_C = require 'expcore.common' -- _C is used to store lots of common functions expected to be used
_CHEATS = false
_DEBUG = false
-- Please go to config/file_loader.lua to edit the files that are loaded
log('[INFO] Reading loader config')
local files = require 'config._file_loader'
-- Error handler for loading files
local errors = {}
local error_count = 0
local error_format = '[ERROR] %s :: %s'
local currently_loading = nil
local function error_handler(err)
error_count = error_count + 1
if err:find('module '..currently_loading..' not found;', nil, true) then
log('[ERROR] File not found: '..currently_loading)
errors[error_count] = error_format:format(currently_loading, err)
else
log('[ERROR] Failed to load: '..currently_loading)
errors[error_count] = debug.traceback(error_format:format(currently_loading, err))
end
end
-- Loads all files from the config and logs that they are loaded
local total_file_count = string.format('%3d', #files)
for index, path in pairs(files) do
currently_loading = path
log(string.format('[INFO] Loading file %3d/%s (%s)', index, total_file_count, path))
xpcall(require, error_handler, path)
end
-- Override the default require; require can no longer load new scripts
log('[INFO] Require Overwrite! No more requires can be made!')
require 'overrides.require'
-- Logs all errors again to make it make it easy to find
log('[INFO] All files loaded with '..error_count..' errors:')
for _, error in ipairs(errors) do log(error) end
log('[END] -----| Explosive Gaming Scenario Loader |-----')

View File

@@ -0,0 +1,101 @@
--[[-- Core Module - Async
- An extention of task and token to allow a single require to register and run async functions.
@core Async
@alias Async
@usage
-- To use Async you must register the allowed functions when the file is loaded, often this will just be giving access to
-- some functions within a module if you expect that at part may be blocked by in game permissions or a custom system you have made
-- you may also want to register functions that you want to have a time delay, such as waiting 2 seconds before printing a message
-- When player.admin is called (either command or gui element event) by a player who isnt admin then it will error
-- here we register the function to promote the player so that it will run async and outside the player scope
local promote_player =
Async.register(function(player)
player.admin = true
end)
-- This will allow us to bypass the error by running one tick later outside of any player scope
Async(promote_player, game.player)
-- Here we make an sync function that we want to have a delay, note the delay is not defined here
local print_message =
Async.register(function(player, message)
player.print(message)
end)
-- We can then call the async function with a delay using the wait function
Async.wait(60, print_message, game.player, 'One second has passed!')
]]
local Task = require 'utils.task' --- @dep utils.task
local Token = require 'utils.token' --- @dep utils.token
local Async = {}
local internal_run =
Token.register(function(params)
local func = Token.get(params.token)
return func(table.unpack(params.params))
end)
--[[-- Register a new async function, must called when the file is loaded
@function register
@tparam function callback the function that can be called as an async function
@treturn string the uid of the async function which can be passed to Async.run and Async.wait
@usage-- Registering a function to set the admin state of a player
local set_admin =
Async.register(function(player, state)
if player.valid then
player.admin = state
end
end)
@usage-- Registering a function to print to a player
local print_to_player =
Async.register(function(player, message)
if player.valid then
player.print(message)
end
end)
]]
Async.register = Token.register
--[[-- Runs an async function, you may supply any number of arguments as required by that function
@tparam string token the token of the async function you want to run
@tparam[opt] any ... the other params that you want to pass to your function
@usage-- Make a player admin regardless of if you are admin
Async.run(set_admin, player, true)
]]
function Async.run(token, ...)
Task.queue_task(internal_run, {
token = token,
params = {...}
})
end
--[[-- Runs an async function after the given number of ticks, you may supply any number of arguments as required by that function
@tparam number ticks the number of ticks that you want the function to run after
@tparam string token the token of the async function you want to run
@tparam[opt] any ... the other params that you want to pass to your function
@usage-- Print a message to a player after 5 seconds
Async.wait(300, print_to_player, 'Hello, World!')
]]
function Async.wait(ticks, token, ...)
Task.set_timeout_in_ticks(ticks, internal_run, {
token = token,
params = {...}
})
end
return setmetatable(Async, {
__call = function(self, ...)
self.run(...)
end
})

View File

@@ -0,0 +1,883 @@
--[[-- Core Module - Commands
- Factorio command making module that makes commands with better parse and more modularity
@core Commands
@alias Commands
@usage--- Full code example, see below for explanation
Commands.new_command('repeat-name', 'Will repeat you name a number of times in chat.')
:add_param('repeat-count', 'number-range-int', 1, 5) -- required int in range 1 to 5 inclusive
:add_param('smiley', true, function(input, player, reject) -- optional boolean default false
if not input then return end
input = input:lower()
if input == 'true' or input == 'yes' then return true end
return false
end)
:set_defaults{ smiley = false }
:set_flag('admin_only') -- command is admin only
:add_alias('name', 'rname') -- allow alias: name and rname
:register(function(player, repeat_count, smiley, raw)
game.print(player.name..' used a command with input: '..raw)
local msg = ') '..player.name
if smiley then
msg = ':'..msg
end
for 1 = 1, repeat_count do
Command.print(1..msg)
end
end)
@usage--- Example Command Explanation:
-- Making commands basics, the commands can be set up with any number of params and flags that you want,
-- you can add aliases for the commands and set default values for optional params and of course register your command callback.
-- In our example we will have a command that will repeat the users name in chat X amount of times and only allow admins to use it.
-- First we create the new command, note this will not register the command to the game this is done at the end.
-- We will call the command "repeat-name" and set the help message as follows:
Commands.new_command('repeat-name', 'Will repeat you name a number of times in chat.')
-- Now for our first param, we have named it "repeat-count" and it will be a required value, between 1 and 5 inclusive:
-- By using "number-range-int" we are saying to use this parser to convert our input text, common ones exist in config.expcore.command_general_parse
:add_param('repeat-count', 'number-range-int', 1, 5)
-- Our second param needs a custom parser, meaning it isnt defined with add_parser, this is an option for when it is unlikely for
-- any other command to use the same input type. In the example it is a boolean type and we are just showing it here as part of the example.
-- As for the param its self it will be called "smiley" and will be optional with a default value of false:
:add_param('smiley', true, function(input, player, reject)
-- Since it is optional the input can be nil, in which case we just return
if not input then return end
-- If it is not nil then we check for a truthy value
if input == 'true' or input == 'yes' then return true end
-- Note that because we did not return nil or reject then false will be passed to command callback, see example parse
return false
end)
-- Once all params are defined you can add some default values for your optional params, the default value will be used only
-- when no value is given as input, if an invalid value is given then the command will fail and the default will not be used, the
-- default can also be a function which is passed the player as an argument and should return a value to be the default.
-- Here we set the default for "smiley" to false:
:set_defaults{smiley=false}
-- Another example of defaults if we have: item, amount[opt], player[opt]
:set_defaults{
amount = 50, -- More than one value can be set at a time
player = function(player) return player end -- Default is the player using the command
}
-- Now the params are set up we can alter how the command works, we can set auth flags, add aliases, or enable "auto concat":
:set_flag('admin_only') -- In our case we want "admin_only" to be set to true so only admins can use the command
:add_alias('name', 'rname') -- We also add two aliases here: "name" and "rname" which point to this command
-- :enable_auto_concat() -- We do not use this in our case but this can also be used to enable the "auto concat" feature
-- And finally we want to register a callback to this command, the callback is what defines what the command does, can be as complex as you
-- want it to be, or as simple as our example; the command receives two params plus all param you have defined:
-- 1) the player who used the command
-- 2) in our case repeat_count which will be a number
-- 3) in our case smiley which will be a boolean
-- 4) the raw input; this param is always last as is always present as a catch all
:register(function(player, repeat_count, smiley, raw)
-- This is to show the value for raw as this is an example command, the log file will also show this
game.print(player.name..' used a command with input: '..raw)
local msg = ') '..player.name
if smiley then
msg = ':'..msg
end
for 1 = 1, repeat_count do
-- this print function will return ANY value to the user in a desync safe manor, this includes if the command was used through rcon
Command.print(1..msg)
end
-- See below for what can be used here
end)
-- Values that can be returned from register callback
Commands.print(any, colour[opt]) -- This will return any value value to the user including if it is ran through rcon console
Commands.error(message[opt]) -- This returns a warning to the user, aka an error that does not prevent execution of the command
return Commands.error(message[opt]) -- This returns an error to the user, and will halt the command execution, ie no success message is returned
Commands.success(message[opt]) -- Used to return a success message however don't use this method, see below
return Commands.success(message[opt]) -- Will return the success message to the user and your given message, halts execution
return <any> -- If any value is returned then it will be returned to the player via a Commands.success call
@usage--- Example Authenticator:
-- The command system is best used when you can control who uses commands;
-- to do this you need to define an authenticator which is ran every time a command is run;
-- in this example I will show a simple one that requires certain commands to require the user to be a game admin.
-- For our admin only example we will set a flag to true when we want it to be admin only;
-- when we define the command will will use :set_flag('admin_only');
-- then inside the authenticator we will test if the flag is present using: if flags.admin_only then
-- When the authenticator is called by the command handler it will be passed 4 arguments:
-- 1) player - the player who used the command
-- 2) command - the name of the command that is being used
-- 3) flags - the flags which have been set for this command, flags are set with :set_flag(name, value)
-- 4) reject - the reject function which is the preferred method to prevent execution of the command
-- No return is required to allow the command to execute but it is best practice to return true;
-- we do this in two cases in our authenticator:
-- 1) when the "admin_only" flag is not set, which we take assume that any one can use it
-- 2) when the "admin_only" flag is set, and the player is admin
-- When want to prevent execution of the command we must reject it, listed is how that can be done:
-- 1) return false -- this is the most basic rejection and should only be used while testing
-- 2) return reject -- returning the reject function is as a fail safe in case you forget to call it, same as returning false
-- 3) reject() -- this will block execution while allowing further code to be ran in your authenticator
-- 4) reject('This command is for admins only!') -- using reject as a function allows a error message to be returned
-- 5) return reject() -- using return on either case above is best practice as you should execute all your code before rejecting
-- Example Code:
Commands.add_authenticator(function(player, command, flags, reject)
-- Check if the command is admin only
if flags.admin_only then
-- Return true if player is admin, or reject and return error message
return player.admin or reject('This command is for admins only!')
else
-- Return true if command was not admin only
return true
end
end)
@usage--- Example Parser:
-- Before you make a command it is important to understand the most powerful feature of this command handler;
-- when you define a command you are able to type the params and have then be parsed and validated before your command is executed;
-- This module should be paired with a general command parse but you may want to create your own.
-- For our example we will create a parse to accept only integer numbers in a given range:
-- 1) we will give it the name "number-range-int" this is the "type" that the input is expected to be
-- 2) when we define the type we will also define the min and max of the range so we can use the function more than once
:add_param('repeat_count', 'number-range-int', 5, 10) -- "repeat_count" is a required "number-range-int" in a range 5 to 10 inclusive
-- The command parse will be passed 3 arguments plus any other which you define, in our case:
-- 1) input - the input that has been given by the user for this param, the role of this function is to transform this value
-- nb: the input is a string but can be nil if the param is marked as optional
-- 2) player - the player who is using the command, this is always present
-- 3) reject - the reject function to throw an error to the user, this is always present
-- 4) range_min - the range min, this is user defined and has the value given when the param is defined
-- 5) range_max - the range max, this is user defined and has the value given when the param is defined
-- When returning from the param parse you have a few options with how to do this:
-- 1) you return the new value for the param (any non nil value) this value is then passed to the command callback
-- 2) not returning will cause a generic invalid error and the command is rejected, not recommenced
-- 3) return reject -- this is just a failsafe in case the function is not called, same as no return
-- 4) return reject() -- will give a shorter error message as you pass a nil custom error
-- 5) return reject('Number entered is not in range: '..range_min..', '..range_max) -- returns a custom error to the user
-- nb: if you do not return reject after you call it then you will still be returning nil so there will be a duplicate error message
-- It should be noted that if you want to expand on an existing parse you can use Commands.parse(type, input, player, reject)
-- this function will either return a new value for the input or nil, if it is nil you should return nil to prevent duplicate
-- error messages to the user:
input = Commands.parse('number-int', input, player, reject)
if not input then return end -- nil check
-- Example Code:
Commands.add_parse('number-range-int', function(input, player, reject, range_min, range_max)
local rtn = tonumber(input) and math.floor(tonumber(input)) or nil -- converts input to number
if not rtn or rtn < range_min or rtn > range_max then
-- the input is either not a number or is outside the range
return reject('Number entered is not in range: '..range_min..', '..range_max)
else
-- returns the input as a number value rather than a string, thus the param is now the correct type
return rtn
end
end)
]]
local Game = require 'utils.game' --- @dep utils.game
local player_return, write_json = _C.player_return, _C.write_json --- @dep expcore.common
local trace = debug.traceback
local Commands = {
--- Constant values used by the command system
defines = {
error = 'CommandError',
unauthorized = 'CommandErrorUnauthorized',
success = 'CommandSuccess'
},
--- An array of all custom commands that are registered
commands = {},
--- When true any authenticator error will result in authorization failure, more secure
authorization_failure_on_error = false,
--- An array of all custom authenticators that are registered
authenticators = {},
--- Used to store default functions which are common parse function such as player or number in range
parsers = {},
--- Returns a value to the player, different to success as this does not signal the end of your command
print = player_return,
--- The command prototype which stores all command defining functions
_prototype = {},
}
--- Authentication.
-- Functions that control who can use commands
-- @section auth
--[[-- Adds an authorization function, function used to check if a player if allowed to use a command
@tparam function authenticator The function you want to register as an authenticator
@treturn number The index it was inserted at, used to remove the authenticator
@usage-- If the admin_only flag is set, then make sure the player is an admin
local admin_authenticator =
Commands.add_authenticator(function(player, command, flags, reject)
if flags.admin_only and not player.admin then
return reject('This command is for admins only!')
else
return true
end
end)
]]
function Commands.add_authenticator(authenticator)
local next_index = #Commands.authenticators + 1
Commands.authenticators[next_index] = authenticator
return next_index
end
--[[-- Removes an authorization function, can use the index or the function value
@tparam function|number authenticator The authenticator to remove, either the index return from add_authenticator or the function used
@treturn boolean If the authenticator was found and removed successfully
@usage-- Removing the admin authenticator, can not be done during runtime
Commands.remove_authenticator(admin_authenticator)
]]
function Commands.remove_authenticator(authenticator)
if type(authenticator) == 'number' then
-- If a number is passed then it is assumed to be the index
if Commands.authenticators[authenticator] then
Commands.authenticators[authenticator] = nil
return true
end
else
-- will search the array and remove the key
for index, value in pairs(Commands.authenticators) do
if value == authenticator then
Commands.authenticators[index] = nil
return true
end
end
end
return false
end
--[[-- Mostly used internally, calls all authenticators, returns if the player is authorized
@tparam LuaPlayer player The player who is using the command, passed to authenticators
@tparam string command_name The name of the command being used, passed to authenticators
@treturn[1] boolean true Player is authorized
@treturn[1] string commands Define value for success
@treturn[2] boolean false Player is unauthorized
@treturn[2] string|locale_string The reason given by the failed authenticator
@usage-- Test if a player can use "repeat-name"
local authorized, status = Commands.authorize(game.player, 'repeat-name')
]]
function Commands.authorize(player, command_name)
local command_data = Commands.commands[command_name]
if not command_data then return false end
if not player then return true end
-- This is the reject function given to authenticators
local failure_message
local function reject(message)
failure_message = message or {'expcore-commands.unauthorized'}
return Commands.defines.unauthorized
end
-- This is the internal error function used when an authenticator errors
local function authenticator_error(err)
log('[ERROR] Authorization failed: '..trace(err))
if Commands.authorization_failure_on_error then
return reject('Internal Error')
end
end
-- Loops over each authenticator, if any return false then then command will not be ran
for _, authenticator in pairs(Commands.authenticators) do
-- player: LuaPlayer, command: string, flags: table, reject: function(error_message: string)
local _, rtn = xpcall(authenticator, authenticator_error, player, command_name, command_data.flags, reject)
if rtn == false or rtn == Commands.defines.unauthorized or rtn == reject or failure_message ~= nil then
if failure_message == nil then failure_message = {'expcore-commands.unauthorized'} end
return false, failure_message
end
end
return true, Commands.defines.success
end
--- Parse.
-- Functions that help with parsing
-- @section parse
--[[-- Adds a parse function which can be called by name (used in add_param)
nb: this is not required as you can use the callback directly this just allows it to be called by name
@tparam string name The name of the parse, should describe a type of input such as number or player, must be unique
@tparam function parser The function that is ran to parse the input string
@treturn boolean Was the parse added, will be false if the name is already used
@usage-- Adding a parse to validate integers in a given range
Commands.add_parse('number-range-int', function(input, player, reject, range_min, range_max)
local rtn = tonumber(input) and math.floor(tonumber(input)) or nil -- converts input to number
if not rtn or rtn < range_min or rtn > range_max then
-- The input is either not a number or is outside the range
return reject('Number entered is not in range: '..range_min..', '..range_max)
else
-- Returns the input as a number rather than a string, thus the param is now the correct type
return rtn
end
end)
]]
function Commands.add_parse(name, parser)
if Commands.parsers[name] then return false end
Commands.parsers[name] = parser
return true
end
--[[-- Removes a parse function, see add_parse for adding them, cant be done during runtime
@tparam string name The name of the parse to remove
@usage-- Removing a parse
Commands.remove_parse('number-range-int')
]]
function Commands.remove_parse(name)
Commands.parsers[name] = nil
end
--[[-- Intended to be used within other parse functions, runs a parse and returns success and new value
@tparam string name The name of the parse to call, must be a registered parser
@tparam string input The input to pass to the parse, must be a string but not necessarily the original input
@tparam LuaPlayer player The player that is using the command, pass directly from your arguments
@tparam function reject The reject function, pass directly from your arguments
@treturn any The new value for the input, if nil is return then either there was an error or the input was nil
@usage-- Parsing an int after first checking it is a number
Commands.add_parse('number', function(input, player, reject)
local number = tonumber(input)
if number then return number end
return reject('Input must be a number value')
end)
Commands.add_parse('number-int', function(input, player, reject)
local number = Commands.parse('number', input, player, reject)
if not number then return end
return math.floor(number)
end)
]]
function Commands.parse(name, input, player, reject, ...)
if not Commands.parsers[name] then return end
local success, rtn = pcall(Commands.parsers[name], input, player, reject, ...)
if not success then error(rtn, 2) return end
if not rtn or rtn == Commands.defines.error then return end
return rtn
end
--- Getters.
-- Functions that get commands
-- @section getters
--[[-- Gets all commands that a player is allowed to use, game commands are not included
@tparam[opt] LuaPlayer player The player that you want to get commands of, nil will return all commands
@treturn table All commands that that player is allowed to use, or all commands
@usage-- Get the commands you are allowed to use
local commands = Commands.get(game.player)
@usage-- Get all commands that are registered
local commands = Commands.get()
]]
function Commands.get(player)
player = Game.get_player_from_any(player)
if not player then
return Commands.commands
end
local allowed = {}
for name, command_data in pairs(Commands.commands) do
if Commands.authorize(player, name) then
allowed[name] = command_data
end
end
return allowed
end
--[[-- Searches command names and help messages to find possible commands, game commands are included
@tparam string keyword The word which you are trying to find in your search
@tparam[opt] LuaPlayer player The player to get allowed commands of, if nil all commands are searched
@treturn table All commands that contain the key word, and allowed by the player if a player was given
@usage-- Get all commands which "repeat"
local commands = Commands.search('repeat')
@usage-- Get all commands which "repeat" and you are allowed to use
local commands = Commands.search('repeat', game.player)
]]
function Commands.search(keyword, player)
local custom_commands = Commands.get(player)
local matches = {}
keyword = keyword:lower()
-- Loops over custom commands
for name, command_data in pairs(custom_commands) do
-- combines name help and aliases into one message to be searched
local search = string.format('%s %s %s %s', name, command_data.help, command_data.searchable_description, table.concat(command_data.aliases, ' '))
if search:lower():match(keyword) then
matches[name] = command_data
end
end
-- Loops over the names of game commands
for name, description in pairs(commands.game_commands) do
if name:lower():match(keyword) then
-- because game commands lack some stuff that the custom ones have they are formatted
matches[name] = {
name = name,
help = description,
description = '',
aliases = {}
}
end
end
return matches
end
--- Creation.
-- Functions that create a new command
-- @section creation
--[[-- Creates a new command object to added details to, this does not register the command to the game api
@tparam string name The name of the command to be created
@tparam string help The help message for the command
@treturn table This will be used with other functions to define the new command
@usage-- Define a new command
Commands.new_command('repeat-name', 'Will repeat you name a number of times in chat.')
]]
function Commands.new_command(name, help, descr)
local command = setmetatable({
name = name,
help = help,
searchable_description = descr or '',
callback = function() Commands.internal_error(false, name, 'No callback registered') end,
auto_concat = false,
min_param_count = 0,
max_param_count = 0,
flags = {}, -- stores flags that can be used by auth
aliases = {}, -- stores aliases to this command
params = {}, -- [param_name] = {optional: boolean, default: any, parse: function, parse_args: table}
}, {
__index = Commands._prototype
})
Commands.commands[name] = command
return command
end
--[[-- Adds a new param to the command this will be displayed in the help and used to parse the input
@tparam string name The name of the new param that is being added to the command
@tparam[opt=false] boolean optional Is this param optional, these must be added after all required params
@tparam[opt] ?string|function parse This function will take the input and return a new value, if not given no parse is done
@tparam[opt] any ... Extra args you want to pass to the parse function; for example if the parse is general use
@treturn table Pass through to allow more functions to be called
@usage-- Adding a required param which has a parser pre-defined
command:add_param('repeat-count', 'number-range-int', 1, 5)
@usage-- Adding an optional param which has a custom parse, see Commands.add_parse for details
command:add_param('smiley', true, function(input, player, reject)
if not input then return end
return input:lower() == 'true' or input:lower() == 'yes' or false
end)
]]
function Commands._prototype:add_param(name, optional, parse, ...)
local parse_args = {...}
if type(optional) ~= 'boolean' then
parse_args = {parse, ...}
parse = optional
optional = false
end
self.params[name] = {
optional = optional,
parse = parse or function(string) return string end,
parse_args = parse_args
}
self.max_param_count = self.max_param_count + 1
if not optional then
self.min_param_count = self.min_param_count + 1
end
return self
end
--[[-- Add default values to params, only as an effect if the param is optional, if default value is a function it is called with the acting player
@tparam table defaults A table which is keyed by the name of the param and the value is the default value for that param
@treturn table Pass through to allow more functions to be called
@usage-- Adding default values
command:set_defaults{
smiley = false,
-- not in example just used to show arguments given
player_name = function(player)
return player.name
end
}
]]
function Commands._prototype:set_defaults(defaults)
for name, value in pairs(defaults) do
if self.params[name] then
self.params[name].default = value
end
end
return self
end
--[[-- Adds a flag to the command which is passed via the flags param to the authenticators, can be used to assign command roles or usage type
@tparam string name The name of the flag to be added, set to true if no value is given
@tparam[opt=true] any value The value for the flag, can be anything that the authenticators are expecting
@treturn table Pass through to allow more functions to be called
@usage-- Setting a custom flag
command:set_flag('admin_only', true)
@usage-- When value is true it does not need to be given
command:set_flag('admin_only')
]]
function Commands._prototype:set_flag(name, value)
self.flags[name] = value or true
return self
end
--[[-- Adds an alias, or multiple, that will be registered to this command, eg /teleport can be used as /tp
@tparam string ... Any amount of aliases that you want this command to be callable with
@treturn table Pass through to allow more functions to be called
@usage-- Added multiple aliases to a command
command:add_alias('name', 'rname')
]]
function Commands._prototype:add_alias(...)
local start_index = #self.aliases
for index, alias in ipairs{...} do
self.aliases[start_index+index] = alias
end
return self
end
--[[-- Enables auto concatenation for this command, all params after the last are added to the last param, useful for reasons or other long text input
nb: this will disable max param checking as they will be concatenated onto the end of that last param
@treturn table Pass through to allow more functions to be called
@usage-- Enable auto concat for a command
command:enable_auto_concat()
]]
function Commands._prototype:enable_auto_concat()
self.auto_concat = true
return self
end
--[[-- Adds the callback to the command and registers: aliases, params and help message with the base game api
nb: this must be the last function ran on the command and must be done for the command to work
@tparam function callback The callback for the command, will receive the player running command, and any params added with add_param
@usage-- Registering your command to the base game api
command:register(function(player, repeat_count, smiley, raw)
local msg = ') '..player.name
if smiley then msg = ':'..msg end
for 1 = 1, repeat_count do
Command.print(1..msg)
end
end)
]]
function Commands._prototype:register(callback)
self.callback = callback
-- Generates a description to be used
local description = ''
for param_name, param_details in pairs(self.params) do
if param_details.optional then
description = string.format('%s [%s]', description, param_name)
else
description = string.format('%s <%s>', description, param_name)
end
end
self.description = description
-- Last resort error handler for commands
local function command_error(err)
Commands.internal_error(false, self.name, trace(err))
end
-- Callback that the game will call
local function command_callback(event)
event.name = self.name
xpcall(Commands.run_command, command_error, event)
end
-- Registers the command under its own name
local help = {'expcore-commands.command-help', description, self.help}
commands.add_command(self.name, help, command_callback)
-- Adds any aliases that it has
for _, alias in pairs(self.aliases) do
if not commands.commands[alias] and not commands.game_commands[alias] then
commands.add_command(alias, help, command_callback)
end
end
end
--- Status.
-- Functions that indicate status
-- @section status
--[[-- Sends a value to the player, followed by a command complete message, returning a value will trigger this automatically
@tparam[opt] any value The value to return to the player, if nil then only the success message is returned
@treturn Commands.defines.success Return this to the command handler to prevent two success messages
@usage-- Print a custom success message
return Commands.success('Your message has been printed')
@usage-- Returning the value has the same result
return 'Your message has been printed'
]]
function Commands.success(value)
if value ~= nil then player_return(value) end
player_return({'expcore-commands.command-ran'}, 'cyan')
return Commands.defines.success
end
--[[-- Sends a value to the player, different to success as this does not signal the end of your command
@function print
@tparam any value The value that you want to return to the player
@tparam table colour The colour of the message that the player sees
@usage-- Output a message to the player
Commands.print('Your command is in progress')
]]
--[[-- Sends an error message to the player and when returned will stop execution of the command
nb: this is for non fatal errors meaning there is no log of this event, use during register callback
@tparam[opt=''] string error_message An optional error message that can be sent to the user
@tparam[opt=utility/wire_pickup] string play_sound The sound to play for the error
@treturn Commands.defines.error Return this to command handler to terminate execution
@usage-- Send an error message to the player, and stops further code running
return Commands.error('The player you selected is offline')
]]
function Commands.error(error_message, play_sound)
error_message = error_message or ''
player_return({'expcore-commands.command-fail', error_message}, 'orange_red')
if play_sound ~= false then
play_sound = play_sound or 'utility/wire_pickup'
if game.player then game.player.play_sound{path=play_sound} end
end
return Commands.defines.error
end
--[[-- Sends an error to the player and logs the error, used internally please avoid direct use
nb: use error(error_message) within your callback to trigger do not trigger directly as code execution may still continue
@tparam boolean success The success value returned from pcall, or just false to trigger error
@tparam string command_name The name of the command this is used within the log
@tparam string error_message The error returned by pcall or some other error, this is logged and not returned to player
@treturn boolean The opposite of success so true means to cancel execution, used internally
@usage-- Used in the command system to log handler errors
local success, err = pcall(command_data.callback, player, table.unpack(params))
if Commands.internal_error(success, command_data.name, err) then
return command_log(player, command_data, 'Internal Error: Command Callback Fail', raw_params, command_event.parameter, err)
end
]]
function Commands.internal_error(success, command_name, error_message)
if not success then
Commands.error('Internal Error, Please contact an admin', 'utility/cannot_build')
log{'expcore-commands.command-error-log-format', command_name, error_message}
end
return not success
end
--- Logs command usage to file
local function command_log(player, command, comment, params, raw, details)
local player_name = player and player.name or '<Server>'
write_json('log/commands.log', {
player_name = player_name,
command_name = command.name,
comment = comment,
details = details,
params = params,
raw = raw
})
end
--- Main event function that is ran for all commands, used internally please avoid direct use
-- @tparam table command_event Passed directly from the add_command function
-- @usage Commands.run_command(event)
function Commands.run_command(command_event)
local command_data = Commands.commands[command_event.name]
-- Player can be nil when it is the server
local player
if command_event.player_index and command_event.player_index > 0 then
player = game.players[command_event.player_index]
end
-- Check if the player is allowed to use the command
local authorized, auth_fail = Commands.authorize(player, command_data.name)
if not authorized then
command_log(player, command_data, 'Failed Auth', {}, command_event.parameter)
Commands.error(auth_fail, 'utility/cannot_build')
return
end
-- Check for parameter being nil
if command_data.min_param_count > 0 and not command_event.parameter then
command_log(player, command_data, 'No Params Given', {}, command_event.parameter)
Commands.error{'expcore-commands.invalid-inputs', command_data.name, command_data.description}
return
end
-- Extract quoted arguments
local raw_input = command_event.parameter or ''
local quote_params = {}
local input_string = raw_input:gsub('"[^"]-"', function(word)
local no_spaces = word:gsub('%s', '%%s')
quote_params[no_spaces] = word:sub(2, -2)
return ' '..no_spaces..' '
end)
-- Extract unquoted arguments
local raw_params = {}
local last_index = 0
local param_number = 0
for word in input_string:gmatch('%S+') do
param_number = param_number + 1
if param_number > command_data.max_param_count then
-- there are too many params given to the command
if not command_data.auto_concat then
-- error as they should not be more
command_log(player, command_data, 'Invalid Input: Too Many Params', raw_params, raw_input)
Commands.error{'expcore-commands.invalid-inputs', command_data.name, command_data.description}
return
else
-- concat to the last param
if quote_params[word] then
raw_params[last_index] = raw_params[last_index]..' "'..quote_params[word]..'"'
else
raw_params[last_index] = raw_params[last_index]..' '..word
end
end
else
-- new param that needs to be added
if quote_params[word] then
last_index = last_index + 1
raw_params[last_index] = quote_params[word]
else
last_index = last_index + 1
raw_params[last_index] = word
end
end
end
-- Check the param count
local param_count = #raw_params
if param_count < command_data.min_param_count then
command_log(player, command_data, 'Invalid Input: Not Enough Params', raw_params, raw_input)
Commands.error{'expcore-commands.invalid-inputs', command_data.name, command_data.description}
return
end
-- Parse the arguments
local index = 1
local params = {}
for param_name, param_data in pairs(command_data.params) do
local parse_callback = param_data.parse
-- If its a string this get it from the parser table
if type(parse_callback) == 'string' then
parse_callback = Commands.parsers[parse_callback]
end
-- If its not a function throw and error
if type(parse_callback) ~= 'function' then
Commands.internal_error(false, command_data.name, 'Invalid param parse '..tostring(param_data.parse))
command_log(player, command_data, 'Internal Error: Invalid Param Parse', params, raw_input, tostring(param_data.parse))
return
end
-- This is the reject function given to parse callbacks
local function reject(error_message)
error_message = error_message or ''
command_log(player, command_data, 'Invalid Param Given', raw_params, input_string)
return Commands.error{'expcore-commands.invalid-param', param_name, error_message}
end
-- input: string, player: LuaPlayer, reject: function, ... extra args
local success, param_parsed = pcall(parse_callback, raw_params[index], player, reject, table.unpack(param_data.parse_args))
if Commands.internal_error(success, command_data.name, param_parsed) then
return command_log(player, command_data, 'Internal Error: Param Parse Fail', params, raw_input, param_parsed)
end
if param_data.optional == true and raw_params[index] == nil then
-- If the param is optional and nil then it is set to default
param_parsed = param_data.default
if type(param_parsed) == 'function' then
success, param_parsed = pcall(param_parsed, player)
if Commands.internal_error(success, command_data.name, param_parsed) then
return command_log(player, command_data, 'Internal Error: Default Value Fail', params, raw_input, param_parsed)
end
end
elseif param_parsed == nil or param_parsed == Commands.defines.error or param_parsed == reject then
-- No value was returned or error was returned, if nil then give generic error
if param_parsed ~= Commands.defines.error then
command_log(player, command_data, 'Invalid Param Given', raw_params, raw_input, param_name)
Commands.error{'expcore-commands.command-error-param-format', param_name, 'please make sure it is the correct type'}
end
return
end
-- Add the param to the table to be passed to the command callback
params[index] = param_parsed
index = index + 1
end
-- Run the command
-- player: LuaPlayer, ... command params, raw: string
params[command_data.max_param_count+1] = raw_input
local success, rtn = pcall(command_data.callback, player, table.unpack(params))
if Commands.internal_error(success, command_data.name, rtn) then
return command_log(player, command_data, 'Internal Error: Command Callback Fail', raw_params, command_event.parameter, rtn)
end
-- Give output to the player
if rtn == Commands.defines.error or rtn == Commands.error then
return command_log(player, command_data, 'Custom Error', raw_params, raw_input)
elseif rtn ~= Commands.defines.success and rtn ~= Commands.success then
Commands.success(rtn)
end
command_log(player, command_data, 'Success', raw_params, raw_input)
end
return Commands

View File

@@ -0,0 +1,825 @@
--[[-- Core Module - Common
- Adds some commonly used functions used in many modules
@core Common
@alias Common
]]
local Colours = require 'utils.color_presets' --- @dep utils.color_presets
local Game = require 'utils.game' --- @dep utils.game
local Common = {}
--- Type Checking.
-- @section typeCheck
--[[-- Asserts the argument is of type test_type
@tparam any value the value to be tested
@tparam[opt=nil] string test_type the type to test for if not given then it tests for nil
@treturn boolean is v of type test_type
@usage-- Check for a string value
local is_string = type_check(value, 'string')
@usage-- Check for a nil value
local is_nil = type_check(value)
]]
function Common.type_check(value, test_type)
return test_type and value and type(value) == test_type or not test_type and not value or false
end
--[[-- Raises an error if the value is of the wrong type
@tparam any value the value that you want to test the type of
@tparam string test_type the type that the value should be
@tparam string error_message the error message that is returned
@tparam number level the level to call the error on (level = 1 is the caller)
@treturn boolean true if no error was called
@usage-- Raise error if value is not a number
type_error(value, 'number', 'Value must be a number')
]]
function Common.type_error(value, test_type, error_message, level)
level = level and level+1 or 2
return Common.type_check(value, test_type) or error(error_message, level)
end
--[[-- Asserts the argument is one of type test_types
@param value the variable to check
@param test_types the type as a table of strings
@treturn boolean true if value is one of test_types
@usage-- Check for a string or table
local is_string_or_table = multi_type_check(value, {'string', 'table'})
]]
function Common.multi_type_check(value, test_types)
local vtype = type(value)
for _, arg_type in ipairs(test_types) do
if vtype == arg_type then
return true
end
end
return false
end
--[[-- Raises an error if the value is of the wrong type
@tparam any value the value that you want to test the type of
@tparam table test_types the type as a table of strings
@tparam string error_message the error message that is returned
@tparam number level the level to call the error on (level = 1 is the caller)
@treturn boolean true if no error was called
@usage-- Raise error if value is not a string or table
multi_type_error('foo', {'string', 'table'}, 'Value must be a string or table')
]]
function Common.multi_type_error(value, test_types, error_message, level)
level = level and level+1 or 2
return Common.mult_type_check(value, test_types) or error(error_message, level)
end
--[[-- Raises an error when the value is the incorrect type, uses a consistent error message format
@tparam any value the value that you want to test the type of
@tparam string test_type the type that the value should be
@tparam number param_number the number param it is
@tparam[opt] string param_name the name of the param
@treturn boolean true if no error was raised
@usage-- Output: "Bad argument #2 to "<anon>"; argument is of type string expected number"
validate_argument_type(value, 'number', 2)
@usage-- Output: "Bad argument #2 to "<anon>"; "repeat_count" is of type string expected number"
validate_argument_type(value, 'number', 2, 'repeat_count')
]]
function Common.validate_argument_type(value, test_type, param_number, param_name)
if not Common.test_type(value, test_type) then
local function_name = debug.getinfo(2, 'n').name or '<anon>'
local error_message
if param_name then
error_message = string.format('Bad argument #%d to %q; %q is of type %s expected %s', param_number, function_name, param_name, type(value), test_type)
else
error_message = string.format('Bad argument #%d to %q; argument is of type %s expected %s', param_number, function_name, type(value), test_type)
end
return error(error_message, 3)
end
return true
end
--[[-- Raises an error when the value is the incorrect type, uses a consistent error message format
@tparam any value the value that you want to test the type of
@tparam string test_types the types that the value should be
@tparam number param_number the number param it is
@tparam[opt] string param_name the name of the param
@treturn boolean true if no error was raised
@usage-- Output: "Bad argument #2 to "<anon>"; argument is of type number expected string or table"
validate_argument_type(value, {'string', 'table'}, 2)
@usage-- Output: "Bad argument #2 to "<anon>"; "player" is of type number expected string or table"
validate_argument_type(value, {'string', 'table'}, 2, 'player')
]]
function Common.validate_argument_multi_type(value, test_types, param_number, param_name)
if not Common.multi_type_check(value, test_types) then
local function_name = debug.getinfo(2, 'n').name or '<anon>'
local error_message
if param_name then
error_message = string.format('Bad argument #%2d to %q; %q is of type %s expected %s', param_number, function_name, param_name, type(value), table.concat(test_types, ' or '))
else
error_message = string.format('Bad argument #%2d to %q; argument is of type %s expected %s', param_number, function_name, type(value), table.concat(test_types, ' or '))
end
return error(error_message, 3)
end
return true
end
--- Will raise an error if called during runtime
-- @usage error_if_runtime()
function Common.error_if_runtime()
if _LIFECYCLE == 8 then
local function_name = debug.getinfo(2, 'n').name or '<anon>'
error(function_name..' can not be called during runtime', 3)
end
end
--- Will raise an error if the function is a closure
-- @usage error_if_runetime_closure(func)
function Common.error_if_runetime_closure(func)
if _LIFECYCLE == 8 and Debug.is_closure(func) then
local function_name = debug.getinfo(2, 'n').name or '<anon>'
error(function_name..' can not be called during runtime with a closure', 3)
end
end
--- Value Returns.
-- @section valueReturns
--[[-- Tests if a string contains a given substring.
@tparam string s the string to check for the substring
@tparam string contains the substring to test for
@treturn boolean true if the substring was found in the string
@usage-- Test if a string contains a sub string
local found = string_contains(str, 'foo')
]]
function Common.string_contains(s, contains)
return s and string.find(s, contains) ~= nil
end
--[[-- Used to resolve a value that could also be a function returning that value
@tparam any value the value which you want to test is not nil and if it is a function then call the function
@treturn any the value given or returned by value if it is a function
@usage-- Default value handling
-- if default value is not a function then it is returned
-- if default value is a function then it is called with the first argument being self
local value = Common.resolve_value(self.defaut_value, self)
]]
function Common.resolve_value(value, ...)
return value and type(value) == 'function' and value(...) or value
end
--- Converts a varible into its boolean value, nil and false return false
-- @treturn boolean the boolean form of the varible
-- @usage local bool = cast_bool(var)
function Common.cast_bool(var)
return var and true or false
end
--- Returns either the second or third argument based on the first argument
-- @usage ternary(input_string == 'test', 'Input is test', 'Input is not test')
function Common.ternary(c, t, f)
return c and t or f
end
--- Returns a string for a number with comma seperators
-- @usage comma_value(input_number)
function Common.comma_value(n) -- credit http://richard.warburton.it
local left, num, right = string.match(n, '^([^%d]*%d)(%d*)(.-)$')
return left .. (num:reverse():gsub('(%d%d%d)', '%1, '):reverse()) .. right
end
--[[-- Sets a table element to value while also returning value.
@tparam table tbl to change the element of
@tparam string key the key to set the value of
@tparam any value the value to set the key as
@treturn any the value that was set
@usage-- Set and return value
local value = set_and_return(players, player.name, player.online_time)
]]
function Common.set_and_return(tbl, key, value)
tbl[key] = value
return value
end
--[[-- Writes a table object to a file in json format
@tparam string path the path of the file to write include / to use dir
@tparam table tbl the table that will be converted to a json string and wrote to file
@usage-- Write a lua table as a json to script-outpt/dump
write_json('dump', tbl)
]]
function Common.write_json(path, tbl)
game.write_file(path, game.table_to_json(tbl)..'\n', true, 0)
end
--[[-- Calls a require that will not error if the file is not found
@usage local file = opt_require('file.not.present') -- will not cause any error
@tparam string path the path that you want to require
@return the returns from that file or nil, error if not loaded
@usage-- Require a file without causing errors, for when a file might not exist
local Module = opt_require 'expcore.common'
]]
function Common.opt_require(path)
local success, rtn = pcall(require, path)
if success then return rtn
else return nil, rtn end
end
--[[-- Returns a desync safe file path for the current file
@tparam[opt=0] number offset the offset in the stack to get, 0 is current file
@treturn string the file path
@usage-- Get the current file path
local file_path = get_file_path()
]]
function Common.get_file_path(offset)
offset = offset or 0
return debug.getinfo(offset+2, 'S').source:match('^.+/currently%-playing/(.+)$'):sub(1, -5)
end
--[[-- Converts a table to an enum
@tparam table tbl table the that will be converted
@treturn table the new table that acts like an enum
@usage-- Make an enum
local colors = enum{
'red',
'green',
'blue'
}
]]
function Common.enum(tbl)
local rtn = {}
for k, v in pairs(tbl) do
if type(k) ~= 'number' then
rtn[v]=k
end
end
for k, v in pairs(tbl) do
if type(k) == 'number' then
table.insert(rtn, v)
end
end
for k, v in pairs(rtn) do
rtn[v]=k
end
return rtn
end
--[[-- Returns the closest match to the input
@tparam table options table a of options for the auto complete
@tparam string input string the input that will be completed
@tparam[opt=false] boolean use_key when true the keys of options will be used as the options
@tparam[opt=false] boolean rtn_key when true the the key will be returned rather than the value
@return the list item found that matches the input
@usage-- Get the element that includes "foo"
local value = auto_complete(tbl, "foo")
@usage-- Get the element with a key that includes "foo"
local value = auto_complete(tbl, "foo", true)
@usage-- Get the key with that includes "foo"
local key = auto_complete(tbl, "foo", true, true)
]]
function Common.auto_complete(options, input, use_key, rtn_key)
if type(input) ~= 'string' then return end
input = input:lower()
for key, value in pairs(options) do
local check = use_key and key or value
if Common.string_contains(string.lower(check), input) then
return rtn_key and key or value
end
end
end
--- Formatting.
-- @section formatting
--[[-- Returns a valid string with the name of the actor of a command.
@tparam string player_name the name of the player to use rather than server, used only if game.player is nil
@treturn string the name of the current actor
@usage-- Get the current actor
local player_name = get_actor()
]]
function Common.get_actor(player_name)
return game.player and game.player.name or player_name or '<server>'
end
--[[-- Returns a message with valid chat tags to change its colour
@tparam string message the message that will be in the output
@tparam table color a color which contains r, g, b as its keys
@treturn string the message with the color tags included
@usage-- Use factorio tags to color a chat message
local message = format_chat_colour('Hello, World!', { r=355, g=100, b=100 })
]]
function Common.format_chat_colour(message, color)
color = color or Colours.white
local color_tag = '[color='..math.round(color.r, 3)..', '..math.round(color.g, 3)..', '..math.round(color.b, 3)..']'
return string.format('%s%s[/color]', color_tag, message)
end
--[[-- Returns a message with valid chat tags to change its colour, using localization
@tparam ?string|table message the message that will be in the output
@tparam table color a color which contains r, g, b as its keys
@treturn table the message with the color tags included
@usage-- Use factorio tags and locale strings to color a chat message
local message = format_chat_colour_localized('Hello, World!', { r=355, g=100, b=100 })
]]
function Common.format_chat_colour_localized(message, color)
color = color or Colours.white
color = math.round(color.r, 3)..', '..math.round(color.g, 3)..', '..math.round(color.b, 3)
return {'color-tag', color, message}
end
--[[-- Returns the players name in the players color
@tparam LuaPlayer player the player to use the name and color of
@tparam[opt=false] boolean raw_string when true a string is returned rather than a localized string
@treturn table the players name with tags for the players color
@usage-- Format a players name using the players color as a string
local message = format_chat_player_name(game.player, true)
]]
function Common.format_chat_player_name(player, raw_string)
player = Game.get_player_from_any(player)
local player_name = player and player.name or '<Server>'
local player_chat_colour = player and player.chat_color or Colours.white
if raw_string then
return Common.format_chat_colour(player_name, player_chat_colour)
else
return Common.format_chat_colour_localized(player_name, player_chat_colour)
end
end
--[[-- Will return a value of any type to the player/server console, allows colour for in-game players
@tparam any value a value of any type that will be returned to the player or console
@tparam[opt=defines.colour.white] ?defines.color|string colour the colour of the text for the player, ignored when printing to console
@tparam[opt=game.player] LuaPlayer player the player that return will go to, if no game.player then returns to server
@usage-- Return a value to the current actor, rcon included
player_return('Hello, World!')
@usage-- Return a value to the current actor, with color
player_return('Hello, World!', 'green')
@usage-- Return to a player other than the current
player_return('Hello, World!', nil, player)
]]
function Common.player_return(value, colour, player)
colour = Common.type_check(colour, 'table') and colour or Colours[colour] ~= Colours.white and Colours[colour] or Colours.white
player = player or game.player
-- converts the value to a string
local returnAsString
if Common.type_check(value, 'table') or type(value) == 'userdata' then
if Common.type_check(value.__self, 'userdata') or type(value) == 'userdata' then
-- value is userdata
returnAsString = 'Cant Display Userdata'
elseif Common.type_check(value[1], 'string') and string.find(value[1], '.+[.].+') and not string.find(value[1], '%s') then
-- value is a locale string
returnAsString = value
elseif getmetatable(value) ~= nil and not tostring(value):find('table: 0x') then
-- value has a tostring meta method
returnAsString = tostring(value)
else
-- value is a table
returnAsString = table.inspect(value, {depth=5, indent=' ', newline='\n'})
end
elseif Common.type_check(value, 'function') then
-- value is a function
returnAsString = 'Cant Display Functions'
else returnAsString = tostring(value) end
-- returns to the player or the server
if player then
-- allows any valid player identifier to be used
player = Game.get_player_from_any(player)
if not player then error('Invalid Player given to player_return', 2) end
-- plays a nice sound that is different to normal message sound
player.play_sound{path='utility/scenario_message'}
player.print(returnAsString, colour)
else rcon.print(returnAsString) end
end
--[[-- Formats tick into a clean format, denominations from highest to lowest
-- time will use : separates
-- when a denomination is false it will overflow into the next one
@tparam number ticks the number of ticks that represents a time
@tparam table options table a of options to use for the format
@treturn string a locale string that can be used
@usage-- Output: "0h 5m"
local time = format_time(18000, { hours=true, minutes=true, string=true })
@usage-- Output: "0 hours and 5 minutes"
local time = format_time(18000, { hours=true, minutes=true, string=true, long=true })
@usage-- Output: "00:05:00"
local time = format_time(18000, { hours=true, minutes=true, seconds=true, string=true })
@usage-- Output: "--:--:--"
local time = format_time(18000, { hours=true, minutes=true, seconds=true, string=true, null=true })
]]
function Common.format_time(ticks, options)
-- Sets up the options
options = options or {
days=false,
hours=true,
minutes=true,
seconds=false,
long=false,
time=false,
string=false,
null=false
}
-- Basic numbers that are used in calculations
local max_days, max_hours, max_minutes, max_seconds = ticks/5184000, ticks/216000, ticks/3600, ticks/60
local days, hours = max_days, max_hours-math.floor(max_days)*24
local minutes, seconds = max_minutes-math.floor(max_hours)*60, max_seconds-math.floor(max_minutes)*60
-- Handles overflow of disabled denominations
local rtn_days, rtn_hours, rtn_minutes, rtn_seconds = math.floor(days), math.floor(hours), math.floor(minutes), math.floor(seconds)
if not options.days then
rtn_hours = rtn_hours + rtn_days*24
end
if not options.hours then
rtn_minutes = rtn_minutes + rtn_hours*60
end
if not options.minutes then
rtn_seconds = rtn_seconds + rtn_minutes*60
end
-- Creates the null time format, does not work with long
if options.null and not options.long then
rtn_days='--'
rtn_hours='--'
rtn_minutes='--'
rtn_seconds='--'
end
-- Format options
local suffix = 'time-symbol-'
local suffix_2 = '-short'
if options.long then
suffix = ''
suffix_2 = ''
end
local div = options.string and ' ' or 'time-format.simple-format-tagged'
if options.time then
div = options.string and ':' or 'time-format.simple-format-div'
suffix = false
end
-- Adds formatting
if suffix ~= false then
if options.string then
-- format it as a string
local long = suffix == ''
rtn_days = long and rtn_days..' days' or rtn_days..'d'
rtn_hours = long and rtn_hours..' hours' or rtn_hours..'h'
rtn_minutes = long and rtn_minutes..' minutes' or rtn_minutes..'m'
rtn_seconds = long and rtn_seconds..' seconds' or rtn_seconds..'s'
else
rtn_days = {suffix..'days'..suffix_2, rtn_days}
rtn_hours = {suffix..'hours'..suffix_2, rtn_hours}
rtn_minutes = {suffix..'minutes'..suffix_2, rtn_minutes}
rtn_seconds = {suffix..'seconds'..suffix_2, rtn_seconds}
end
elseif not options.null then
-- weather string or not it has same format
rtn_days = string.format('%02d', rtn_days)
rtn_hours = string.format('%02d', rtn_hours)
rtn_minutes = string.format('%02d', rtn_minutes)
rtn_seconds = string.format('%02d', rtn_seconds)
end
-- The final return is construed
local rtn
local append = function(dom, value)
if dom and options.string then
rtn = rtn and rtn..div..value or value
elseif dom then
rtn = rtn and {div, rtn, value} or value
end
end
append(options.days, rtn_days)
append(options.hours, rtn_hours)
append(options.minutes, rtn_minutes)
append(options.seconds, rtn_seconds)
return rtn
end
--- Factorio.
-- @section factorio
--[[-- Copies items to the position and stores them in the closest entity of the type given
-- Copies the items by prototype name, but keeps them in the original inventory
@tparam table items items which are to be added to the chests, an array of LuaItemStack
@tparam[opt=navies] LuaSurface surface the surface that the items will be copied to
@tparam[opt={0, 0}] table position the position that the items will be copied to {x=100, y=100}
@tparam[opt=32] number radius the radius in which the items are allowed to be placed
@tparam[opt=iron-chest] string chest_type the chest type that the items should be copied into
@treturn LuaEntity the last chest that had items inserted into it
@usage-- Copy all the items in a players inventory and place them in chests at {0, 0}
copy_items_stack(game.player.get_main_inventory().get_contents())
]]
function Common.copy_items_stack(items, surface, position, radius, chest_type)
chest_type = chest_type or 'iron-chest'
surface = surface or game.surfaces[1]
if position and type(position) ~= 'table' then return end
if type(items) ~= 'table' then return end
-- Finds all entities of the given type
local p = position or {x=0, y=0}
local r = radius or 32
local entities = surface.find_entities_filtered{area={{p.x-r, p.y-r}, {p.x+r, p.y+r}}, name=chest_type} or {}
local count = #entities
local current = 1
-- Makes a new empty chest when it is needed
local function make_new_chest()
local pos = surface.find_non_colliding_position(chest_type, position, 32, 1)
local chest = surface.create_entity{name=chest_type, position=pos, force='neutral'}
table.insert(entities, chest)
count = count + 1
return chest
end
-- Function used to round robin the items into all chests
local function next_chest(item)
local chest = entities[current]
if count == 0 then return make_new_chest() end
if chest.get_inventory(defines.inventory.chest).can_insert(item) then
-- If the item can be inserted then the chest is returned
current = current+1
if current > count then current = 1 end
return chest
else
-- Other wise it is removed from the list
table.remove(entities, current)
count = count - 1
end
end
-- Inserts the items into the chests
local last_chest
for i=1,#items do
local item = items[i]
if item.valid_for_read then
local chest = next_chest(item)
if not chest or not chest.valid then return error(string.format('Cant move item %s to %s{%s, %s} no valid chest in radius', item.name, surface.name, p.x, p.y)) end
chest.insert(item)
last_chest = chest
end
end
return last_chest
end
--[[-- Moves items to the position and stores them in the closest entity of the type given
-- Differs from move_items by accepting a table of LuaItemStack and transferring them into the inventory - not copying
@tparam table items items which are to be added to the chests, an array of LuaItemStack
@tparam[opt=navies] LuaSurface surface the surface that the items will be moved to
@tparam[opt={0, 0}] table position the position that the items will be moved to {x=100, y=100}
@tparam[opt=32] number radius the radius in which the items are allowed to be placed
@tparam[opt=iron-chest] string chest_type the chest type that the items should be moved into
@treturn LuaEntity the last chest that had items inserted into it
@usage-- Copy all the items in a players inventory and place them in chests at {0, 0}
move_items_stack(game.player.get_main_inventory())
]]
function Common.move_items_stack(items, surface, position, radius, chest_type)
chest_type = chest_type or 'steel-chest'
surface = surface or game.surfaces[1]
if position and type(position) ~= 'table' then
return
end
if type(items) ~= 'table' then
return
end
-- Finds all entities of the given type
local p = position or {x=0, y=0}
local r = radius or 32
local entities = surface.find_entities_filtered{area={{p.x - r, p.y - r}, {p.x + r, p.y + r}}, name={chest_type, 'iron-chest'}} or {}
local count = #entities
local current = 0
local last_entity = nil
-- ipairs does not work on LuaInventory
for i = 1, #items do
local item = items[i]
if item.valid_for_read then
local inserted = false
-- Attempt to insert the items
for j = 1, count do
local entity = entities[((current + j - 1) % count) + 1]
if entity.can_insert(item) then
last_entity = entity
current = current + 1
entity.insert(item)
inserted = true
break
end
end
-- If it was not inserted then a new entity is needed
if not inserted then
--[[
if not options.allow_creation then
error('Unable to insert items into a valid entity, consider enabling allow_creation')
end
if options.name == nil then
error('Name must be provided to allow creation of new entities')
end
if options.position then
pos = surface.find_non_colliding_position(chest_type, p, r, 1, true)
elseif options.area then
pos = surface.find_non_colliding_position_in_box(chest_type, options.area, 1, true)
else
pos = surface.find_non_colliding_position(chest_type, {0,0}, 0, 1, true)
end
]]
local pos = surface.find_non_colliding_position(chest_type, p, r, 1, true)
last_entity = surface.create_entity{name=chest_type, position=pos, force='neutral'}
count = count + 1
entities[count] = last_entity
last_entity.insert(item)
end
end
end
--[[
-- Makes a new empty chest when it is needed
local function make_new_chest()
local pos = surface.find_non_colliding_position(chest_type, position, 32, 1)
local chest = surface.create_entity{name=chest_type, position=pos, force='neutral'}
table.insert(entities, chest)
count = count + 1
return chest
end
-- Function used to round robin the items into all chests
local function next_chest(item)
local chest = entities[current]
if count == 0 then
return make_new_chest()
end
if chest.get_inventory(defines.inventory.chest).can_insert(item) then
-- If the item can be inserted then the chest is returned
current = current + 1
if current > count then
current = 1
end
return chest
else
-- Other wise it is removed from the list
table.remove(entities, current)
count = count - 1
end
end
-- Inserts the items into the chests
local last_chest
for i=1,#items do
local item = items[i]
if item.valid_for_read then
local chest = next_chest(item)
if not chest or not chest.valid then
return error(string.format('Cant move item %s to %s{%s, %s} no valid chest in radius', item.name, surface.name, p.x, p.y))
end
local empty_stack = chest.get_inventory(defines.inventory.chest).find_empty_stack(item.name)
if not empty_stack then
return error(string.format('Cant move item %s to %s{%s, %s} no valid chest in radius', item.name, surface.name, p.x, p.y))
end
empty_stack.transfer_stack(item)
last_chest = chest
end
end
return last_chest
]]
return last_entity
end
--[[-- Prints a colored value on a location, color is based on the value.
nb: src is below but the gradent has been edited
https://github.com/Refactorio/RedMew/blob/9184b2940f311d8c9c891e83429fc57ec7e0c4a2/map_gen/maps/diggy/debug.lua#L31
@tparam number value the value to show must be between -1 and 1, scale can be used to achive this
@tparam LuaSurface surface the surface to palce the value on
@tparam table position {x, y} the possition to palce the value at
@tparam[opt=1] number scale how much to scale the colours by
@tparam[opt=0] number offset the offset in the +x +y direction
@tparam[opt=false] boolean immutable if immutable, only set, never do a surface lookup, values never change
@usage-- Place a 0 at {0, 0}
print_grid_value(0, game.player.surface, { x=0, y=0 })
]]
function Common.print_grid_value(value, surface, position, scale, offset, immutable)
local is_string = type(value) == 'string'
local color = Colours.white
local text = value
if type(immutable) ~= 'boolean' then
immutable = false
end
if not is_string then
scale = scale or 1
offset = offset or 0
position = {x = position.x + offset, y = position.y + offset}
local r = math.clamp(-value/scale, 0, 1)
local g = math.clamp(1-math.abs(value)/scale, 0, 1)
local b = math.clamp(value/scale, 0, 1)
color = { r = r, g = g, b = b}
-- round at precision of 2
text = math.floor(100 * value) * 0.01
if (0 == text) then
text = '0.00'
end
end
if not immutable then
local text_entity = surface.find_entity('flying-text', position)
if text_entity then
text_entity.text = text
text_entity.color = color
return
end
end
surface.create_entity{
name = 'flying-text',
color = color,
text = text,
position = position
}.active = false
end
--[[-- Clears all flying text entities on a surface
@tparam LuaSurface surface the surface to clear
@usage-- Remove all flying text on the surface
clear_flying_text(game.player.surface)
]]
function Common.clear_flying_text(surface)
local entities = surface.find_entities_filtered{name ='flying-text'}
for _, entity in pairs(entities) do
if entity and entity.valid then
entity.destroy()
end
end
end
return Common

View File

@@ -0,0 +1,864 @@
--[[-- Core Module - Datastore
- A module used to store data in the global table with the option to have it sync to an external source.
@core Datastore
@alias DatastoreManager
@usage-- Types of Datastore
-- This datastore will not save data externally and can be used to watch for updates on values within it
-- A common use might be to store data for a gui and only update the gui when a value changes
local LocalDatastore = Datastore.connect('LocalDatastore')
-- This datastore will allow you to use the save and request method, this allows you to have persistent data
-- Should be used over auto save as it creates less save requests, but this means you need to tell the data to be saved
-- We use this type for player data as we know the data only needs to be saved when the player leaves
local PersistentDatastore = Datastore.connect('PersistentDatastore', true) -- save_to_disk
-- This datastore is the same as above but the save method will be called automatically when ever you change a value
-- An auto save datastore should be used if the data does not change often, this can be global settings and things of that sort
-- If it is at all possible to setup events to unload and/or save the data then this is preferable
local AutosaveDatastore = Datastore.connect('AutosaveDatastore', true, true) -- save_to_disk, auto_save
-- Finally you can have a datastore that propagates its changes to all other connected servers, this means request does not need to be used
-- This should be used when you might have data conflicts while saving, this is done by pushing the saved value to all active servers
-- The request method has little use after server start as any external changes to the value will be pushed automatically
-- Auto save can also be used with this type and you should follow the same guidelines above for when this should be avoided
local PropagateDatastore = Datastore.connect('PropagateDatastore', true, false, true) -- save_to_disk, propagate_changes
@usage-- Using Datastores Locally
-- Once you have your datastore connection setup, any further requests with connect will return the same datastore
-- This is important to know because the settings passed as parameters you have an effect when it is first created
-- One useful thing that you might want to set up before runtime is a serializer, this will convert non string keys into strings
-- This serializer will allow use to pass a player object and still have it serialized to the players name
local ExampleData = Datastore.connect('ExampleData')
ExampleData:set_serializer(function(rawKey)
return rawKey.name
end)
-- If we want to get data from the datastore we can use get or get_all
local value = ExampleData:get(player, defaultValue)
local values = ExampleData:get_all()
-- If we want to set data then we can use set, increment, update, or update_all
ExampleData:set(player, 10)
ExampleData:increment(player)
ExampleData:update(player, function(player_name, value)
return value * 2
end)
ExampleData:update_all(function(player_name, value)
return value * 2
end)
-- If we want to remove data then we use remove
ExampleData:remove(player)
-- We can also listen for updates to a value done by any of the above methods with on_update
ExampleData:on_update(function(player_name, value)
game.print(player_name..' has had their example data updated to '..tostring(value))
end)
@usage-- Using Datastore Externally
-- If save_to_disk is used then this opens up the option for persistent data which you can request, save, and remove
-- All of the local methods are still usable put now there is the option for extra events
-- In order for this to work there must be an external script to read datastore.pipe and inject with Datastore.ingest
-- To request data you would use request and the on_load event, this event can be used to modify data before it is used
ExampleData:request(player)
ExampleData:on_load(function(player_name, value)
game.print('Loaded example data for '..player_name)
-- A value can be returned here to overwrite the received value
end)
-- To save data you would use save and the on_save event, this event can be used to modify data before it is saved
ExampleData:save(player)
ExampleData:on_save(function(player_name, value)
game.print('Saved example data for '..player_name)
-- A value can be returned here to overwrite the value which is saved
end)
-- To remove data locally but not externally, like if a player logs off, you would use unload and on_unload
ExampleData:unload(player)
ExampleData:on_unload(function(player_name, value)
game.print('Unloaded example data for '..player_name)
-- Any return is ignored, this is event is for cleaning up other data
end)
@usage-- Using Datastore Messaging
-- The message action can be used regardless of save_to_disk being set as no data is saved, but an external script is still required
-- These messages can be used to send data to other servers which doesnt need to be saved such as shouts or commands
-- Using messages is quite simple only using message and on_message
ExampleData:message(key, message)
ExampleData:on_message(function(key, message)
game.print('Received message '..message)
end)
@usage-- Combined Datastores
-- A combined datastore is a datastore which stores its data inside of another datastore
-- This means that the data is stored more efficiently in the external database and less requests need to be made
-- To understand how combined datastores work think of each key in the parent as a table where the sub datastore is a key in that table
-- Player data is the most used version of the combined datastore, below is how the player data module is setup
local PlayerData = Datastore.connect('PlayerData', true) -- saveToDisk
PlayerData:set_serializer(Datastore.name_serializer) -- use player name as key
PlayerData:combine('Statistics')
PlayerData:combine('Settings')
PlayerData:combine('Required')
-- You can then further combine datastores to any depth, below we add some possible settings and statistics that we might use
-- Although we dont in this example, each of these functions returns the datastore object which you should use as a local value
PlayerData.Settings:combine('Color')
PlayerData.Settings:combine('Quickbar')
PlayerData.Settings:combine('JoinMessage')
PlayerData.Statistics:combine('Playtime')
PlayerData.Statistics:combine('JoinCount')
-- Because sub datastore work just like a normal datastore you dont need any special code, using get and set will still return as if it wasnt a sub datastore
-- Things like the serializer and the datastore settings are always the same as the parent so you dont need to worry about setting up the serializer each time
-- And because save, request, and unload methods all point to the root datastore you are able to request and save your data as normal
-- If you used get_all on PlayerData this is what you would get:
{
Cooldude2606 = {
Settings = {
Color = 'ColorValue',
Quickbar = 'QuickbarValue',
JoinMessage = 'JoinMessageValue'
},
Statistics = {
Playtime = 'PlaytimeValue',
JoinCount = 'JoinCountValue'
}
}
}
-- If you used get_all on PlayerData.Settings this is what you would get:
{
Cooldude2606 = {
Color = 'ColorValue',
Quickbar = 'QuickbarValue',
JoinMessage = 'JoinMessageValue'
}
}
-- If you used get_all on PlayerData.Settings.Color this is what you would get:
{
Cooldude2606 = 'ColorValue'
}
]]
local Event = require 'utils.event' --- @dep utils.event
local DatastoreManager = {}
local Datastores = {}
local Datastore = {}
local Data = {}
local copy = table.deep_copy
local trace = debug.traceback
--- Save datastores in the global table
global.datastores = Data
Event.on_load(function()
Data = global.datastores
for datastoreName, datastore in pairs(Datastores) do
datastore.data = Data[datastoreName]
end
end)
----- Datastore Manager
-- @section datastoreManager
--- Metatable used on datastores
DatastoreManager.metatable = {
__index = function(self, key) return rawget(self.children, key) or rawget(Datastore, key) end,
__newidnex = function(_, _, _) error('Datastore can not be modified', 2) end,
__call = function(self, ...) return self:get(...) end
}
--[[-- Make a new datastore connection, if a connection already exists then it is returned
@tparam string datastoreName The name that you want the new datastore to have, this can not have any whitespace
@tparam[opt=false] boolean saveToDisk When set to true, using the save method with write the data to datastore.pipe
@tparam[opt=false] boolean autoSave When set to true, using any method which modifies data will cause the data to be saved
@tparam[opt=false] boolean propagateChanges When set to true, using the save method will send the data to all other connected servers
@treturn table The new datastore connection that can be used to access and modify data in the datastore
@usage-- Connecting to the test datastore which will allow saving to disk
local ExampleData = Datastore.connect('ExampleData', true) -- saveToDisk
]]
function DatastoreManager.connect(datastoreName, saveToDisk, autoSave, propagateChanges)
if Datastores[datastoreName] then return Datastores[datastoreName] end
if _LIFECYCLE ~= _STAGE.control then
-- Only allow this function to be called during the control stage
error('New datastore connection can not be created during runtime', 2)
end
local new_datastore = {
name = datastoreName,
value_name = datastoreName,
auto_save = autoSave or false,
save_to_disk = saveToDisk or false,
propagate_changes = propagateChanges or false,
serializer = false,
parent = false,
children = {},
metadata = {},
events = {},
data = {}
}
Data[datastoreName] = new_datastore.data
Datastores[datastoreName] = new_datastore
return setmetatable(new_datastore, DatastoreManager.metatable)
end
--[[-- Make a new datastore that stores its data inside of another one
@tparam string datastoreName The name of the datastore that will contain the data for the new datastore
@tparam string subDatastoreName The name of the new datastore, this name will also be used as the key inside the parent datastore
@treturn table The new datastore connection that can be used to access and modify data in the datastore
@usage-- Setting up a datastore which stores its data inside of another datastore
local BarData = Datastore.combine('ExampleData', 'Bar')
]]
function DatastoreManager.combine(datastoreName, subDatastoreName)
local datastore = assert(Datastores[datastoreName], 'Datastore not found '..tostring(datastoreName))
return datastore:combine(subDatastoreName)
end
--[[-- Ingest the result from a request, this is used through a rcon interface to sync data
@tparam string action The action that should be done, can be: remove, message, propagate, or request
@tparam string datastoreName The name of the datastore that should have the action done to it
@tparam string key The key of that datastore that is having the action done to it
@tparam string valueJson The json string for the value being ingested, remove does not require a value
@usage-- Replying to a data request
Datastore.ingest('request', 'ExampleData', 'TestKey', 'Foo')
]]
function DatastoreManager.ingest(action, datastoreName, key, valueJson)
local datastore = assert(Datastores[datastoreName], 'Datastore ingest error, Datastore not found '..tostring(datastoreName))
assert(type(action) == 'string', 'Datastore ingest error, Action is not a string got: '..type(action))
assert(type(key) == 'string', 'Datastore ingest error, Key is not a string got: '..type(key))
if action == 'remove' then
datastore:raw_set(key)
elseif action == 'message' then
local success, value = pcall(game.json_to_table, valueJson)
if not success or value == nil then value = tonumber(valueJson) or valueJson end
datastore:raise_event('on_message', key, value)
elseif action == 'propagate' or action == 'request' then
local success, value = pcall(game.json_to_table, valueJson)
if not success or value == nil then value = tonumber(valueJson) or valueJson end
local old_value = datastore:raw_get(key)
value = datastore:raise_event('on_load', key, value, old_value)
datastore:set(key, value)
end
end
--[[-- Debug, Use to get all datastores, or return debug info on a datastore
@tparam[opt] string datastoreName The name of the datastore to get the debug info of
@usage-- Get all the datastores
local datastores = Datastore.debug()
@usage-- Getting the debug info for a datastore
local debug_info = Datastore.debug('ExampleData')
]]
function DatastoreManager.debug(datastoreName)
if not datastoreName then return Datastores end
local datastore = assert(Datastores[datastoreName], 'Datastore not found '..tostring(datastoreName))
return datastore:debug()
end
--[[-- Commonly used serializer, returns the name of the object
@tparam any rawKey The raw key that will be serialized, this can be things like player, force, surface, etc
@treturn string The name of the object that was passed
@usage-- Using the name serializer for your datastore
local ExampleData = Datastore.connect('ExampleData')
ExampleData:set_serializer(Datastore.name_serializer)
]]
function DatastoreManager.name_serializer(rawKey)
return rawKey.name
end
----- Datastore Internal
-- @section datastore-internal
--[[-- Debug, Get the debug info for this datastore
@treturn table The debug info for this datastore, contains stuff like parent, settings, children, etc
@usage-- Get the debug info for a datastore
local ExampleData = Datastore.connect('ExampleData')
local debug_info = ExampleData:debug()
]]
function Datastore:debug()
local debug_info = {}
if self.parent then
debug_info.parent = self.parent.name
else
debug_info.settings = { auto_save = self.auto_save, save_to_disk = self.save_to_disk, propagate_changes = self.propagate_changes, serializer = not not self.serializer }
end
local children = {}
for name in pairs(self.children) do children[#children+1] = name end
if #children > 0 then debug_info.children = children end
local events = {}
for name, handlers in pairs(self.events) do events[name] = #handlers end
if next(events) then debug_info.events = events end
if next(self.metadata) then debug_info.metadata = self.metadata end
debug_info.data = self:get_all()
return debug_info
end
--[[-- Internal, Get data following combine logic
@tparam string key The key to get the value of from this datastore
@tparam[opt=false] boolean fromChild If the get request came from a child of this datastore
@treturn any The value that was stored at this key in this datastore
@usage-- Internal, Get the data from a datastore
local value = self:raw_get('TestKey')
]]
function Datastore:raw_get(key, fromChild)
local data = self.data
if self.parent then
data = self.parent:raw_get(key, true)
key = self.value_name
end
local value = data[key]
if value ~= nil then return value end
if fromChild then value = {} end
data[key] = value
return value
end
--[[-- Internal, Set data following combine logic
@tparam string key The key to set the value of in this datastore
@tparam any value The value that will be set at this key
@usage-- Internal, Set the value in a datastore
self:raw_set('TestKey', 'Foo')
]]
function Datastore:raw_set(key, value)
if self.parent then
local data = self.parent:raw_get(key, true)
data[self.value_name] = value
else
self.data[key] = value
end
end
local function serialize_error(err) error('An error ocurred in a datastore serializer: '..trace(err)) end
--[[-- Internal, Return the serialized key
@tparam any rawKey The key that needs to be serialized, if it is already a string then it is returned
@treturn string The key after it has been serialized
@usage-- Internal, Ensure that the key is a string
key = self:serialize(key)
]]
function Datastore:serialize(rawKey)
if type(rawKey) == 'string' then return rawKey end
assert(self.serializer, 'Datastore does not have a serializer and received non string key')
local success, key = xpcall(self.serializer, serialize_error, rawKey)
return success and key or nil
end
--[[-- Internal, Writes an event to the output file to be saved and/or propagated
@tparam string action The action that should be wrote to datastore.pipe, can be request, remove, message, save, propagate
@tparam string key The key that the action is being preformed on
@tparam any value The value that should be used with the action
@usage-- Write a data request to datastore.pipe
self:write_action('request', 'TestKey')
@usage-- Write a data save to datastore.pipe
self:write_action('save', 'TestKey', 'Foo')
]]
function Datastore:write_action(action, key, value)
local data = {action, self.name, key}
if value ~= nil then
data[4] = type(value) == 'table' and game.table_to_json(value) or value
end
game.write_file('ext/datastore.out', table.concat(data, ' ')..'\n', true, 0)
end
----- Datastore Local
-- @section datastore-local
--[[-- Create a new datastore which is stores its data inside of this datastore
@tparam string subDatastoreName The name of the datastore that will have its data stored in this datastore
@treturn table The new datastore that was created inside of this datastore
@usage-- Add a new sub datastore
local ExampleData = Datastore.connect('ExampleData')
local BarData = ExampleData:combine('Bar')
]]
function Datastore:combine(subDatastoreName)
local new_datastore = DatastoreManager.connect(self.name..'.'..subDatastoreName)
self.children[subDatastoreName] = new_datastore
new_datastore.value_name = subDatastoreName
new_datastore.serializer = self.serializer
new_datastore.auto_save = self.auto_save
new_datastore.parent = self
Data[new_datastore.name] = nil
new_datastore.data = nil
return new_datastore
end
--[[-- Set a callback that will be used to serialize keys which aren't strings
@tparam function callback The function that will be used to serialize non string keys passed as an argument
@usage-- Set a custom serializer, this would be the same as Datastore.name_serializer
local ExampleData = Datastore.connect('ExampleData')
ExampleData:set_serializer(function(rawKey)
return rawKey.name
end)
]]
function Datastore:set_serializer(callback)
assert(type(callback) == 'function', 'Callback must be a function')
self.serializer = callback
end
--[[-- Set a default value to be returned by get if no other default is given, using will mean get will never return nil, set using the default will set to nil to save space
@tparam any value The value that will be deep copied by get if the value is nil and no other default is given
@tparam boolean allowSet When true if the default is passed as the value for set it will be set rather than setting nil
@usage-- Set a default value to be returned by get
local ExampleData = Datastore.connect('ExampleData')
ExampleData:set_default('Foo')
]]
function Datastore:set_default(value, allowSet)
self.default = value
self.allow_set_to_default = allowSet
end
--[[-- Set metadata tags on this datastore which can be accessed by other scripts
@tparam table tags A table of tags that you want to set in the metadata for this datastore
@usage-- Adding metadata that could be used by a gui to help understand the stored data
local ExampleData = Datastore.connect('ExampleData')
ExampleData:set_metadata{
caption = 'Test Data',
tooltip = 'Data used for testing datastores',
type = 'table'
}
]]
function Datastore:set_metadata(tags)
local metadata = self.metadata
for key, value in pairs(tags) do
metadata[key] = value
end
end
--[[-- Get a value from local storage, option to have a default value, do not edit the data returned as changes may not save, use update if you want to make changes
@tparam any key The key that you want to get the value of, must be a string unless a serializer is set
@tparam[opt] any default The default value that will be returned if no value is found in the datastore
@usage-- Get a key from the datastore, the default will be deep copied if no value exists in the datastore
local ExampleData = Datastore.connect('ExampleData')
local value = ExampleData:get('TestKey')
]]
function Datastore:get(key, default)
key = self:serialize(key)
local value = self:raw_get(key)
if value ~= nil then return value end
return copy(default or self.default)
end
--[[-- Set a value in local storage, will trigger on_update then on_save, save_to_disk and auto_save is required for on_save
@tparam any key The key that you want to set the value of, must be a string unless a serializer is set
@tparam any value The value that you want to set for this key
@usage-- Set a value in the datastore, this will trigger on_update, if auto_save is true then will trigger save
local ExampleData = Datastore.connect('ExampleData')
ExampleData:set('TestKey', 'Foo')
]]
function Datastore:set(key, value)
key = self:serialize(key)
local old_value = self:raw_get(key)
if value == self.default and not self.allow_set_to_default then
self:raw_set(key)
else
self:raw_set(key, value)
end
self:raise_event('on_update', key, value, old_value)
if self.auto_save then self:save(key) end
return value
end
--[[-- Increment the value in local storage, only works for number values, will trigger on_update then on_save, save_to_disk and auto_save is required for on_save
@tparam any key The key that you want to increment the value of, must be a string unless a serializer is set
@tparam[opt=1] number delta The amount that you want to increment the value by, can be negative or a decimal
@usage-- Increment a value in a datastore, the value must be a number or nil, if nil 0 is used as the start value
local ExampleData = Datastore.connect('ExampleData')
ExampleData:increment('TestNumber')
]]
function Datastore:increment(key, delta)
key = self:serialize(key)
local value = self:raw_get(key) or 0
return self:set(key, value + (delta or 1))
end
local function update_error(err) log('An error occurred in datastore update:\n\t'..trace(err)) end
--[[-- Use a function to update the value locally, will trigger on_update then on_save, save_to_disk and auto_save is required for on_save
@tparam any key The key that you want to apply the update to, must be a string unless a serializer is set
@tparam function callback The function that will be used to update the value at this key
@usage-- Using a function to update a value, if a value is returned then this will be the new value
local ExampleData = Datastore.connect('ExampleData')
ExampleData:increment('TestKey', function(key, value)
return value..value
end)
]]
function Datastore:update(key, callback)
key = self:serialize(key)
local value = self:get(key)
local raw_value = self:raw_get(key)
local old_value = copy(self:raw_get(key))
local success, new_value = xpcall(callback, update_error, key, value)
if not success then
self:raw_set(key, old_value)
elseif new_value ~= nil then
self:set(key, new_value)
elseif raw_value == nil then
self:set(key, value)
else
self:raise_event('on_update', key, value, old_value)
if self.auto_save then self:save(key) end
end
end
--[[-- Remove a value locally and on the external source, works regardless of propagateChanges, requires save_to_disk for external changes
@tparam any key The key that you want to remove locally and externally, must be a string unless a serializer is set
@usage-- Remove a key locally and externally
local ExampleData = Datastore.connect('ExampleData')
ExampleData:remove('TestKey')
]]
function Datastore:remove(key)
key = self:serialize(key)
local old_value = self:raw_get(key)
self:raw_set(key)
self:raise_event('on_update', key, nil, old_value)
if self.save_to_disk then self:write_action('remove', key) end
if self.parent and self.parent.auto_save then return self.parent:save(key) end
end
local function filter_error(err) log('An error ocurred in a datastore filter:\n\t'..trace(err)) end
--[[-- Internal, Used to filter elements from a table
@tparam table tbl The table that will have the filter applied to it
@tparam[opt] function callback The function that will be used as a filter, if none giving then the provided table is returned
@treturn table The table which has only the key values pairs which passed the filter
@usage-- Internal, Filter a table by the values it contains, return true to keep the key value pair
local filtered_table = filter({5,3,4,1,2}, function(key, value)
return value > 2
end)
]]
local function filter(tbl, callback)
if not callback then return tbl end
local rtn = {}
for key, value in pairs(tbl) do
local success, add = xpcall(callback, filter_error, key, value)
if success and add then rtn[key] = value end
end
return rtn
end
--[[-- Get all keys in this datastore, optional filter callback
@tparam[opt] function callback The filter function that can be used to filter the results returned
@treturn table All the data that is in this datastore, filtered if a filter was provided
@usage-- Get all the data in this datastore
local ExampleData = Datastore.connect('ExampleData')
local data = ExampleData:get_all()
@usage-- Get all the data in this datastore, with a filter
local ExampleData = Datastore.connect('ExampleData')
local data = ExampleData:get_all(function(key, value)
return type(value) == 'string'
end)
]]
function Datastore:get_all(callback)
if not self.parent then
return filter(self.data, callback)
else
local data, value_name = {}, self.value_name
for key, value in pairs(self.parent:get_all()) do
data[key] = value[value_name]
end
return filter(data, callback)
end
end
--[[-- Update all keys in this datastore using the same update function
@tparam function callback The update function that will be applied to each key
@usage-- Get all the data in this datastore, with a filter
local ExampleData = Datastore.connect('ExampleData')
ExampleData:update_all(function(key, value)
return value..value
end)
]]
function Datastore:update_all(callback)
local data = self:get_all()
for key, value in pairs(data) do
local old_value = copy(value)
local success, new_value = xpcall(callback, update_error, key, value)
if success and new_value ~= nil then
self:set(key, new_value)
else
self:raise_event('on_update', key, value, old_value)
if self.auto_save then self:save(key) end
end
end
end
----- Datastore External
-- @section datastore-external
--[[-- Request a value from an external source, will trigger on_load when data is received
@tparam any key The key that you want to request from an external source, must be a string unless a serializer is set
@usage-- Request a key from an external source, on_load is triggered when data is received
local ExampleData = Datastore.connect('ExampleData')
ExampleData:request('TestKey')
]]
function Datastore:request(key)
if self.parent then return self.parent:request(key) end
key = self:serialize(key)
self:write_action('request', key)
end
--[[-- Save a value to an external source, will trigger on_save before data is saved, save_to_disk must be set to true
@tparam any key The key that you want to save to an external source, must be a string unless a serializer is set
@usage-- Save a key to an external source, save_to_disk must be set to true for there to be any effect
local ExampleData = Datastore.connect('ExampleData')
ExampleData:save('TestKey')
]]
function Datastore:save(key)
if self.parent then self.parent:save(key) end
if not self.save_to_disk then return end
key = self:serialize(key)
local value = self:raise_event('on_save', key, copy(self:raw_get(key)))
local action = self.propagate_changes and 'propagate' or 'save'
self:write_action(action, key, value)
end
--[[-- Save a value to an external source and remove locally, will trigger on_unload then on_save, save_to_disk is not required for on_unload
@tparam any key The key that you want to unload from the datastore, must be a string unless a serializer is set
@usage-- Unload a key from the datastore, get will now return nil and value will be saved externally if save_to_disk is set to true
local ExampleData = Datastore.connect('ExampleData')
ExampleData:unload('TestKey')
]]
function Datastore:unload(key)
if self.parent then return self.parent:unload(key) end
key = self:serialize(key)
self:raise_event('on_unload', key, copy(self:raw_get(key)))
self:save(key)
self:raw_set(key)
end
--[[-- Use to send a message over the connection, works regardless of saveToDisk and propagateChanges
@tparam any key The key that you want to send a message over, must be a string unless a serializer is set
@tparam any message The message that you want to send to other connected servers, or external source
@usage-- Send a message to other servers on this key, can listen for messages with on_message
local ExampleData = Datastore.connect('ExampleData')
ExampleData:message('TestKey', 'Foo')
]]
function Datastore:message(key, message)
key = self:serialize(key)
self:write_action('message', key, message)
end
--[[-- Save all the keys in the datastore, optional filter callback
@tparam[opt] function callback The filter function that can be used to filter the keys saved
@usage-- Save all the data in this datastore
local ExampleData = Datastore.connect('ExampleData')
local data = ExampleData:save_all()
@usage-- Save all the data in this datastore, with a filter
local ExampleData = Datastore.connect('ExampleData')
ExampleData:save_all(function(key, value)
return type(value) == 'string'
end)
]]
function Datastore:save_all(callback)
local data = self:get_all(callback)
for key in pairs(data) do self:save(key) end
end
--[[-- Unload all the keys in the datastore, optional filter callback
@tparam[opt] function callback The filter function that can be used to filter the keys unloaded
@usage-- Unload all the data in this datastore
local ExampleData = Datastore.connect('ExampleData')
ExampleData:unload_all()
@usage-- Unload all the data in this datastore, with a filter
local ExampleData = Datastore.connect('ExampleData')
ExampleData:unload_all(function(key, value)
return type(value) == 'string'
end)
]]
function Datastore:unload_all(callback)
local data = self:get_all(callback)
for key in pairs(data) do self:unload(key) end
end
----- Events
-- @section events
local function event_error(err) log('An error ocurred in a datastore event handler:\n\t'..trace(err)) end
--[[-- Internal, Raise an event on this datastore
@tparam string event_name The name of the event to raise for this datastore
@tparam string key The key that this event is being raised for
@tparam[opt] any value The current value that this key has, might be a deep copy of the value
@tparam[opt] any old_value The previous value that this key has, might be a deep copy of the value
@tparam[opt] string source Where this call came from, used to do event recursion so can be parent or child
@treturn any The value that is left after being passed through all the event handlers
@usage-- Internal, Getting the value that should be saved
value = self:raise_event('on_save', key, value)
]]
function Datastore:raise_event(event_name, key, value, old_value, source)
-- Raise the event for the children of this datastore
if source ~= 'child' and next(self.children) then
if type(value) ~= 'table' then value = {} end
for value_name, child in pairs(self.children) do
local old_child_value = old_value and old_value[value_name] or nil
value[value_name] = child:raise_event(event_name, key, value[value_name], old_child_value, 'parent')
end
end
-- Raise the event for this datastore
local handlers = self.events[event_name]
if handlers then
for _, handler in ipairs(handlers) do
local success, new_value = xpcall(handler, event_error, key, value, old_value)
if success and new_value ~= nil then value = new_value end
end
end
-- Raise the event for the parent of this datastore
if source ~= 'parent' and self.parent then
local parent_value = self.parent:raw_get(key, true)
self.parent:raise_event(event_name, key, parent_value, parent_value, 'child')
end
-- If this is the save event and the table is empty then return nil
if event_name == 'on_save' and next(self.children) and not next(value) then return end
return value
end
--[[-- Internal, Returns a function which will add a callback to an event
@tparam string event_name The name of the event that this should create a handler adder for
@treturn function The function that can be used to add handlers to this event
@usage-- Internal, Get the function to add handlers to on_load
Datastore.on_load = event_factory('on_load')
]]
local function event_factory(event_name)
return function(self, callback)
assert(type(callback) == 'function', 'Handler must be a function')
local handlers = self.events[event_name]
if not handlers then
self.events[event_name] = { callback }
else
handlers[#handlers+1] = callback
end
end
end
--[[-- Register a callback that triggers when data is loaded from an external source, returned value is saved locally
@tparam function callback The handler that will be registered to the on_load event
@usage-- Adding a handler to on_load, returned value will be saved locally, can be used to deserialize the value beyond a normal json
local ExampleData = Datastore.connect('ExampleData')
ExampleData:on_load(function(key, value)
game.print('Test data loaded for: '..key)
end)
]]
Datastore.on_load = event_factory('on_load')
--[[-- Register a callback that triggers before data is saved, returned value is saved externally
@tparam function callback The handler that will be registered to the on_load event
@usage-- Adding a handler to on_save, returned value will be saved externally, can be used to serialize the value beyond a normal json
local ExampleData = Datastore.connect('ExampleData')
ExampleData:on_save(function(key, value)
game.print('Test data saved for: '..key)
end)
]]
Datastore.on_save = event_factory('on_save')
--[[-- Register a callback that triggers before data is unloaded, returned value is ignored
@tparam function callback The handler that will be registered to the on_load event
@usage-- Adding a handler to on_unload, returned value is ignored, can be used to clean up guis or local values related to this data
local ExampleData = Datastore.connect('ExampleData')
ExampleData:on_load(function(key, value)
game.print('Test data unloaded for: '..key)
end)
]]
Datastore.on_unload = event_factory('on_unload')
--[[-- Register a callback that triggers when a message is received, returned value is ignored
@tparam function callback The handler that will be registered to the on_load event
@usage-- Adding a handler to on_message, returned value is ignored, can be used to receive messages from other connected servers without saving data
local ExampleData = Datastore.connect('ExampleData')
ExampleData:on_message(function(key, value)
game.print('Test data message for: '..key)
end)
]]
Datastore.on_message = event_factory('on_message')
--[[-- Register a callback that triggers any time a value is changed, returned value is ignored
@tparam function callback The handler that will be registered to the on_load event
@usage-- Adding a handler to on_update, returned value is ignored, can be used to update guis or send messages when data is changed
local ExampleData = Datastore.connect('ExampleData')
ExampleData:on_update(function(key, value)
game.print('Test data updated for: '..key)
end)
]]
Datastore.on_update = event_factory('on_update')
----- Module Return
return DatastoreManager

View File

@@ -0,0 +1,153 @@
--[[-- Core Module - External
- A module used to make accessing externally set data easier.
@core External
@alias External
@usage-- Printing all server to chat
local External = require 'expcore.external' --- @dep expcore.external
local message = 'id: %s name: %s version: %s status: %s'
for server_id, server in pairs(External.get_servers()) do
local status = External.get_server_status(server_id)
game.print(message:format(server_id, server.name, server.version, status))
end
]]
local ext, var
local concat = table.concat
local External = {}
--[[-- Checks that local links are valid, will try to add the links if invalid
@treturn boolean If the external data is valid, if false you should not call any other methods from External
@usage-- Check that external data is valid
if not External.valid() then
-- error code here
end
]]
function External.valid()
if global.ext == nil then return false end
if ext == global.ext and var == ext.var then
return var ~= nil
else
ext = global.ext
var = ext.var
return var ~= nil
end
end
--[[-- Gets a table of all the servers, key is the server id, value is the server details
@treturn table A table containing all the servers, key is the server id, value is the server details
@usage-- Get all servers
local servers = External.get_servers()
]]
function External.get_servers()
assert(ext, 'No external data was found, use External.valid() to ensure external data exists.')
return assert(ext.servers, 'No server list was found, please ensure that the external service is running')
end
--[[-- Gets a table of all the servers filtered by name, key is the server id, value is the server details
@tparam string search The string to search for, names, short_names and ids are checked for this string.
@treturn table A table containing all the servers filtered by name, key is the server id, value is the server details
@usage-- Get all servers with public in the name
local servers = External.get_servers_filtered(public)
]]
function External.get_servers_filtered(search)
assert(ext, 'No external data was found, use External.valid() to ensure external data exists.')
local servers = assert(ext.servers, 'No server list was found, please ensure that the external service is running')
local found_servers = {}
search = search:lower()
for server_id, server in pairs(servers) do
local str = concat{server.name, server.short_name, server.id}
if str:lower():find(search, 1, true) then found_servers[server_id] = server end
end
return found_servers
end
--[[-- Gets the details of the current server
@treturn table The details of the current server
@usage-- Get the details of the current server
local server = External.get_current_server()
]]
function External.get_current_server()
assert(ext, 'No external data was found, use External.valid() to ensure external data exists.')
local servers = assert(ext.servers, 'No server list was found, please ensure that the external service is running')
local server_id = assert(ext.current, 'No current id was found, please ensure that the external service is running')
return servers[server_id]
end
--[[-- Gets the details of the given server
@tparam string server_id The internal server if for the server you want the details of
@treturn table The details of the given server
@usage-- Get the details of the given server
local server = External.get_server_details('eu-01')
]]
function External.get_server_details(server_id)
assert(ext, 'No external data was found, use External.valid() to ensure external data exists.')
local servers = assert(ext.servers, 'No server list was found, please ensure that the external service is running')
return servers[server_id]
end
--[[-- Gets the status of the given server
@tparam string server_id The internal server if for the server you want the status of
@tparam boolean raw When true Current will not be returned as status but rather the raw status for the server
@treturn string The status of the given server, one of: Online, Modded, Protected, Current, Offline
@usage-- Get the status of the given server
local status = External.get_server_status('eu-01')
]]
function External.get_server_status(server_id, raw)
assert(var, 'No external data was found, use External.valid() to ensure external data exists.')
local servers = assert(var.status, 'No server status was found, please ensure that the external service is running')
local current = assert(ext.current, 'No current id was found, please ensure that the external service is running')
return not raw and server_id == current and 'Current' or servers[server_id]
end
--[[-- Gets the ups of the current server
@usage-- Get the ups of the current server
local server_ups = External.get_server_ups()
]]
function External.get_server_ups()
assert(var, 'No external data was found, use External.valid() to ensure external data exists.')
return assert(var.server_ups, 'No server ups was found, please ensure that the external service is running')
end
--[[-- Connect a player to the given server
@tparam LuaPlayer player The player that you want to request to join a different server
@tparam string server_id The internal id of the server to connect to, can also be any address but this will show Unknown Server
@tparam[opt=false] boolean self_requested If the player requested the join them selfs, this will hide the message about being asked to switch
@usage-- Request that a player joins a different server
External.request_connection(player, 'eu-01')
@usage-- Request that a player joins a different server, by own request
External.request_connection(player, 'eu-01', true)
]]
function External.request_connection(player, server_id, self_requested)
local server = { address = server_id, name = 'Unknown Server', description = 'This server is not ran by us, please check the address of the server.' }
if ext and ext.servers and ext.servers[server_id] then server = ext.servers[server_id] end
local message = 'Please press the connect button below to join.'
if not self_requested then message = 'You have been asked to switch to a different server.\n'..message end
player.connect_to_server{
address = server.address,
name = '\n[color=orange][font=heading-1]'..server.name..'[/font][/color]\n',
description = server.description..'\n'..message
}
end
--- Module return
return External

View File

@@ -0,0 +1 @@
return require 'expcore.gui._require'

View File

@@ -0,0 +1,144 @@
--[[-- Core Module - Gui
- Used to simplify gui creation using factory functions called element defines
@core Gui
@alias Gui
@usage-- To draw your element you only need to call the factory function
-- You are able to pass any other arguments that are used in your custom functions but the first is always the parent element
local example_button_element = example_button(parent_element)
@usage-- Making a factory function for a button with the caption "Example Button"
-- This method has all the same features as LuaGuiElement.add
local example_button =
Gui.element{
type = 'button',
caption = 'Example Button'
}
@usage-- Making a factory function for a button which is contained within a flow
-- This method is for when you still want to register event handlers but cant use the table method
local example_flow_with_button =
Gui.element(function(definition, parent, ...)
-- ... shows that all other arguments from the factory call are passed to this function
-- Here we are adding a flow which we will then later add a button to
local flow =
parent.add{ -- paraent is the element which is passed to the factory function
name = 'example_flow',
type = 'flow'
}
-- Now we add the button to the flow that we created earlier
local element = definition:triggers_event(
flow.add{
type = 'button',
caption = 'Example Button'
}
)
-- You must return a new element, this is so styles can be applied and returned to the caller
-- You may return any of your elements that you added, consider the context in which it will be used for which should be returned
return element
end)
@usage-- Styles can be added to any element define, simplest way mimics LuaGuiElement.style[key] = value
local example_button =
Gui.element{
type = 'button',
caption = 'Example Button',
style = 'forward_button' -- factorio styles can be applied here
}
:style{
height = 25, -- same as element.style.height = 25
width = 100 -- same as element.style.width = 25
}
@usage-- Styles can also have a custom function when the style is dynamic and depends on other factors
-- Use this method if your style is dynamic and depends on other factors
local example_button =
Gui.element{
type = 'button',
caption = 'Example Button',
style = 'forward_button' -- factorio styles can be applied here
}
:style(function(style, element, ...)
-- style is the current style object for the elemenent
-- element is the element that is being changed
-- ... shows that all other arguments from the factory call are passed to this function
local player = game.players[element.player_index]
style.height = 25
style.width = 100
style.font_color = player.color
end)
@usage-- You are able to register event handlers to your elements, these can be factorio events or custom ones
-- All events are checked to be valid before raising any handlers, this means element.valid = true and player.valid = true
Gui.element{
type = 'button',
caption = 'Example Button'
}
:on_click(function(player, element, event)
-- player is the player who interacted with the element to cause the event
-- element is a refrence to the element which caused the event
-- event is a raw refrence to the event data if player and element are not enough
player.print('Clicked: '..element.name)
end)
@usage-- Example from core_defines, Gui.core_defines.hide_left_flow, called like: hide_left_flow(parent_element)
--- Button which hides the elements in the left flow, shows inside the left flow when frames are visible
-- @element hide_left_flow
local hide_left_flow =
Gui.element{
type = 'sprite-button',
sprite = 'utility/close_black',
style = 'tool_button',
tooltip = {'expcore-gui.left-button-tooltip'}
}
:style{
padding = -3,
width = 18,
height = 20
}
:on_click(function(player, _,_)
Gui.hide_left_flow(player)
end)
@usage-- Eample from defines, Gui.alignment, called like: Gui.alignment(parent, name, horizontal_align, vertical_align)
-- Notice how _ are used to blank arguments that are not needed in that context and how they line up with above
Gui.alignment =
Gui.element(function(_, parent, name, _,_)
return parent.add{
name = name or 'alignment',
type = 'flow',
}
end)
:style(function(style, _,_, horizontal_align, vertical_align)
style.padding = {1, 2}
style.vertical_align = vertical_align or 'center'
style.horizontal_align = horizontal_align or 'right'
style.vertically_stretchable = style.vertical_align ~= 'center'
style.horizontally_stretchable = style.horizontal_align ~= 'center'
end)
]]
local Gui = require 'expcore.gui.prototype'
require 'expcore.gui.helper_functions'
require 'expcore.gui.core_defines'
require 'expcore.gui.top_flow'
require 'expcore.gui.left_flow'
require 'expcore.gui.defines'
local Roles = _C.opt_require('expcore.roles')
local Event = _C.opt_require('utils.event')
if Roles and Event then
Event.add(Roles.events.on_role_assigned, function(e)
Gui.update_top_flow(game.get_player(e.player_index))
end)
Event.add(Roles.events.on_role_unassigned, function(e)
Gui.update_top_flow(game.get_player(e.player_index))
end)
end
return Gui

View File

@@ -0,0 +1,89 @@
--[[-- Core Module - Gui
- Gui defines that are used internally by the gui system
@module Gui
]]
local Gui = require 'expcore.gui.prototype'
local Event = require 'utils.event'
--- Core Defines.
-- @section coreDefines
--- Button which toggles the top flow elements, version which shows inside the top flow when top flow is visible
-- @element hide_top_flow
local hide_top_flow =
Gui.element{
type = 'sprite-button',
sprite = 'utility/preset',
style = 'tool_button',
tooltip = {'gui_util.button_tooltip'},
name = Gui.unique_static_name
}
:style{
padding = -2,
width = 18,
height = 36
}
:on_click(function(player, _,_)
Gui.toggle_top_flow(player, false)
end)
Gui.core_defines.hide_top_flow = hide_top_flow
--- Button which toggles the top flow elements, version which shows inside the left flow when top flow is hidden
-- @element show_top_flow
local show_top_flow =
Gui.element{
type = 'sprite-button',
sprite = 'utility/preset',
style = 'tool_button',
tooltip = {'gui_util.button_tooltip'},
name = Gui.unique_static_name
}
:style{
padding = -2,
width = 18,
height = 20
}
:on_click(function(player, _,_)
Gui.toggle_top_flow(player, true)
end)
Gui.core_defines.show_top_flow = show_top_flow
--- Button which hides the elements in the left flow, shows inside the left flow when frames are visible
-- @element hide_left_flow
local hide_left_flow =
Gui.element{
type = 'sprite-button',
sprite = 'utility/close_black',
style = 'tool_button',
tooltip = {'expcore-gui.left-button-tooltip'},
name = Gui.unique_static_name
}
:style{
padding = -3,
width = 18,
height = 20
}
:on_click(function(player, _,_)
Gui.hide_left_flow(player)
end)
Gui.core_defines.hide_left_flow = hide_left_flow
--- Draw the core elements when a player joins the game
Event.add(defines.events.on_player_created, function(event)
local player = game.players[event.player_index]
-- Draw the top flow
local top_flow = Gui.get_top_flow(player)
hide_top_flow(top_flow)
Gui.update_top_flow(player)
-- Draw the left flow
local left_flow = Gui.get_left_flow(player)
local button_flow = left_flow.add{ type = 'flow', name = 'gui_core_buttons', direction = 'vertical' }
local show_top = show_top_flow(button_flow)
local hide_left = hide_left_flow(button_flow)
show_top.visible = false
hide_left.visible = false
Gui.draw_left_flow(player)
end)

View File

@@ -0,0 +1,298 @@
--[[-- Core Module - Gui
- Common defines that are used by other modules, non of these are used internally
@module Gui
]]
local Gui = require 'expcore.gui.prototype'
--- Defines.
-- @section defines
--[[-- Draw a flow used to align its child elements, default is right align
@element Gui.alignment
@tparam LuaGuiElement parent the parent element to which the alignment will be added
@tparam[opt='alignment'] string name the name of the alignment flow which is added
@tparam[opt='right'] string horizontal_align the horizontal alignment of the elements in the flow
@tparam[opt='center'] string vertical_align the vertical alignment of the elements in the flow
@treturn LuaGuiElement the alignment flow that was created
@usage-- Adding a right align flow
local alignment = Gui.alignment(element, 'example_right_alignment')
@usage-- Adding a horizontal center and top align flow
local alignment = Gui.alignment(element, 'example_center_top_alignment', 'center', 'top')
]]
Gui.alignment =
Gui.element(function(_, parent, name, _,_)
return parent.add{
name = name or 'alignment',
type = 'flow',
}
end)
:style(function(style, _,_, horizontal_align, vertical_align)
style.padding = {1, 2}
style.vertical_align = vertical_align or 'center'
style.horizontal_align = horizontal_align or 'right'
style.vertically_stretchable = style.vertical_align ~= 'center'
style.horizontally_stretchable = style.horizontal_align ~= 'center'
end)
--[[-- Draw a scroll pane that has a table inside of it
@element Gui.scroll_table
@tparam LuaGuiElement parent the parent element to which the scroll table will be added
@tparam number height the maximum height for the scroll pane
@tparam number column_count the number of columns that the table will have
@tparam[opt='scroll'] string name the name of the scroll pane that is added, the table is always called "table"
@treturn LuaGuiElement the table that was created
@usage-- Adding a scroll table with max height of 200 and column count of 3
local scroll_table = Gui.scroll_table(element, 200, 3)
]]
Gui.scroll_table =
Gui.element(function(_, parent, height, column_count, name)
-- Draw the scroll
local scroll_pane =
parent.add{
name = name or 'scroll',
type = 'scroll-pane',
direction = 'vertical',
horizontal_scroll_policy = 'never',
vertical_scroll_policy = 'auto',
style = 'scroll_pane_under_subheader'
}
-- Set the style of the scroll pane
local scroll_style = scroll_pane.style
scroll_style.padding = {1, 3}
scroll_style.maximal_height = height
scroll_style.horizontally_stretchable = true
-- Draw the table
local scroll_table =
scroll_pane.add{
type = 'table',
name = 'table',
column_count = column_count
}
-- Return the scroll table
return scroll_table
end)
:style{
padding = 0,
cell_padding = 0,
vertical_align = 'center',
horizontally_stretchable = true
}
--[[-- Used to add a frame with the header style, has the option for a right alignment flow for buttons
@element Gui.header
@tparam LuaGuiElement parent the parent element to which the header will be added
@tparam ?string|Concepts.LocalizedString caption the caption that will be shown on the header
@tparam[opt] ?string|Concepts.LocalizedString tooltip the tooltip that will be shown on the header
@tparam[opt=false] boolean add_alignment when true an alignment flow will be added to the header
@tparam[opt='header'] string name the name of the header that is being added, the alignment is always called "alignment"
@treturn LuaGuiElement either the header or the header alignment if add_alignment is true
@usage-- Adding a custom header with a label
local header = Gui.header(
element,
'Example Caption',
'Example Tooltip'
)
]]
Gui.header =
Gui.element(function(_, parent, caption, tooltip, add_alignment, name, label_name)
-- Draw the header
local header =
parent.add{
name = name or 'header',
type = 'frame',
style = 'subheader_frame'
}
-- Change the style of the header
local style = header.style
style.padding = {2, 4}
style.use_header_filler = false
style.horizontally_stretchable = true
-- Draw the caption label
if caption then
header.add{
name = label_name or 'header_label',
type = 'label',
style = 'heading_1_label',
caption = caption,
tooltip = tooltip
}
end
-- Return either the header or the added alignment
return add_alignment and Gui.alignment(header) or header
end)
--[[-- Used to add a frame with the footer style, has the option for a right alignment flow for buttons
@element Gui.footer
@tparam LuaGuiElement parent the parent element to which the footer will be added
@tparam ?string|Concepts.LocalizedString caption the caption that will be shown on the footer
@tparam[opt] ?string|Concepts.LocalizedString tooltip the tooltip that will be shown on the footer
@tparam[opt=false] boolean add_alignment when true an alignment flow will be added to the footer
@tparam[opt='footer'] string name the name of the footer that is being added, the alignment is always called "alignment"
@treturn LuaGuiElement either the footer or the footer alignment if add_alignment is true
@usage-- Adding a custom footer with a label
local footer = Gui.footer(
element,
'Example Caption',
'Example Tooltip'
)
]]
Gui.footer =
Gui.element(function(_, parent, caption, tooltip, add_alignment, name)
-- Draw the header
local footer =
parent.add{
name = name or 'footer',
type = 'frame',
style = 'subfooter_frame'
}
-- Change the style of the footer
local style = footer.style
style.padding = {2, 4}
style.use_header_filler = false
style.horizontally_stretchable = true
-- Draw the caption label
if caption then
footer.add{
name = 'footer_label',
type = 'label',
style = 'heading_1_label',
caption = caption,
tooltip = tooltip
}
end
-- Return either the footer or the added alignment
return add_alignment and Gui.alignment(footer) or footer
end)
--[[-- Used for left frames to give them a nice boarder
@element Gui.container
@tparam LuaGuiElement parent the parent element to which the container will be added
@tparam string name the name that you want to give to the outer frame, often just event_trigger
@tparam number width the minimal width that the frame will have
@usage-- Adding a container as a base
local container = Gui.container(parent, 'my_container', 200)
]]
Gui.container =
Gui.element(function(_, parent, name, _)
-- Draw the external container
local frame =
parent.add{
name = name,
type = 'frame'
}
-- Return the container
return frame.add{
name = 'container',
type = 'frame',
direction = 'vertical',
style = 'window_content_frame_packed'
}
end)
:style(function(style, element, _,width)
style.vertically_stretchable = false
local frame_style = element.parent.style
frame_style.padding = 2
frame_style.minimal_width = width
end)
--[[-- Used to make a solid white bar in a gui
@element Gui.bar
@tparam LuaGuiElement parent the parent element to which the bar will be added
@tparam number width the width of the bar that will be made, if not given bar will strech to fill the parent
@usage-- Adding a bar to a gui
local bar = Gui.bar(parent, 100)
]]
Gui.bar =
Gui.element(function(_, parent)
return parent.add{
type = 'progressbar',
size = 1,
value = 1
}
end)
:style(function(style, _,width)
style.height = 3
style.color = {r=255, g=255, b=255}
if width then style.width = width
else style.horizontally_stretchable = true end
end)
--[[-- Used to make a label which is centered and of a certian size
@element Gui.centered_label
@tparam LuaGuiElement parent the parent element to which the label will be added
@tparam number width the width of the label, must be given in order to center the caption
@tparam ?string|Concepts.LocalizedString caption the caption that will be shown on the label
@tparam[opt] ?string|Concepts.LocalizedString tooltip the tooltip that will be shown on the label
@usage-- Adding a centered label
local label = Gui.centered_label(parent, 100, 'This is centered')
]]
Gui.centered_label =
Gui.element(function(_, parent, width, caption, tooltip)
local label = parent.add{
type = 'label',
caption = caption,
tooltip = tooltip,
style = 'description_label'
}
local style = label.style
style.horizontal_align = 'center'
style.single_line = false
style.width = width
return label
end)
--[[-- Used to make a title which has two bars on either side
@element Gui.title_label
@tparam LuaGuiElement parent the parent element to which the label will be added
@tparam number width the width of the first bar, this can be used to position the label
@tparam ?string|Concepts.LocalizedString caption the caption that will be shown on the label
@tparam[opt] ?string|Concepts.LocalizedString tooltip the tooltip that will be shown on the label
@usage-- Adding a centered label
local label = Gui.centered_label(parent, 100, 'This is centered')
]]
Gui.title_label =
Gui.element(function(_, parent, width, caption, tooltip)
local title_flow = parent.add{ type='flow' }
title_flow.style.vertical_align = 'center'
Gui.bar(title_flow, width)
local title_label = title_flow.add{
type = 'label',
caption = caption,
tooltip = tooltip,
style = 'heading_1_label'
}
Gui.bar(title_flow)
return title_label
end)

View File

@@ -0,0 +1,91 @@
--[[-- Core Module - Gui
- Functions used to help with the use of guis
@module Gui
]]
local Gui = require 'expcore.gui.prototype'
--- Helper Functions.
-- @section helperFunctions
--[[-- Get the player that owns a gui element
@tparam LuaGuiElement element the element to get the owner of
@treturn LuaPlayer the player that owns this element
@usage-- Geting the owner of an element
local player = Gui.get_player_from_element(element)
]]
function Gui.get_player_from_element(element)
if not element or not element.valid then return end
return game.players[element.player_index]
end
--[[-- Will toggle the enabled state of an element or set it to the one given
@tparam LuaGuiElement element the element to toggle/set the enabled state of
@tparam[opt] boolean state with given will set the state, else state will be toggled
@treturn boolean the new enabled state that the element has
@usage-- Toggling the the enabled state
local new_enabled_state = Gui.toggle_enabled_state(element)
]]
function Gui.toggle_enabled_state(element, state)
if not element or not element.valid then return end
if state == nil then state = not element.enabled end
element.enabled = state
return state
end
--[[-- Will toggle the visible state of an element or set it to the one given
@tparam LuaGuiElement element the element to toggle/set the visible state of
@tparam[opt] boolean state with given will set the state, else state will be toggled
@treturn boolean the new visible state that the element has
@usage-- Toggling the the visible state
local new_visible_state = Gui.toggle_visible_state(element)
]]
function Gui.toggle_visible_state(element, state)
if not element or not element.valid then return end
if state == nil then state = not element.visible end
element.visible = state
return state
end
--[[-- Destory a gui element without causing any errors, often because the element was already removed
@tparam LuaGuiElement element the element that you want to remove
@treturn boolean true if the element was valid and has been removed
@usage-- Remove a child element if it exists
Gui.destroy_if_valid(element[child_name])
]]
function Gui.destroy_if_valid(element)
if not element or not element.valid then return false end
element.destroy()
return true
end
--[[-- Returns a table to be used as the style for a sprite buttons, produces a sqaure button
@tparam number size the size that you want the button to be
@tparam[opt=-2] number padding the padding that you want on the sprite
@tparam[opt] table style any extra style settings that you want to have
@treturn table the style table to be used with element_define:style()
@usage-- Adding a sprite button with size 20
local button =
Gui.element{
type = 'sprite-button',
sprite = 'entity/inserter'
}
:style(Gui.sprite_style(20))
]]
function Gui.sprite_style(size, padding, style)
style = style or {}
style.padding = padding or -2
style.height = size
style.width = size
return style
end

View File

@@ -0,0 +1,275 @@
--[[-- Core Module - Gui
- Used to define new gui elements and gui event handlers
@module Gui
]]
local Gui = require 'expcore.gui.prototype'
local mod_gui = require 'mod-gui'
local hide_left_flow = Gui.core_defines.hide_left_flow.name
--- Left Flow.
-- @section leftFlow
-- Triggered when a user changed the visibility of a left flow element by clicking a button
Gui.events.on_visibility_changed_by_click = 'on_visibility_changed_by_click'
--- Contains the uids of the elements that will shown on the left flow and their join functions
-- @table left_elements
Gui.left_elements = {}
--[[-- Gets the flow refered to as the left flow, each player has one left flow
@function Gui.get_left_flow(player)
@tparam LuaPlayer player the player that you want to get the left flow for
@treturn LuaGuiElement the left element flow
@usage-- Geting your left flow
local left_flow = Gui.get_left_flow(game.player)
]]
Gui.get_left_flow = mod_gui.get_frame_flow
--[[-- Sets an element define to be drawn to the left flow when a player joins, includes optional check
@tparam[opt] ?boolean|function open_on_join called during first darw to decide if the element should be visible
@treturn table the new element define that is used to register events to this element
@usage-- Adding the example button
example_flow_with_button:add_to_left_flow(true)
]]
function Gui._prototype_element:add_to_left_flow(open_on_join)
_C.error_if_runtime()
if not self.name then error("Elements for the top flow must have a static name") end
self.open_on_join = open_on_join or false
table.insert(Gui.left_elements, self)
return self
end
--[[-- Creates a button on the top flow which will toggle the given element define, the define must exist in the left flow
@tparam string sprite the sprite that you want to use on the button
@tparam ?string|Concepts.LocalizedString tooltip the tooltip that you want the button to have
@tparam table element_define the element define that you want to have toggled by this button, define must exist on the left flow
@tparam[opt] function authenticator used to decide if the button should be visible to a player
@usage-- Add a button to toggle a left element
local toolbar_button =
Gui.left_toolbar_button('entity/inserter', 'Nothing to see here', example_flow_with_button, function(player)
return player.admin
end)
]]
function Gui.left_toolbar_button(sprite, tooltip, element_define, authenticator)
local button = Gui.toolbar_button(sprite, tooltip, authenticator)
-- Add on_click handler to handle click events comming from the player
button:on_click(function(player, _, _)
-- Raise custom event that tells listening elements if the element has changed visibility by a player clicking
-- Used in warp gui to handle the keep open logic
button:raise_event{
name = Gui.events.on_visibility_changed_by_click,
element = Gui.get_top_element(player, button),
state = Gui.toggle_left_element(player, element_define)
}
end)
-- Add property to the left flow element with the name of the button
-- This is for the ability to reverse lookup the button from the left flow element
element_define.toolbar_button = button
button.left_flow_element = element_define
return button
end
Gui._left_flow_order_src = "<default>"
--- Get the order of elements in the left flow, first argument is player but is unused in the default method
function Gui.get_left_flow_order(_)
return Gui.left_elements
end
--- Inject a custom left flow order provider, this should accept a player and return a list of elements definitions to draw
function Gui.inject_left_flow_order(provider)
Gui.get_left_flow_order = provider
local debug_info = debug.getinfo(2, "Sn")
local file_name = debug_info.source:match('^.+/currently%-playing/(.+)$'):sub(1, -5)
local func_name = debug_info.name or ("<anonymous:"..debug_info.linedefined..">")
Gui._left_flow_order_src = file_name..":"..func_name
end
--[[-- Draw all the left elements onto the left flow, internal use only with on join
@tparam LuaPlayer player the player that you want to draw the elements for
@usage-- Draw all the left elements
Gui.draw_left_flow(player)
]]
function Gui.draw_left_flow(player)
local left_flow = Gui.get_left_flow(player)
local hide_button = left_flow.gui_core_buttons[hide_left_flow]
local show_hide_button = false
-- Get the order to draw the elements in
local flow_order = Gui.get_left_flow_order(player)
if #flow_order ~= #Gui.left_elements then
error(string.format("Left flow order provider (%s) did not return the correct element count, expect %d got %d",
Gui._left_flow_order_src, #Gui.left_elements, #flow_order
))
end
for _, element_define in ipairs(flow_order) do
-- Draw the element to the left flow
local draw_success, left_element = xpcall(function()
return element_define(left_flow)
end, debug.traceback)
if not draw_success then
log('There as been an error with an element draw function: '..element_define.defined_at..'\n\t'..left_element)
goto continue
end
-- Check if it should be open by default
local open_on_join = element_define.open_on_join
local visible = type(open_on_join) == 'boolean' and open_on_join or false
if type(open_on_join) == 'function' then
local success, err = xpcall(open_on_join, debug.traceback, player)
if not success then
log('There as been an error with an open on join hander for a gui element:\n\t'..err)
goto continue
end
visible = err
end
-- Set the visible state of the element
left_element.visible = visible
show_hide_button = show_hide_button or visible
-- Check if the the element has a button attached
if element_define.toolbar_button then
Gui.toggle_toolbar_button(player, element_define.toolbar_button, visible)
end
::continue::
end
hide_button.visible = show_hide_button
end
--- Reorder the left flow elements to match that returned by the provider, uses a method equivalent to insert sort
function Gui.reorder_left_flow(player)
local left_flow = Gui.get_left_flow(player)
-- Get the order to draw the elements in
local flow_order = Gui.get_left_flow_order(player)
if #flow_order ~= #Gui.left_elements then
error(string.format("Left flow order provider (%s) did not return the correct element count, expect %d got %d",
Gui._left_flow_order_src, #Gui.left_elements, #flow_order
))
end
-- Reorder the elements, index 1 is the core ui buttons so +1 is required
for index, element_define in ipairs(flow_order) do
local element = left_flow[element_define.name]
left_flow.swap_children(index+1, element.get_index_in_parent())
end
end
--[[-- Update the visible state of the hide button, can be used to check if any frames are visible
@tparam LuaPlayer player the player to update the left flow for
@treturn boolean true if any left element is visible
@usage-- Check if any left elements are visible
local visible = Gui.update_left_flow(player)
]]
function Gui.update_left_flow(player)
local left_flow = Gui.get_left_flow(player)
local hide_button = left_flow.gui_core_buttons[hide_left_flow]
for _, element_define in ipairs(Gui.left_elements) do
local left_element = left_flow[element_define.name]
if left_element.visible then
hide_button.visible = true
return true
end
end
hide_button.visible = false
return false
end
--[[-- Hides all left elements for a player
@tparam LuaPlayer player the player to hide the elements for
@usage-- Hide your left elements
Gui.hide_left_flow(game.player)
]]
function Gui.hide_left_flow(player)
local top_flow = Gui.get_top_flow(player)
local left_flow = Gui.get_left_flow(player)
local hide_button = left_flow.gui_core_buttons[hide_left_flow]
-- Set the visible state of all elements in the flow
hide_button.visible = false
for _, element_define in ipairs(Gui.left_elements) do
left_flow[element_define.name].visible = false
-- Check if the the element has a toobar button attached
if element_define.toolbar_button then
-- Check if the topflow contains the button
local button = top_flow[element_define.toolbar_button.name]
if button then
-- Style the button
Gui.toggle_toolbar_button(player, element_define.toolbar_button, false)
-- Raise the custom event if all of the top checks have passed
element_define.toolbar_button:raise_event{
name = Gui.events.on_visibility_changed_by_click,
element = button,
state = false
}
end
end
end
end
--- Checks if an element is loaded, used internally when the normal left gui assumptions may not hold
function Gui.left_flow_loaded(player, element_define)
local left_flow = Gui.get_left_flow(player)
return left_flow[element_define.name] ~= nil
end
--[[-- Get the element define that is in the left flow, use in events without an element refrence
@tparam LuaPlayer player the player that you want to get the element for
@tparam table element_define the element that you want to get
@treturn LuaGuiElement the gui element linked to this define for this player
@usage-- Get your left element
local frame = Gui.get_left_element(game.player, example_flow_with_button)
]]
function Gui.get_left_element(player, element_define)
local left_flow = Gui.get_left_flow(player)
return assert(left_flow[element_define.name], "Left element failed to load")
end
--[[-- Toggles the visible state of a left element for a given player, can be used to set the visible state
@tparam LuaPlayer player the player that you want to toggle the element for
@tparam table element_define the element that you want to toggle
@tparam[opt] boolean state with given will set the state, else state will be toggled
@treturn boolean the new visible state of the element
@usage-- Toggle your example button
Gui.toggle_top_flow(game.player, example_flow_with_button)
@usage-- Show your example button
Gui.toggle_top_flow(game.player, example_flow_with_button, true)
]]
function Gui.toggle_left_element(player, element_define, state)
-- Set the visible state
local element = Gui.get_left_element(player, element_define)
if state == nil then state = not element.visible end
element.visible = state
Gui.update_left_flow(player)
-- Check if the the element has a button attached
if element_define.toolbar_button then
Gui.toggle_toolbar_button(player, element_define.toolbar_button, state)
end
return state
end

View File

@@ -0,0 +1,412 @@
--[[-- Core Module - Gui
- Used to simplify gui creation using factory functions called element defines
@module Gui
]]
local Event = require 'utils.event' --- @dep utils.event
local Gui = {
--- The current highest uid that is being used by a define, will not increase during runtime
uid = 0,
--- Used to automatically assign a unique static name to an element
unique_static_name = {},
--- String indexed table used to avoid conflict with custom event names, similar to how defines.events works
events = {},
--- Uid indexed array that stores all the factory functions that were defined, no new values will be added during runtime
defines = {},
--- An string indexed table of all the defines which are used by the core of the gui system, used for internal reference
core_defines = {},
--- Used to store the file names where elements were defined, this can be useful to find the uid of an element, mostly for debugging
file_paths = {},
--- Used to store extra information about elements as they get defined such as the params used and event handlers registered to them
debug_info = {},
--- The prototype used to store the functions of an element define
_prototype_element = {},
--- The prototype metatable applied to new element defines
_mt_element = {}
}
--- Allow access to the element prototype methods
Gui._mt_element.__index = Gui._prototype_element
--- Allows the define to be called to draw the element
function Gui._mt_element.__call(self, parent, ...)
local element, no_events = self._draw(self, parent, ...)
if self._style then self._style(element.style, element, ...) end
-- Asserts to catch common errors
if element then
if self.name and self.name ~= element.name then
error("Static name \""..self.name.."\" expected but got: "..tostring(element.name))
end
local event_triggers = element.tags and element.tags.ExpGui_event_triggers
if event_triggers and table.array_contains(event_triggers, self.uid) then
error("Element::triggers_events should not be called on the value you return from the definition")
end
elseif self.name then
error("Static name \""..self.name.."\" expected but no element was returned from the definition")
end
-- Register events by default, but allow skipping them
if no_events == self.no_events then
return element
else
return element and self:triggers_events(element)
end
end
--- Get where a function was defined as a string
local function get_defined_at(level)
local debug_info = debug.getinfo(level, "Sn")
local file_name = debug_info.source:match('^.+/currently%-playing/(.+)$'):sub(1, -5)
local func_name = debug_info.name or ("<anonymous:"..debug_info.linedefined..">")
return file_name..":"..func_name
end
--- Element Define.
-- @section elementDefine
--[[-- Used to define new elements for your gui, can be used like LuaGuiElement.add or a custom function
@tparam ?table|function element_define the define information for the gui element, same data as LuaGuiElement.add, or a custom function may be used
@treturn table the new element define, this can be considered a factory for the element which can be called to draw the element to any other element
@usage-- Using element defines like LuaGuiElement.add
-- This returns a factory function to draw a button with the caption "Example Button"
local example_button =
Gui.element{
type = 'button',
caption = 'Example Button'
}
@usage-- Using element defines with a custom factory function
-- This method can be used if you still want to be able register event handlers but it is too complex to be compatible with LuaGuiElement.add
local example_flow_with_button =
Gui.element(function(event_trigger, parent, ...)
-- ... shows that all other arguments from the factory call are passed to this function
-- parent is the element which was passed to the factory function where you should add your new element
-- here we are adding a flow which we will then later add a button to
local flow =
parent.add{
name = 'example_flow',
type = 'flow'
}
-- event_trigger should be the name of any elements you want to trigger your event handlers, such as on_click or on_state_changed
-- now we add the button to the flow that we created earlier
local element =
flow.add{
name = event_trigger,
type = 'button',
caption = 'Example Button'
}
-- you must return your new element, this is so styles can be applied and returned to the caller
-- you may return any of your elements that you add, consider the context in which it will be used for what should be returned
return element
end)
]]
function Gui.element(element_define)
_C.error_if_runtime()
-- Set the metatable to allow access to register events
local element = setmetatable({}, Gui._mt_element)
-- Increment the uid counter
local uid = Gui.uid + 1
Gui.uid = uid
element.uid = uid
Gui.debug_info[uid] = { draw = 'None', style = 'None', events = {} }
-- Add the definition function
if type(element_define) == 'table' then
Gui.debug_info[uid].draw = element_define
if element_define.name == Gui.unique_static_name then
element_define.name = "ExpGui_"..tostring(uid)
end
for k, v in pairs(element_define) do
if element[k] == nil then
element[k] = v
end
end
element._draw = function(_, parent)
return parent.add(element_define)
end
else
Gui.debug_info[uid].draw = get_defined_at(element_define)
element._draw = element_define
end
-- Add the define to the base module
element.defined_at = get_defined_at(3)
Gui.file_paths[uid] = element.defined_at
Gui.defines[uid] = element
-- Return the element so event handers can be accessed
return element
end
--[[-- Used to extent your element define with a style factory, this style will be applied to your element when created, can also be a custom function
@tparam ?table|function style_define style table where each key and value pair is treated like LuaGuiElement.style[key] = value, a custom function can be used
@treturn table the element define is returned to allow for event handlers to be registered
@usage-- Using the table method of setting the style
local example_button =
Gui.element{
type = 'button',
caption = 'Example Button',
style = 'forward_button' -- factorio styles can be applied here
}
:style{
height = 25, -- same as element.style.height = 25
width = 100 -- same as element.style.width = 25
}
@usage-- Using the function method to set the style
-- Use this method if your style is dynamic and depends on other factors
local example_button =
Gui.element{
type = 'button',
caption = 'Example Button',
style = 'forward_button' -- factorio styles can be applied here
}
:style(function(style, element, ...)
-- style is the current style object for the elemenent
-- element is the element that is being changed
-- ... shows that all other arguments from the factory call are passed to this function
local player = game.players[element.player_index]
style.height = 25
style.width = 100
style.font_color = player.color
end)
]]
function Gui._prototype_element:style(style_define)
_C.error_if_runtime()
-- Add the definition function
if type(style_define) == 'table' then
Gui.debug_info[self.uid].style = style_define
self._style = function(style)
for key, value in pairs(style_define) do
style[key] = value
end
end
else
Gui.debug_info[self.uid].style = get_defined_at(style_define)
self._style = style_define
end
-- Return the element so event handers can be accessed
return self
end
--[[-- Enforce the fact the element has a static name, this is required for the cases when a function define is used
@tparam[opt] string element The element that will trigger calls to the event handlers
@treturn table the element define is returned to allow for event handlers to be registered
]]
function Gui._prototype_element:static_name(name)
_C.error_if_runtime()
if name == Gui.unique_static_name then
self.name = "ExpGui_"..tostring(self.uid)
else
self.name = name
end
return self
end
--[[-- Used to link an element to an element define such that any event on the element will call the handlers on the element define
-- You should not call this on the element you return from your constructor because this is done automatically
@tparam LuaGuiElement element The element that will trigger calls to the event handlers
@treturn LuaGuiElement The element passed as the argument to allow for cleaner returns
]]
function Gui._prototype_element:triggers_events(element)
if not self._has_events then return element end
local tags = element.tags
if not tags then
element.tags = { ExpGui_event_triggers = { self.uid } }
return element
elseif not tags.ExpGui_event_triggers then
tags.ExpGui_event_triggers = { self.uid }
elseif table.array_contains(tags.ExpGui_event_triggers, self.uid) then
error("Element::triggers_events called multiple times on the same element with the same definition")
else
table.insert(tags.ExpGui_event_triggers, self.uid)
end
-- To modify a set of tags, the whole table needs to be written back to the respective property.
element.tags = tags
return element
end
--- Explicitly skip events on the element returned by your definition function
function Gui._prototype_element:no_events(element)
return element, self.no_events
end
--[[-- Set the handler which will be called for a custom event, only one handler can be used per event per element
@tparam string event_name the name of the event you want to handler to be called on, often from Gui.events
@tparam function handler the handler that you want to be called when the event is raised
@treturn table the element define so more handleres can be registered
@usage-- Register a handler to "my_custom_event" for this element
element_deinfe:on_event('my_custom_event', function(event)
event.player.print(player.name)
end)
]]
function Gui._prototype_element:on_event(event_name, handler)
_C.error_if_runtime()
table.insert(Gui.debug_info[self.uid].events, event_name)
Gui.events[event_name] = event_name
self[event_name] = handler
self._has_events = true
return self
end
--[[-- Raise the handler which is attached to an event; external use should be limited to custom events
@tparam table event the event table passed to the handler, must contain fields: name, element
@treturn table the element define so more events can be raised
@usage Raising a custom event
element_define:raise_event{
name = 'my_custom_event',
element = element
}
]]
function Gui._prototype_element:raise_event(event)
-- Check the element is valid
local element = event.element
if not element or not element.valid then
return self
end
-- Get the event handler for this element
local handler = self[event.name]
if not handler then
return self
end
-- Get the player for this event
local player_index = event.player_index or element.player_index
local player = game.players[player_index]
if not player or not player.valid then
return self
end
event.player = player
local success, err = xpcall(handler, debug.traceback, player, element, event)
if not success then
error('There as been an error with an event handler for a gui element:\n\t'..err)
end
return self
end
-- This function is used to link element define events and the events from the factorio api
local function event_handler_factory(event_name)
Event.add(event_name, function(event)
local element = event.element
if not element or not element.valid then return end
local event_triggers = element.tags.ExpGui_event_triggers
if not event_triggers then return end
for _, uid in pairs(event_triggers) do
local element_define = Gui.defines[uid]
if element_define then
element_define:raise_event(event)
end
end
end)
Gui.events[event_name] = event_name
return function(self, handler)
return self:on_event(event_name, handler)
end
end
--- Element Events.
-- @section elementEvents
--- Called when the player opens a GUI.
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_open(function(event)
-- event.player.print(table.inspect(event))
--end)
Gui._prototype_element.on_open = event_handler_factory(defines.events.on_gui_opened)
--- Called when the player closes the GUI they have open.
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_close(function(event)
-- event.player.print(table.inspect(event))
--end)
Gui._prototype_element.on_close = event_handler_factory(defines.events.on_gui_closed)
--- Called when LuaGuiElement is clicked.
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_click(function(event)
-- event.player.print(table.inspect(event))
--end)
Gui._prototype_element.on_click = event_handler_factory(defines.events.on_gui_click)
--- Called when a LuaGuiElement is confirmed, for example by pressing Enter in a textfield.
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_confirmed(function(event)
-- event.player.print(table.inspect(event))
--end)
Gui._prototype_element.on_confirmed = event_handler_factory(defines.events.on_gui_confirmed)
--- Called when LuaGuiElement checked state is changed (related to checkboxes and radio buttons).
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_checked_changed(function(event)
-- event.player.print(table.inspect(event))
--end)
Gui._prototype_element.on_checked_changed = event_handler_factory(defines.events.on_gui_checked_state_changed)
--- Called when LuaGuiElement element value is changed (related to choose element buttons).
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_elem_changed(function(event)
-- event.player.print(table.inspect(event))
--end)
Gui._prototype_element.on_elem_changed = event_handler_factory(defines.events.on_gui_elem_changed)
--- Called when LuaGuiElement element location is changed (related to frames in player.gui.screen).
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_location_changed(function(event)
-- event.player.print(table.inspect(event))
--end)
Gui._prototype_element.on_location_changed = event_handler_factory(defines.events.on_gui_location_changed)
--- Called when LuaGuiElement selected tab is changed (related to tabbed-panes).
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_tab_changed(function(event)
-- event.player.print(table.inspect(event))
--end)
Gui._prototype_element.on_tab_changed = event_handler_factory(defines.events.on_gui_selected_tab_changed)
--- Called when LuaGuiElement selection state is changed (related to drop-downs and listboxes).
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_selection_changed(function(event)
-- event.player.print(table.inspect(event))
--end)
Gui._prototype_element.on_selection_changed = event_handler_factory(defines.events.on_gui_selection_state_changed)
--- Called when LuaGuiElement switch state is changed (related to switches).
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_switch_changed(function(event)
-- event.player.print(table.inspect(event))
--end)
Gui._prototype_element.on_switch_changed = event_handler_factory(defines.events.on_gui_switch_state_changed)
--- Called when LuaGuiElement text is changed by the player.
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_text_changed(function(event)
-- event.player.print(table.inspect(event))
--end)
Gui._prototype_element.on_text_changed = event_handler_factory(defines.events.on_gui_text_changed)
--- Called when LuaGuiElement slider value is changed (related to the slider element).
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_value_changed(function(event)
-- event.player.print(table.inspect(event))
--end)
Gui._prototype_element.on_value_changed = event_handler_factory(defines.events.on_gui_value_changed)
-- Module return
return Gui

View File

@@ -0,0 +1,315 @@
--[[-- Core Module - Gui
- Controls the elements on the top flow
@module Gui
]]
local Gui = require 'expcore.gui.prototype'
local mod_gui = require 'mod-gui' --- @dep mod-gui
local toolbar_button_size = 36
local hide_top_flow = Gui.core_defines.hide_top_flow.name
local show_top_flow = Gui.core_defines.show_top_flow.name
--- Top Flow.
-- @section topFlow
-- Triggered when a user changed the visibility of a left flow element by clicking a button
Gui.events.on_toolbar_button_toggled = 'on_toolbar_button_toggled'
--- Contains the uids of the elements that will shown on the top flow and their auth functions
-- @table top_elements
Gui.top_elements = {}
--- The style that should be used for buttons on the top flow
-- @field Gui.top_flow_button_style
Gui.top_flow_button_style = mod_gui.button_style
--- The style that should be used for buttons on the top flow when their flow is visible
-- @field Gui.top_flow_button_toggled_style
Gui.top_flow_button_toggled_style = 'menu_button_continue'
--[[-- Styles a top flow button depending on the state given
@tparam LuaGuiElement button the button element to style
@tparam boolean state The state the button is in
@usage-- Sets the button to the visible style
Gui.toolbar_button_style(button, true)
@usage-- Sets the button to the hidden style
Gui.toolbar_button_style(button, false)
]]
function Gui.toolbar_button_style(button, state, size)
---@cast button LuaGuiElement
if state then
button.style = Gui.top_flow_button_toggled_style
else
button.style = Gui.top_flow_button_style
end
button.style.minimal_width = size or toolbar_button_size
button.style.height = size or toolbar_button_size
button.style.padding = -2
end
--[[-- Gets the flow refered to as the top flow, each player has one top flow
@function Gui.get_top_flow(player)
@tparam LuaPlayer player the player that you want to get the flow for
@treturn LuaGuiElement the top element flow
@usage-- Geting your top flow
local top_flow = Gui.get_top_flow(game.player)
]]
Gui.get_top_flow = mod_gui.get_button_flow
--[[-- Sets an element define to be drawn to the top flow when a player joins, includes optional authenticator
@tparam[opt] function authenticator called during toggle or update to decide weather the element should be visible
@treturn table the new element define to allow event handlers to be registered
@usage-- Adding an element to the top flow on join
example_button:add_to_top_flow(function(player)
-- example button will only be shown if the player is an admin
-- note button will not update its state when player.admin is changed Gui.update_top_flow must be called for this
return player.admin
end)
]]
function Gui._prototype_element:add_to_top_flow(authenticator)
_C.error_if_runtime()
if not self.name then error("Elements for the top flow must have a static name") end
self.authenticator = authenticator or true
table.insert(Gui.top_elements, self)
return self
end
--- Returns true if the top flow has visible elements
function Gui.top_flow_has_visible_elements(player)
local top_flow = Gui.get_top_flow(player)
for _, child in pairs(top_flow.children) do
if child.name ~= hide_top_flow then
if child.visible then
return true
end
end
end
return false
end
Gui._top_flow_order_src = "<default>"
--- Get the order of elements in the top flow, first argument is player but is unused in the default method
function Gui.get_top_flow_order(_)
return Gui.top_elements
end
--- Inject a custom top flow order provider, this should accept a player and return a list of elements definitions to draw
function Gui.inject_top_flow_order(provider)
Gui.get_top_flow_order = provider
local debug_info = debug.getinfo(2, "Sn")
local file_name = debug_info.source:match('^.+/currently%-playing/(.+)$'):sub(1, -5)
local func_name = debug_info.name or ("<anonymous:"..debug_info.linedefined..">")
Gui._top_flow_order_src = file_name..":"..func_name
end
--[[-- Updates the visible state of all the elements on the players top flow, uses authenticator
@tparam LuaPlayer player the player that you want to update the top flow for
@usage-- Update your top flow
Gui.update_top_flow(game.player)
]]
function Gui.update_top_flow(player)
local top_flow = Gui.get_top_flow(player)
-- Get the order to draw the elements in
local flow_order = Gui.get_top_flow_order(player)
if #flow_order ~= #Gui.top_elements then
error(string.format("Top flow order provider (%s) did not return the correct element count, expect %d got %d",
Gui._top_flow_order_src, #Gui.top_elements, #flow_order
))
end
-- Set the visible state of all elements in the flow
for index, element_define in ipairs(flow_order) do
-- Ensure the element exists
local element = top_flow[element_define.name]
if not element then
element = element_define(top_flow)
else
top_flow.swap_children(index+1, element.get_index_in_parent())
end
-- Set the visible state
local allowed = element_define.authenticator
if type(allowed) == 'function' then allowed = allowed(player) end
element.visible = allowed or false
-- If its not visible and there is a left element, then hide it
if element_define.left_flow_element and not element.visible and Gui.left_flow_loaded(player, element_define.left_flow_element) then
Gui.toggle_left_element(player, element_define.left_flow_element, false)
end
end
-- Check if there are any visible elements in the top flow
if not Gui.top_flow_has_visible_elements(player) then
-- None are visible so hide the top_flow and its show button
Gui.toggle_top_flow(player, false)
local left_flow = Gui.get_left_flow(player)
local show_button = left_flow.gui_core_buttons[show_top_flow]
show_button.visible = false
end
end
--- Reorder the top flow elements to match that returned by the provider, uses a method equivalent to insert sort
function Gui.reorder_top_flow(player)
local top_flow = Gui.get_top_flow(player)
-- Get the order to draw the elements in
local flow_order = Gui.get_top_flow_order(player)
if #flow_order ~= #Gui.top_elements then
error(string.format("Top flow order provider (%s) did not return the correct element count, expect %d got %d",
Gui._top_flow_order_src, #Gui.top_elements, #flow_order
))
end
-- Reorder the elements, index 1 is the core ui buttons so +1 is required
for index, element_define in ipairs(flow_order) do
local element = top_flow[element_define.name]
top_flow.swap_children(index+1, element.get_index_in_parent())
end
end
--[[-- Toggles the visible state of all the elements on a players top flow, effects all elements
@tparam LuaPlayer player the player that you want to toggle the top flow for
@tparam[opt] boolean state if given then the state will be set to this
@treturn boolean the new visible state of the top flow
@usage-- Toggle your flow
Gui.toggle_top_flow(game.player)
@usage-- Open your top flow
Gui.toggle_top_flow(game.player, true)
]]
function Gui.toggle_top_flow(player, state)
-- Get the top flow, we need the parent as we want to toggle the outer frame
local top_flow = Gui.get_top_flow(player).parent
if state == nil then state = not top_flow.visible end
-- Get the show button for the top flow
local left_flow = Gui.get_left_flow(player)
local show_button = left_flow.gui_core_buttons[show_top_flow]
-- Change the visibility of the top flow and show top flow button
show_button.visible = not state
top_flow.visible = state
return state
end
--[[-- Get the element define that is in the top flow, use in events without an element refrence
@tparam LuaPlayer player the player that you want to get the element for
@tparam table element_define the element that you want to get
@treturn LuaGuiElement the gui element linked to this define for this player
@usage-- Get your top element
local button = Gui.get_top_element(game.player, example_button)
]]
function Gui.get_top_element(player, element_define)
local top_flow = Gui.get_top_flow(player)
return assert(top_flow[element_define.name], "Top element failed to load")
end
--[[-- Toggles the state of a toolbar button for a given player, can be used to set the visual state
@tparam LuaPlayer player the player that you want to toggle the element for
@tparam table element_define the element that you want to toggle
@tparam[opt] boolean state with given will set the state, else state will be toggled
@treturn boolean the new visible state of the element
@usage-- Toggle your example button
Gui.toggle_toolbar_button(game.player, toolbar_button)
@usage-- Show your example button
Gui.toggle_toolbar_button(game.player, toolbar_button, true)
]]
function Gui.toggle_toolbar_button(player, element_define, state)
local toolbar_button = Gui.get_top_element(player, element_define)
if state == nil then state = toolbar_button.style.name ~= Gui.top_flow_button_toggled_style end
Gui.toolbar_button_style(toolbar_button, state, toolbar_button.style.minimal_width)
element_define:raise_event{
name = Gui.events.on_toolbar_button_toggled,
element = toolbar_button,
player = player,
state = state
}
return state
end
--[[-- Creates a button on the top flow with consistent styling
@tparam string sprite the sprite that you want to use on the button
@tparam ?string|Concepts.LocalizedString tooltip the tooltip that you want the button to have
@tparam[opt] function authenticator used to decide if the button should be visible to a player
@usage-- Add a button to the toolbar
local toolbar_button =
Gui.left_toolbar_button('entity/inserter', 'Nothing to see here', function(player)
return player.admin
end)
]]
function Gui.toolbar_button(sprite, tooltip, authenticator)
return Gui.element{
type = 'sprite-button',
sprite = sprite,
tooltip = tooltip,
style = Gui.top_flow_button_style,
name = Gui.unique_static_name
}
:style{
minimal_width = toolbar_button_size,
height = toolbar_button_size,
padding = -2
}
:add_to_top_flow(authenticator)
end
--[[-- Creates a toggle button on the top flow with consistent styling
@tparam string sprite the sprite that you want to use on the button
@tparam ?string|Concepts.LocalizedString tooltip the tooltip that you want the button to have
@tparam[opt] function authenticator used to decide if the button should be visible to a player
@usage-- Add a button to the toolbar
local toolbar_button =
Gui.toolbar_toggle_button('entity/inserter', 'Nothing to see here', function(player)
return player.admin
end)
:on_event(Gui.events.on_toolbar_button_toggled, function(player, element, event)
game.print(table.inspect(event))
end)
]]
function Gui.toolbar_toggle_button(sprite, tooltip, authenticator)
local button =
Gui.element{
type = 'sprite-button',
sprite = sprite,
tooltip = tooltip,
style = Gui.top_flow_button_style,
name = Gui.unique_static_name
}
:style{
minimal_width = toolbar_button_size,
height = toolbar_button_size,
padding = -2
}
:add_to_top_flow(authenticator)
button:on_click(function(player, _, _)
Gui.toggle_toolbar_button(player, button)
end)
return button
end

View File

@@ -0,0 +1,358 @@
--[[-- Core Module - Permission Groups
- Permission group making for factorio so you never have to make one by hand again
@core Groups
@alias Permissions_Groups
@usage--- Example Group (Allow All)
-- here we will create an admin group however we do not want them to use the map editor or mess with the permission groups
Permission_Groups.new_group('Admin') -- this defines a new group called "Admin"
:allow_all() -- this makes the default to allow any input action unless set other wise
:disallow{ -- here we disallow the input action we don't want them to use
'add_permission_group',
'delete_permission_group',
'import_permissions_string',
'map_editor_action',
'toggle_map_editor'
}
@usage--- Example Group (Disallow All)
-- here we will create a group that cant do anything but talk in chat
Permission_Groups.new_group('Restricted') -- this defines a new group called "Restricted"
:disallow_all() -- this makes the default to disallow any input action unless set other wise
:allow('write_to_console') -- here we allow them to chat, {} can be used here if we had more than one action
]]
local Game = require 'utils.game' --- @dep utils.game
local Event = require 'utils.event' --- @dep utils.event
local Async = require 'expcore.async' --- @dep expcore.async
local Permissions_Groups = {
groups={}, -- store for the different groups that are created
_prototype={} -- stores functions that are used on group instances
}
-- Async function to add players to permission groups
local add_to_permission_group =
Async.register(function(permission_group, player)
permission_group.add_player(player)
end)
Permissions_Groups.async_token_add_to_permission_group = add_to_permission_group
-- Async function to remove players from permission groups
local remove_from_permission_group =
Async.register(function(permission_group, player)
permission_group.remove_player(player)
end)
Permissions_Groups.async_token_remove_from_permission_group = remove_from_permission_group
--- Getters.
-- Functions that get permission groups
-- @section getters
--[[-- Defines a new permission group that can have it actions set in the config
@tparam string name the name of the new group
@treturn Permissions_Groups._prototype the new group made with function to allow and disallow actions
@usage-- Defining a new permission group
Groups.new_group('Admin')
]]
function Permissions_Groups.new_group(name)
local group = setmetatable({
name=name,
actions={},
allow_all_actions=true
}, {
__index= Permissions_Groups._prototype
})
Permissions_Groups.groups[name] = group
return group
end
--[[-- Returns the group with the given name, case sensitive
@tparam string name the name of the group to get
@treturn ?Permissions_Groups._prototype|nil the group with that name or nil if non found
@usage-- Getting a permision group
local admin_group = Groups.get_group_by_name('Admin')
]]
function Permissions_Groups.get_group_by_name(name)
return Permissions_Groups.groups[name]
end
--[[-- Returns the group that a player is in
@tparam LuaPlayer player the player to get the group of can be name index etc
@treturn ?Permissions_Groups._prototype|nil the group with that player or nil if non found
@usage-- Get your permission group
local group = Groups.get_group_from_player(game.player)
]]
function Permissions_Groups.get_group_from_player(player)
player = Game.get_player_from_any(player)
if not player then return end
local group = player.permission_group
if group then
return Permissions_Groups.groups[group.name]
end
end
--- Setters.
-- Functions that control all groups
-- @section players
--[[-- Reloads/creates all permission groups and sets them to they configured state
@usage-- Reload the permission groups, used internally
Groups.reload_permissions()
]]
function Permissions_Groups.reload_permissions()
for _, group in pairs(Permissions_Groups.groups) do
group:create()
end
end
--[[-- Sets a player's group to the one given, a player can only have one group at a time
@tparam LuaPlayer player the player to effect can be name index etc
@tparam string group the name of the group to give to the player
@treturn boolean true if the player was added successfully, false other wise
@usage-- Set your permission group
Groups.set_player_group(game.player, 'Admin')
]]
function Permissions_Groups.set_player_group(player, group)
player = Game.get_player_from_any(player)
group = Permissions_Groups.get_group_by_name(group)
if not group or not player then return false end
group:add_player(player)
return true
end
--- Actions.
-- Functions that control group actions
-- @section actions
--[[-- Sets the allow state of an action for this group, used internally but is safe to use else where
@tparam ?string|defines.input_action action the action that you want to set the state of
@tparam boolean state the state that you want to set it to, true = allow, false = disallow
@treturn Permissions_Groups._prototype returns self so function can be chained
@usage-- Set an action to be disallowed
group:set_action('toggle_map_editor', false)
]]
function Permissions_Groups._prototype:set_action(action, state)
local input_action = defines.input_action[action]
if input_action == nil then input_action = action end
assert(type(input_action) == 'number', tostring(action)..' is not a valid input action')
self.actions[input_action] = state
return self
end
--[[-- Sets an action or actions to be allowed for this group even with disallow_all triggered, Do not use in runtime
@tparam string|Array<string> actions the action or actions that you want to allow for this group
@treturn Permissions_Groups._prototype returns self so function can be chained
@usage-- Allow some actions
group:allow{
'write_to_console'
}
]]
function Permissions_Groups._prototype:allow(actions)
if type(actions) ~= 'table' then
actions = {actions}
end
for _, action in pairs(actions) do
self:set_action(action, true)
end
return self
end
--[[-- Sets an action or actions to be disallowed for this group even with allow_all triggered, Do not use in runtime
@tparam string|Array<string> actions the action or actions that you want to disallow for this group
@treturn Permissions_Groups._prototype returns self so function can be chained
@usage-- Disalow some actions
group:disallow{
'add_permission_group',
'delete_permission_group',
'import_permissions_string',
'map_editor_action',
'toggle_map_editor'
}
]]
function Permissions_Groups._prototype:disallow(actions)
if type(actions) ~= 'table' then
actions = {actions}
end
for _, action in pairs(actions) do
self:set_action(action, false)
end
return self
end
--[[-- Sets the default state for any actions not given to be allowed, useful with :disallow
@treturn Permissions_Groups._prototype returns self so function can be chained
@usage-- Allow all actions unless given by disallow
group:allow_all()
]]
function Permissions_Groups._prototype:allow_all()
self.allow_all_actions = true
return self
end
--[[-- Sets the default state for any action not given to be disallowed, useful with :allow
@treturn Permissions_Groups._prototype returns self so function can be chained
@usage-- Disallow all actions unless given by allow
group:disallow_all()
]]
function Permissions_Groups._prototype:disallow_all()
self.allow_all_actions = false
return self
end
--[[-- Returns if an input action is allowed for this group
@tparam ?string|defines.input_action action the action that you want to test for
@treturn boolean true if the group is allowed the action, false other wise
@usage-- Test if a group is allowed an action
local allowed = group:is_allowed('write_to_console')
]]
function Permissions_Groups._prototype:is_allowed(action)
if type(action) == 'string' then
action = defines.input_action[action]
end
local state = self.actions[action]
if state == nil then
state = self.allow_all_actions
end
return state
end
--- Players.
-- Functions that control group players
-- @section players
--[[-- Creates or updates the permission group with the configured actions, used internally
@treturn LuaPermissionGroup the permission group that was created
@usage-- Create the permission group so players can be added, used internally
group:create()
]]
function Permissions_Groups._prototype:create()
local group = self:get_raw()
if not group then
group = game.permissions.create_group(self.name)
end
for _, action in pairs(defines.input_action) do
group.set_allows_action(action, self:is_allowed(action))
end
return group
end
--[[-- Returns the LuaPermissionGroup that was created with this group object, used internally
@treturn LuaPermissionGroup the raw lua permission group
@usage-- Get the factorio api permision group, used internally
local permission_group = group:get_raw()
]]
function Permissions_Groups._prototype:get_raw()
return game.permissions.get_group(self.name)
end
--[[-- Adds a player to this group
@tparam LuaPlayer player LuaPlayer the player you want to add to this group can be name or index etc
@treturn boolean true if the player was added successfully, false other wise
@usage-- Add a player to this permission group
group:add_player(game.player)
]]
function Permissions_Groups._prototype:add_player(player)
player = Game.get_player_from_any(player)
local group = self:get_raw()
if not group or not player then return false end
Async(add_to_permission_group, group, player)
return true
end
--[[-- Removes a player from this group
@tparam LuaPlayer player LuaPlayer the player you want to remove from this group can be name or index etc
@treturn boolean true if the player was removed successfully, false other wise
@usage-- Remove a player from this permission group
group:remove_player(game.player)
]]
function Permissions_Groups._prototype:remove_player(player)
player = Game.get_player_from_any(player)
local group = self:get_raw()
if not group or not player then return false end
Async(remove_from_permission_group, group, player)
return true
end
--[[-- Returns all player that are in this group with the option to filter to online/offline only
@tparam[opt] boolean online if nil returns all players, if true online players only, if false returns online players only
@treturn table a table of players that are in this group; filtered if online param is given
@usage-- Get all players in this group
local online_players = group:get_players()
@usage-- Get all online players in this group
local online_players = group:get_players(true)
]]
function Permissions_Groups._prototype:get_players(online)
local players = {}
local group = self:get_raw()
if group then
if online == nil then
return group.players
else
for _, player in pairs(group.players) do
if player.connected == online then
table.insert(player, player)
end
end
end
end
return players
end
--[[-- Prints a message to every player in this group
@tparam string message the message that you want to send to the players
@treturn number the number of players that received the message
@usage-- Print a message to all players in thie group
group:print('Hello, World!')
]]
function Permissions_Groups._prototype:print(message)
local players = self:get_players(true)
for _, player in pairs(players) do
player.print(message)
end
return #players
end
-- when the game starts it will make the permission groups
Event.on_init(function()
Permissions_Groups.reload_permissions()
end)
return Permissions_Groups

View File

@@ -0,0 +1,152 @@
--[[-- Core Module - PlayerData
- A module used to store player data in a central datastore to minimize data requests and saves.
@core PlayerData
@usage-- Adding a colour setting for players
local PlayerData = require 'expcore.player_data'
local PlayerColors = PlayerData.Settings:combine('Color')
-- Set the players color when their data is loaded
PlayerColors:on_load(function(player_name, color)
local player = game.players[player_name]
player.color = color
end)
-- Overwrite the saved color with the players current color
PlayerColors:on_save(function(player_name, _)
local player = game.players[player_name]
return player.color -- overwrite existing data with the current color
end)
@usage-- Add a playtime statistic for players
local Event = require 'utils.event'
local PlayerData = require 'expcore.player_data'
local Playtime = PlayerData.Statistics:combine('Playtime')
-- When playtime reaches an hour interval tell the player and say thanks
Playtime:on_update(function(player_name, playtime)
if playtime % 60 == 0 then
local hours = playtime / 60
local player = game.players[player_name]
player.print('Thanks for playing on our servers, you have played for '..hours..' hours!')
end
end)
-- Update playtime for players, data is only loaded for online players so update_all can be used
Event.add_on_nth_tick(3600, function()
Playtime:update_all(function(player_name, playtime)
return playtime + 1
end)
end)
]]
local Event = require 'utils.event' --- @dep utils.event
local Async = require 'expcore.async' --- @dep expcore.async
local Datastore = require 'expcore.datastore' --- @dep expcore.datastore
local Commands = require 'expcore.commands' --- @dep expcore.commands
require 'config.expcore.command_general_parse' --- @dep config.expcore.command_general_parse
--- Common player data that acts as the root store for player data
local PlayerData = Datastore.connect('PlayerData', true) -- saveToDisk
PlayerData:set_serializer(Datastore.name_serializer) -- use player name
--- Store and enum for the data saving preference
local DataSavingPreference = PlayerData:combine('DataSavingPreference')
local PreferenceEnum = { 'All', 'Statistics', 'Settings', 'Required' }
for k,v in ipairs(PreferenceEnum) do PreferenceEnum[v] = k end
DataSavingPreference:set_default('All')
DataSavingPreference:set_metadata{
name = {'expcore-data.preference'},
tooltip = {'expcore-data.preference-tooltip'},
value_tooltip ={'expcore-data.preference-value-tooltip'}
}
--- Sets your data saving preference
-- @command set-data-preference
Commands.new_command('set-preference', 'Allows you to set your data saving preference')
:add_param('option', false, 'string-options', PreferenceEnum)
:register(function(player, option)
DataSavingPreference:set(player, option)
return {'expcore-data.set-preference', option}
end)
--- Gets your data saving preference
-- @command data-preference
Commands.new_command('preference', 'Shows you what your current data saving preference is')
:register(function(player)
return {'expcore-data.get-preference', DataSavingPreference:get(player)}
end)
--- Gets your data and writes it to a file
Commands.new_command('save-data', 'Writes all your player data to a file on your computer')
:register(function(player)
player.print{'expcore-data.get-data'}
game.write_file('expgaming_player_data.json', game.table_to_json(PlayerData:get(player, {})), false, player.index)
end)
--- Async function called after 5 seconds with no player data loaded
local check_data_loaded = Async.register(function(player)
local player_data = PlayerData:get(player)
if not player_data or not player_data.valid then
player.print{'expcore-data.data-failed'}
Datastore.ingest('request', 'PlayerData', player.name, '{"valid":false}')
end
end)
--- When player data loads tell the player if the load had failed previously
PlayerData:on_load(function(player_name, player_data, existing_data)
if not player_data or player_data.valid == false then return end
if existing_data and existing_data.valid == false then
game.players[player_name].print{'expcore-data.data-restore'}
end
player_data.valid = true
end)
--- Remove data that the player doesnt want to have stored
PlayerData:on_save(function(player_name, player_data)
local dataPreference = DataSavingPreference:get(player_name)
dataPreference = PreferenceEnum[dataPreference]
if dataPreference == PreferenceEnum.All then
player_data.valid = nil
return player_data
end
local saved_player_data = { PlayerRequired = player_data.PlayerRequired, DataSavingPreference = PreferenceEnum[dataPreference] }
if dataPreference <= PreferenceEnum.Settings then saved_player_data.PlayerSettings = player_data.PlayerSettings end
if dataPreference <= PreferenceEnum.Statistics then saved_player_data.PlayerStatistics = player_data.PlayerStatistics end
return saved_player_data
end)
--- Display your data preference when your data loads
DataSavingPreference:on_load(function(player_name, dataPreference)
game.players[player_name].print{'expcore-data.get-preference', dataPreference or DataSavingPreference.default}
end)
--- Load player data when they join
Event.add(defines.events.on_player_joined_game, function(event)
local player = game.players[event.player_index]
Async.wait(300, check_data_loaded, player)
PlayerData:raw_set(player.name)
PlayerData:request(player)
end)
--- Unload player data when they leave
Event.add(defines.events.on_player_left_game, function(event)
local player = game.players[event.player_index]
local player_data = PlayerData:get(player)
if player_data and player_data.valid == true then
PlayerData:unload(player)
else PlayerData:raw_set(player.name) end
end)
----- Module Return -----
return {
All = PlayerData, -- Root for all of a players data
Statistics = PlayerData:combine('Statistics'), -- Common place for stats
Settings = PlayerData:combine('Settings'), -- Common place for settings
Required = PlayerData:combine('Required'), -- Common place for required data
DataSavingPreference = DataSavingPreference, -- Stores what data groups will be saved
PreferenceEnum = PreferenceEnum -- Enum for the allowed options for data saving preference
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
[language]
local-language=Afrikaans

View File

@@ -0,0 +1,2 @@
[language]
local-language=Arabic

View File

@@ -0,0 +1,2 @@
[language]
local-language=Belarusian

View File

@@ -0,0 +1,2 @@
[language]
local-language=Bulgarian

View File

@@ -0,0 +1,2 @@
[language]
local-language=Catalan

View File

@@ -0,0 +1,2 @@
[language]
local-language=Czech

View File

@@ -0,0 +1,2 @@
[language]
local-language=Danish

View File

@@ -0,0 +1,2 @@
[language]
local-language=German

View File

@@ -0,0 +1,2 @@
[language]
local-language=Greek

View File

@@ -0,0 +1,95 @@
[chat-popup]
message=__1__: __2__
ping=You have been mentioned in chat by __1__.
[damage-popup]
player-health=__1__
player-damage=__1__
[links]
discord=https://discord.explosivegaming.nl
website=https://www.explosivegaming.nl
status=https://status.explosivegaming.nl
github=https://github.com/explosivegaming/scenario
patreon=https://www.patreon.com/ExpGaming
[info]
players-online=There are __1__ players online
total-map-time=This map has been on for __1__
discord=Join us on our discord at: https://discord.explosivegaming.nl
website=Please visit our website at: https://www.explosivegaming.nl
status=Want to check if out servers are down? Visit: https://status.explosivegaming.nl
github=Want to help improve our server with some extra features? Help us at: https://github.com/explosivegaming/scenario
patreon=Consider supporting our server at: https://www.patreon.com/ExpGaming
custom-commands=We use custom commands, such as /tag and /me, use /chelp for more info.
read-readme=Make sure you have read the information gui (It can be found through the info mark on the top left)
softmod=We run a softmod on our servers. A softmod is a custom scenario that runs on this server, an example is the player list.
redmew=We don't talk about redmew here; they beat us to 1000 members ;-;
lhd=All trains must be LHD! This is a long standing rule on our servers, please respect this.
[warnings]
received=You received a warning from __1__. You have __2__ warnings. __3__
pre-kick=This is your last warning before you are kicked.
kick=You were kicked for having too many warnings; you may rejoin if you wish.
pre-pre-ban=You are close to receiving a ban; successful ban appeals are unlikely.
pre-ban=This your LAST warning before you are BANNED! Successful ban appeals are unlikely.
ban=You were banned for having too many warnings; visit __1__ to request a ban appeal.
script-warning=You are receiving script warnings; if you recive too many you will receive a permanent warning (__1__/__2__)
script-warning-removed=A script warning has expired (__1__/__2__)
script-warning-limit=__1__ has received a permanent warning from the script.
[chat-bot]
reply=[Chat Bot] __1__
disallow=You can't use global chat commands
players-online=There are __1__ players online
players=There have been __1__ players on this map
not-real-dev=Cooldude2606 is a dev for this server and makes the softmod and is not a factorio dev.
softmod=A softmod is a custom scenario that runs on this server,an example is the player list.
blame=Blame __1__ for what just happened!
afk=You're afk? Look at __1__, that player has been afk for: __2__
current-evolution=Current evolution factor is __1__
magic=Fear the admin magic (ノ゚∀゚)ノ⌒・*:.。. .。.:*・゜゚・*☆
aids=≖ ‿ ≖ Fear the aids of a public server ≖ ‿ ≖
riot=(admins) ┬┴┬┴┤ᵒ_ᵒ)├┬┴┬┴ \(´ω` )/\ (  ´)/\ ( ´ω`)/ (rest of server)
loops=NO LOOPS; LOOPS ARE BAD; JUST NO LOOPS!!!!!; IF YOU MAKE A LOOP.... IT WILL NOT END WELL!!!!!!!
lenny=( ͡° ͜ʖ ͡°)
hodor=Hodor
get-popcorn-1=Heating the oil and waiting for the popping sound...
get-popcorn-2=__1__ your popcorn is finished. Lean backwards and watch the drama unfold.
get-snaps-1=Pouring the glasses and finding the correct song book...
get-snaps-2=Singing a song...🎤🎶
get-snaps-3=schkål, my friends!
get-cocktail-1= 🍸 Inintiating mind reading unit... 🍸
get-cocktail-2= 🍸 Mixing favourite ingredients of __1__ 🍸
get-cocktail-3=🍸 __1__ your cocktail is done.🍸
make-coffee-1= ☕ Boiling the water and grinding the coffee beans... ☕
make-coffee-2= ☕ __1__ we ran out of coffe beans! Have some tea instead. ☕
order-pizza-1= 🍕 Finding nearest pizza supplier... 🍕
order-pizza-2= 🍕 Figuring out the favourite pizza of __1__ 🍕
order-pizza-3= 🍕 __1__ your pizza is here! 🍕
make-tea-1= ☕ Boiling the water... ☕
make-tea-2= ☕ __1__ your tea is done! ☕
get-mead-1= Filling the drinking horn
get-mead-2= Skål!
get-beer-1= 🍺 Pouring A Glass 🍺
get-beer-2= 🍻 Chears Mate 🍻
verify=Please return to our discord and type r!verify __1__
[afk-kick]
message=All players were kicked because everyone was AFK.
[report-jail]
jail=__1__ was jailed because they were reported too many times. Please wait for a moderator.
[protection-jail]
jail=__1__ was jailed because they removed too many protected entities. Please wait for a moderator.
[nukeprotect]
found=You cannot have __1__ in your inventory, so it was placed into the chests at spawn.
[logging]
add-l=[RES] __1__ at level __2__ has been researched
add-n=[RES] __1__ has been researched
[deconlog]
decon=__1__ tried to deconstruct on __2__ from __3__ to __4__ which has __5__ items.

View File

@@ -0,0 +1,245 @@
[expcom-admin-chat]
description=Sends a message in chat that only admins can see.
format=[Admin Chat] __1__: __2__
[expcom-admin-marker]
description=Toggles admin marker mode, new markers can only be edited by admins
exit=You have left admin marker mode, all new markers will not be protected.
enter=You have entered admin marker mode, all new markers will be protected.
place=You have placed an admin marker.
edit=You have edited an admin marker.
revert=You cannot edit admin markers.
[expcom-artillery]
description=Artillery Target Remote
[expcom-bonus]
description=Changes the amount of bonus you receive
set=Your bonus has been set to __1__.
perm=You dont have enough permission to set more than __1__.
[expcom-bot-queue]
description-get=Get bot queue
description-set=Set bot queue
result=__1__ set the bot queue to __2__ successful attempts and __3__ failed attempts
[expcom-cheat]
description-cheat=Toggles cheat mode for your player, or another player.
description-res=Set all research for your force.
description-day=Toggles always day in surface.
res=__1__ has enabled all technologies
day=__1__ set always day to __2__
[expcom-chelp]
description=Searches for a keyword in all commands you are allowed to use.
title=Help results for "__1__":
footer=[__1__ results found: page __2__ of __3__]
format=/__1__ __2__ - __3__ __4__
alias=Alias: __1__
out-of-range=__1__ is an invalid page number.
[expcom-clr-inv]
description=Clears a players inventory
[expcom-connect]
description=Connect to another server
description-player=Send a player to a different server
description-all=Connect all players to another server
too-many-matching=Multiple server were found with the given name: __1__
wrong-version=Servers were found but are on a different version: __1__
same-server=You are already connected to the server: __1__
offline=You cannot connect as the server is currently offline: __1__
none-matching=No servers were found with that name, if you used an address please append true to the end of your command.
[expcom-debug]
description=Opens the debug pannel for viewing tables.
[expcom-enemy]
description-kill=Kill all biters only
description-remove=Remove biters and prevent generation
[expcom-ff]
description=Toggle friendly fire
ff=__1__ set friendly fire to __2__
[expcom-find]
description=Find a player on your map.
[expcom-home]
description-home=Teleports you to your home location
description-home-set=Sets your home location to your current position
description-home-get=Returns your current home location
description-return=Teleports you to previous location
no-home=You have no home set.
no-return=You can't return when home has not yet been used.
home-set=Your home point has been set to x: __1__ y: __2__
return-set=Your return point has been set to x: __1__ y: __2__
home-get=Your home point is at x: __1__ y: __2__
[expcom-interface]
description=Sends an invocation to be ran and returns the result.
[expcom-inv-search]
description-ia=Display players sorted by the quantity of an item held
description-ir=Display players who hold an item sorted by join time
description-i=Display players sorted by the quantity of an item held and playtime
description-io=Display online players sorted by the quantity of an item held and playtime
reject-item=No item was found with internal name __1__; try using rich text selection.
results-heading=Players found with [item=__1__]:
results-item=__1__) __2__ has __3__ items. (__4__)
results-none=No players have [item=__1__]
[expcom-jail]
description-jail=Puts a player into jail and removes all other roles.
description-unjail=Removes a player from jail.
give=__1__ was jailed by __2__. Reason: __3__
remove=__1__ was unjailed by __2__.
already-jailed=__1__ is already in jail.
not-jailed=__1__ is not currently in jail.
[expcom-join-message]
description-msg=Sets your custom join message
description-clr=Clear your join message
[expcom-kill]
description=Kills yourself or another player.
already-dead=You are already dead.
[expcom-lawnmower]
description=Clean up biter corpse, decoratives and nuclear hole
[expcom-lastlocation]
description=Sends you the last location of a player
response=Last location of __1__ was [gps=__2__,__3__]
[expcom-me]
description=Sends an action message in the chat
[expcom-personal-logistics]
description=Set Personal Logistic (-1 to cancel all) (Select spidertron to edit spidertron)
[expcom-pol]
description-clr=Clear pollution
description-off=Disable pollution
clr=__1__ cleared the pollution.
off=__1__ disabled the pollution.
[expcom-protection]
description-pe=Toggles entity protection selection, hold shift to remove protection
description-pa=Toggles area protection selection, hold shift to remove protection
entered-entity-selection=Entered entity selection, select entites to protect, hold shift to remove protection.
entered-area-selection=Entered area selection, select areas to protect, hold shift to remove protection.
protected-entities=__1__ entities have been protected.
unprotected-entities=__1__ entities have been unprotected.
already-protected=This area is already protected.
protected-area=This area is now protected.
unprotected-area=This area is now unprotected.
repeat-offence=__1__ has removed __2__ at [gps=__3__,__4__]
[expcom-quickbar]
description=Saves your quickbar preset items to file
[expcom-rainbow]
description=Sends an rainbow message in the chat
[expcom-ratio]
description=This command will give the input and output ratios of the selected machine. Use the parameter for calculating the machines needed for that amount of items per second.
notSelecting=Please select an entity with a recipe.
item-in=You need __1__ per second of [item=__2__].
fluid-in=You need __1__ per second of [fluid=__2__].
item-out=This will result in: __1__ [item=__2__] per second.
fluid-out=This will result in: __1__ [fluid=__2__] per second.
machines=And you will need __1__ machines (with the same speed as this one) for this.
[expcom-repair]
description=Repairs entities on your force around you
result=__1__ entites were revived and __2__ were healed to max health.
[expcom-report]
description-report=Reports a player and notifies moderators
description-get-reports=Gets a list of all reports that a player has on them. If no player then lists all players and the number of reports on them.
description-clear-reports=Clears all reports from a player or just the report from one player.
player-immune=This player can not be reported.
self-report=You cannot report yourself.
non-admin=__1__ was reported for __2__.
admin=__1__ was reported by __2__ for __3__.
already-reported=You can only report a player once, you can ask a moderator to clear this report.
not-reported=The player had no reports on them.
player-count-title=The following players have reports against them:
player-report-title=__1__ has the following reports against them:
list=__1__: __2__
removed=__1__ has one or more reports removed by __2__.
[expcom-res]
description-ares=Automatically queue up research
res=__1__ set auto research to __2__
msg=[color=255, 255, 255] Research completed at __1__ - [technology=__2__][/color]
inf=[color=255, 255, 255] Research completed at __1__ - [technology=__2__] - __3__[/color]
inf-q=[color=255, 255, 255] Research added to queue - [technology=__1__] - __2__[/color]
res-name=[technology=__1__] __2__
name=Name
target=Target
attempt=Attempt
difference=Diff
main-tooltip=Research GUI
[expcom-roles]
description-assign-role=Assigns a role to a player
description-unassign-role=Unassigns a role from a player
description-list-roles=Lists all roles in they correct order
higher-role=The role you tried to assign is higher than your highest.
list=All roles are: __1__
list-player=__1__ has: __2__
list-element=__1__, __2__
[expcom-server-ups]
no-ext=No external source was found, cannot display server ups.
[expcom-spawn]
description=Teleport to spawn
unavailable=They was a problem getting you to spawn, please try again later.
[expcom-spectate]
description-spectate=Toggles spectator mode
description-follow=Start following a player in spectator
follow-self=You can not follow yourself
[expcom-speed]
description=Set game speed
result=__1__ set the game speed to __2__
[expcom-surface-clearing]
description-ci=Clear Item On Ground
description-cb=Clear Blueprint
[expcom-tag]
description=Sets your player tag.
description-clear=Clears your tag. Or another player if you are admin.
[expcom-tp]
description-tp=Teleports a player to another player.
description-bring=Teleports a player to you.
description-goto=Teleports you to a player.
no-position-found=No position to teleport to was found, please try again later.
to-self=Player can not be teleported to themselves.
[expcom-train]
description=Set All Trains to Automatic
manual-result=__1__ put __2__ trains into automatic mode
[expcom-warnings]
description-give=Gives a warning to a player; may lead to automatic script action.
description-get=Gets the number of warnings a player has. If no player then lists all players and the number of warnings they have.
description-clear=Clears all warnings (and script warnings) from a player
received=__1__ received a warning from __2__ for __3__.
player=__1__ has __2__ warnings and __3__/__4__ script warnings.
player-detail=__1__ gave warning for: __2__
list-title=The following players have this many warnings (and this many script warnings):
list=__1__: __2__ (__3__/__4__)
cleared=__1__ had all their warnings cleared by __2__.
[expcom-waterfill]
description=Change tile to water
waterfill-distance=Too close to designated location
waterfill-cliff=Not enough cliff explosive to create water
entered-area-selection=Entered area selection, select areas to convert.

View File

@@ -0,0 +1,3 @@
[command-auth]
admin-only=This command is for (game) admins only!
command-disabled=This command has been disabled by management!

View File

@@ -0,0 +1,108 @@
[join-message]
greet=[color=0,1,0] Welcome to explosive gaming community server! If you like the server join our discord: __1__ [/color]
message-set=Your join message has been updated.
message-cleared=Your join message has been cleared.
[quickbar]
saved=Your quickbar filters have been saved.
[exp-required]
Warnings=Warnings
Warnings-tooltip=The total number of warnings you have received from staff
Warnings-value-tooltip=The total number of warnings you have received from staff
[exp-settings]
Colour=Colour
Colour-tooltip=Your player colour
Colour-value-tooltip=Change by using /color
JoinMessage=Join Message
JoinMessage-tooltip=The message displayed when you join
JoinMessage-value-tooltip=Change by using /join-message
QuickbarFilters=Quickbar Filters
QuickbarFilters-tooltip=The filters on your quickbar
QuickbarFilters-value-tooltip=Change by using /save-quickbar
UsesAlt=Alt View
UsesAlt-tooltip=Whether you use alt view when you play
UsesAlt-value-tooltip=Change by pressing __CONTROL__show-info__
UsesServerUps=Server UPS
UsesServerUps-tooltip=Whether the current server UPS is shown
UsesServerUps-value-tooltip=Change by using /server-ups
Tag=Player Tag
Tag-tooltip=The tag shown after your name
Tag-value-tooltip=Change by using /tag
TagColor=Player Tag color
TagColor-tooltip=The color of the tag shown after your name
TagColor-value-tooltip=Change by using /tag-color
Bonus=Player Bonus
Bonus-tooltip=The bonus given to your character
Bonus-value-tooltip=Change by using /bonus
HasEnabledDecon=Quick Tree Decon
ToolbarState=Toolbox
ToolbarState-tooltip=The order and favourites in your toolbox
ToolbarState-value-tooltip=This value is calculated automatically when you leave the game
[exp-statistics]
MapsPlayed=Maps Played
MapsPlayed-tooltip=The number of unique maps you have played on
JoinCount=Join Count
JoinCount-tooltip=The number of times you have joined our servers
Playtime=Playtime
Playtime-tooltip=The amount of time you have spent on our servers
AfkTime=AFK Time
AfkTime-tooltip=The amount of time you have been AFK on our servers
ChatMessages=Messages
ChatMessages-tooltip=The number of messages you have sent in chat
CommandsUsed=Commands
CommandsUsed-tooltip=The number of commands you have used
RocketsLaunched=Rockets Launched
RocketsLaunched-tooltip=The number of rockets launched while you were online
ResearchCompleted=Research Completed
ResearchCompleted-tooltip=The number of research projects completed while you were online
MachinesBuilt=Machines Built
MachinesBuilt-tooltip=The number of machines you have built
MachinesRemoved=Machines Removed
MachinesRemoved-tooltip=The number of machines you have removed
TilesBuilt=Tiles Placed
TilesBuilt-tooltip=The number of tiles you have placed
TilesRemoved=Tiles Removed
TilesRemoved-tooltip=The number of tiles you have removed
TreesDestroyed=Trees Destroyed
TreesDestroyed-tooltip=The number of trees you have destroyed
OreMined=Ore Mined
OreMined-tooltip=The amount of ore you have mined
ItemsCrafted=Items Crafted
ItemsCrafted-tooltip=The number of items you have crafted
ItemsPickedUp=Items Picked Up
ItemsPickedUp-tooltip=The number of items you have picked up
Kills=Kills
Kills-tooltip=The number of biters and biter bases you have squished
Deaths=Deaths
Deaths-tooltip=The number of times you have died
DamageDealt=Damage Dealt
DamageDealt-tooltip=The amount of damage you have dealt to other forces
DistanceTravelled=Distance Travelled
DistanceTravelled-tooltip=The total distance in tiles that you have travelled
CapsulesUsed=Capsules Used
CapsulesUsed-tooltip=The number of capsules you have used
EntityRepaired=Machines Repaired
EntityRepaired-tooltip=The number of machines which you have repaired
DeconstructionPlannerUsed=Decon Planner Used
DeconstructionPlannerUsed-tooltip=The number of times you have used the deconstruction planner
MapTagsMade=Map Tags Created
MapTagsMade-tooltip=The number of map tags you have created
DamageDeathRatio=Damage Death Ratio
DamageDeathRatio-tooltip=Damage over Death
KillDeathRatio=Kill Death Ratio
KillDeathRatio-tooltip=Kill over Death
SessionTime=Session Time
SessionTime-tooltip=Session Time
BuildRatio=Build Ratio
BuildRatio-tooltip=Build over Remove
RocketPerHour=Rocket Per Hour
RocketPerHour-tooltip=Rocket Launched Over Play Time in Hour
TreeKillPerMinute=Tree Kill Per Minute
TreeKillPerMinute-tooltip=Tree Destroyed Per Minute
NetPlayTime=Net Play Time
NetPlayTime-tooltip=Net Play Time
AFKTimeRatio=AFK Time Ratio
AFKTimeRatio-tooltip=AFK Time over Play Time

View File

@@ -0,0 +1,53 @@
time-symbol-days-short=__1__d
color-tag=[color=__1__]__2__[/color]
[time-format]
simple-format-tagged=__1__ __2__
simple-format-div=__1__:__2__
[expcore-commands]
unauthorized=Unauthorized, Access is denied due to invalid credentials
reject-string-options=Invalid Option, Must be one of: __1__
reject-string-max-length=Invalid Length, Max: __1__
reject-number=Invalid Number.
reject-number-range=Invalid Range, Min (inclusive): __1__, Max (inclusive): __2__
reject-player=Invalid Player Name, __1__ ,try using tab key to auto-complete the name
reject-player-online=Player is offline.
reject-player-alive=Player is dead.
reject-force=Invalid Force Name.
reject-surface=Invalid Surface Name.
reject-color=Invalid Color Name.
invalid-inputs=Invalid Input, /__1__ __2__
invalid-param=Invalid Param "__1__"; __2__
command-help=__1__ - __2__
command-ran=Command Complete
command-fail=Command failed to run: __1__
command-error-log-format=[ERROR] command/__1__ :: __2__
[expcore-roles]
error-log-format-flag=[ERROR] roleFlag/__1__ :: __2__
error-log-format-assign=[ERROR] rolePromote/__1__ :: __2__
game-message-assign=__1__ has been assigned to __2__ by __3__
game-message-unassign=__1__ has been unassigned from __2__ by __3__
reject-role=Invalid Role Name.
reject-player-role=Player has a higher role.
[gui_util]
button_tooltip=Shows/hides the toolbar.
[expcore-gui]
left-button-tooltip=Hide all open windows.
[expcore-data]
set-preference=You data saving preference has been set to __1__. Existing data will not be effected until you rejoin.
get-preference=You data saving preference is __1__. Use /set-preference to change this. Use /save-data to get a local copy of your data.
get-data=Your player data has been writen to file, location: factorio/script_output/expgaming_player_data.json
data-failed=Your player data has failed to load. Any changes to your data will not be saved.
data-restore=Your player data has been restored and changes will now save when you leave.
preference=Saving Preference
preference-tooltip=Which categories will be saved when you leave the game
preference-value-tooltip=Change by using /set-preference
preference-All=All data will be saved
preference-Statistics=Only statistics, settings, and required data will be saved
preference-Settings=Only settings and required data will be saved
preference-Required=Only required data will be saved

View File

@@ -0,0 +1,321 @@
[player-list]
main-tooltip=Player List
open-action-bar=Options
close-action-bar=Close Options
reason-confirm=Confirm Reason
reason-entry=Enter Reason
goto-player=Goto player
bring-player=Bring player
report-player=Report player
warn-player=Warn player
jail-player=Jail player
kick-player=Kick player
ban-player=Ban player
afk-time=__1__% of total map time\nLast moved __2__ ago
open-map=__1__ __2__\n__3__\nClick to open map
[rocket-info]
main-tooltip=Rocket Info
launch-tooltip=Launch rocket
launch-tooltip-disabled=Launch rocket (not ready)
toggle-rocket-tooltip=Disable auto launch
toggle-rocket-tooltip-disabled=Enable auto launch
toggle-section-tooltip=Expand Section
toggle-section-collapse-tooltip=Collapse Section
section-caption-stats=Statistics
section-tooltip-stats=Statistics about how rockets are launched
section-caption-milestones=Milestones
section-tooltip-milestones=The times when milestones were met
section-caption-progress=Build Progress
section-tooltip-progress=The progress for your rocket silos
progress-no-silos=You have no rocket silos
data-caption-first-launch=First Launch
data-tooltip-first-launch=The time of the first launch
data-caption-last-launch=Last Launch
data-tooltip-last-launch=The time of the last launch
data-caption-fastest-launch=Fastest Launch
data-tooltip-fastest-launch=The time taken for the fastest launch
data-caption-total-rockets=Total Launched
data-tooltip-total-rockets=Total number of rockets launched by your force
value-tooltip-total-rockets=__1__% of all rockets on this map
data-caption-avg-launch=Average Time
data-tooltip-avg-launch=The average amount of time taken to launch a rocket
data-caption-avg-launch-n=Average Time __1__
data-tooltip-avg-launch-n=The average amount of time taken to launch the last __1__ rockets
data-caption-milestone-n=Milestone __1__
data-tooltip-milestone-n=Time taken to each __1__ rockets
data-caption-milestone-next=N/A
data-tooltip-milestone-next=Not yet achieved
progress-x-pos=X __1__
progress-y-pos=Y __1__
progress-label-tooltip=View on map
progress-launched=Launched
progress-caption=__1__%
progress-tooltip=This silo has launched __1__ rockets
launch-failed=Failed to launch rocket, please wait a few seconds and try again.
[science-info]
main-caption=Science Packs
main-tooltip=Science Info
eta-caption=ETA:
eta-tooltip=The estimated time left for the current research
eta-time=T- __1__
unit=__1__ spm
pos-tooltip=Total made: __1__
neg-tooltip=Total used: __1__
net-tooltip=Total net: __1__
no-packs=You have not made any science packs yet
[task-list]
main-caption=Task List [img=info]
main-tooltip=Task List
sub-tooltip=Tasks that remain to be done\n- You can use richtext to include images [img=utility/not_enough_repair_packs_icon] or [color=blue]color[/color] your tasks.
no-tasks=No tasks found!
no-tasks-tooltip=Click on the plus button to the top right of this window to add a new task!
last-edit=Last edited by __1__ at __2__
add-tooltip=Add new task
confirm=Confirm
confirm-tooltip=Save task (minimum of 5 characters long)
discard=Discard
discard-tooltip=Discard task/changes
delete=Delete
delete-tooltip=Delete task
close-tooltip=Close task details
edit=Edit task
edit-tooltip=Currently being edited by: __1__
edit-tooltip-none=Currently being edited by: Nobody
create-footer-header=Create task
edit-footer-header=Edit task
view-footer-header=Task details
[autofill]
main-tooltip=Autofill settings
toggle-section-caption=__1__ __2__
toggle-section-tooltip=Expand Section
toggle-section-collapse-tooltip=Collapse Section
toggle-entity-tooltip=Toggle the autofill of __1__
toggle-tooltip=Toggle the autofill of __1__ into __2__ slots
amount-tooltip=Amount of items to insert into the __1__ slots
invalid=Autofill set to maximum amount: __1__ __2__ for __3__
inserted=Inserted __1__ __2__ into __3__
[warp-list]
main-caption=Warp List [img=info]
main-tooltip=Warp List
sub-tooltip=Warps can only be used every __1__ seconds and when within __2__ tiles\n__3__\n__4__\n__5__\n__6__\n__7__\n__8__
sub-tooltip-current= - __1__ This is your current warp point;
sub-tooltip-connected= - __1__ You can travel to this warp point;
sub-tooltip-different= - __1__ This warp is on a different network;
sub-tooltip-cooldown= - __1__ You are currently on cooldown;
sub-tooltip-not_available= - __1__ You are not in range of a warp point;
sub-tooltip-bypass= - __1__ Your role allows you to travel here;
too-close=Cannot create warp; too close to existing warp point: __1__
too-close-to-water=Cannot create warp; too close to water, please move __1__ tiles away from the water body or landfill it
too-close-to-entities=Cannot create warp; too close to other entities, please move __1__ tiles away from the entities or remove them
last-edit=Last edited by __1__ at __2__\nClick to view on map
add-tooltip=Add new warp
confirm-tooltip=Save changes
cancel-tooltip=Discard changes
edit-tooltip=Currently being edited by: __1__
edit-tooltip-none=Currently being edited by: Nobody
remove-tooltip=Remove warp
timer-tooltip=Please wait __1__ seconds before you can warp
timer-tooltip-zero=After each warp you will need to wait __1__ seconds before you can warp again
goto-tooltip=Go to x __1__ y __2__
goto-bypass=Go to x __1__ y __2__, bypass mode
goto-bypass-different-network=Go to x __1__ y __2__, bypass mode different network
goto-same-warp=You are already on this warp
goto-different-network=This warp is on a different network, use power poles to connect it
goto-cooldown=You are on cooldown, wait for your cooldown to recharge
goto-disabled=You are not on a warp point, walk to a warp point
goto-edit=Edit warp icon
[readme]
main-tooltip=Infomation
welcome-tab=Welcome
welcome-tooltip=Welcome to Explosive Gaming
welcome-general=Welcome to Explosive Gaming; we host many factorio servers. While you are here, we ask you to follow our rules. You can find these in the tab above. You can also find our custom commands and links to our other servers. This map has been online for __2__.\nPlease note that our servers reset periodically, the next reset is: __1__
welcome-roles=We run a custom role system to help protect the work of others. As a result you may not be able to use your deconstruction planner yet or drop item on the groud. Roles also give you access to some custom features such as adding tasks to our task list or making new warp points.\nYou have been assigned the roles: __1__
welcome-chat=Chatting can be difficult for new players because its different than other games! Its very simple, the button you need to press is the “GRAVE/TILDE” key (which is located under the “ESC key”) - If you would like to change the key, go to your Controls tab in options.\nThe setting you need to change is “Toggle chat (and Lua console)” you currently have it set to "__CONTROL__toggle-console__"
rules-tab=Rules
rules-tooltip=Rules for our server
rules-general=By playing on our servers you accept the rules below. If you are supected of breaking a rule then you will be questioned by one of our moderators. If required you will be banned from our servers, appeals can be made by contacting an Administrator on our discord.
rules-1=Hacking / cheating / abusing bugs will not be tolerated.
rules-2=Any bugs or exploits found should be reported to our staff members.
rules-3=Be respectful to other players in chat, this includes any other forms of communication.
rules-4=Taking all items from a belt or logistics network is forbidden: sharing resources is mandatory.
rules-5=Spamming of chat, bots, unlimited chests, or concrete is not allowed.
rules-6=Before removing major parts of the factory tell other players why you are removing it.
rules-7=Do not cause unnecessary lag by placing/removing tiles with bots or using active provider chests, if think some thing might cause lag please ask staff first.
rules-8=Do not walk in random directions with no reason, this is to reduce map download times.
rules-9=Do not use speakers on global or with alerts without prior permission, no one wants to hear your music at full volume without knowing where the off switch is.
rules-10=Do not rotate belts, deactivate belts with wires, or cause production to stop without good reason, this goes for inserters and spliters as well.
rules-11=Do not make train roundabouts. Other loops such as RoRo stations are allowed.
rules-12=When using trains, use the same size other players have used, many players use 1-2-1, 2-4-2, or 3-8-3, meaning 1 engine 2 cargo 1 engine.
rules-13=Trains are Left Hand Drive (LHD) only, this means trains drive on the left side of the tracks.
rules-14=Do not beg for roles, advertise other servers, or link to harmful sites.
rules-15=Use common sense, report rule breakers, and Administrators have the final word.
commands-tab=Commands
commands-tooltip=Commands which you are able to use
commands-general=We have lots of custom commands which you are able to use. Below you can find a list of all the commands that you are allowed to use and what they do. If you need more information or want to search for a command you can use our /search-help command.
servers-tab=Servers
servers-tooltip=Links to our other servers and sites
servers-general=This is only one of our servers for factorio, we host many of others as well. Below you can find details about all the servers that we host as well as links to our external services such as discord or github.
servers-factorio=Factorio Servers
servers-1=S1 Public
servers-d1=This is our 48 hour reset server.
servers-2=S2 Off
servers-d2=This server is closed.
servers-3=S3 Weekly
servers-d3=This is our weekly reset server.
servers-4=S4 Monthly
servers-d4=This is our monthly reset server.
servers-5=S5 Modded
servers-d5=This is our modded server, see discord for details.
servers-6=S6 Donator
servers-d6=This is our donator only server, online when requested.
servers-7=S7 Event
servers-d7=This is our event server, we try to run events at least once per week.
servers-8=S8 T̷-̶s̶-̴:̷
servers-d8=N̵o̴ ̶o̷-̶e̵ ̴k̸n̷-̶w̵s̸ ̴w̷h̷a̶-̶ ̷h̴a̴p̷p̴e̷n̷s̸ ̷o̶n̴ ̷t̶h̴-̶s̶ ̷s̷e̶r̸v̸e̴r̷,̶ ̸i̸t̴ ̷m̶-̸g̴h̶t̷ ̸n̸-̶t̵ ̷e̴v̸e̸n̶t̷ ̵-̷x̴i̵s̶t̸.̸
servers-connect-Offline=Server is currently offline
servers-connect-Current=This is your current server
servers-connect-Version=Server is on a different version: __1__
servers-connect-Password=Server requires a password
servers-connect-Modded=Server requires mods to be downloaded
servers-connect-Online=Server is online
servers-external=External Links
servers-open-in-browser=Open in your browser
backers-tab=Backers
backers-tooltip=People who have helped make our server
backers-general=We would like to thank all our staff and backers who have helped our community grow. Our staff have helped to keep our servers safe from trolls and a fun place to play. Our backers have helped us to cover our running costs and provide a great community for us all to enjoy together.
backers-management=Administrators
backers-board=Board Members and Senior Backers
backers-staff=Staff Members
backers-backers=Sponsors and Supporters
backers-active=Active Players
data-tab=Data
data-tooltip=All of your stored player data
data-general=Our servers will save your player data so that we can sync it between servers. All of your data can be found below, if you wish to save a copy locally then use /save-data. If you want to change what data is saved then use /set-preference.
data-settings=Settings
data-statistics=Statistics
data-required=Required
data-misc=Miscellaneous
data-format=__1____2__
[tree-decon]
main-tooltip=Toggle fast tree decon
enabled=enabled
disabled=disabled
toggle-msg=Fast decon has been __1__
[bonus]
main-tooltip=Bonus
control-pts-a=Points available
control-pts-n=Points needed
control-pts-r=Points remaining
control-reset=Reset
control-apply=Apply
control-pts-exceed=Points allocated exceeded allowance
display-cmms=Mining
display-cmms-tooltip=Character manual mining speed
display-crs=Running
display-crs-tooltip=Character running speed
display-ccs=Crafting
display-ccs-tooltip=Character crafting speed
display-cisb=Inventory
display-cisb-tooltip=Character inventory slots bonus
display-chb=Health
display-chb-tooltip=Character health bonus
display-crdb=Reach
display-crdb-tooltip=Character reach distance bonus
display-personal-battery-recharge=Battery
display-personal-battery-recharge-tooltip=Player battery recharge
display-fmms=Mining
display-fmms-tooltip=Force manual mining speed
display-frs=Running
display-frs-tooltip=Force running speed
display-fcs=Crafting
display-fcs-tooltip=Force crafting speed
display-fisb=Inventory
display-fisb-tooltip=Force inventory slots bonus
display-fhb=Health
display-fhb-tooltip=Force health bonus
display-frdb=Reach
display-frdb-tooltip=Force reach distance bonus
display-fwrsm=Robot Speed
display-fwrsm-tooltip=Force worker robots speed modifier
display-fwrbm=Robot Battery
display-fwrbm-tooltip=Force worker robots battery modifier
display-fwrsb=Robot Storage
display-fwrsb-tooltip=Force worker robots storage bonus
display-ffrlm=Robot Lifetime
display-ffrlm-tooltip=Force following robots lifetime modifier
[vlayer]
main-tooltip=Vlayer GUI
description-pbr=Recharge Player Battery upto a portion with vlayer
description-vi=Vlayer Info
pbr-not-running=vlayer need to be running
display-item-solar=[img=entity/solar-panel] Solar Panel
display-item-accumulator=[img=entity/accumulator] Accumulator
display-current-production=[virtual-signal=signal-P] Current Production
display-sustained-production=[virtual-signal=signal-S] Sustained Production
display-max-capacity=[virtual-signal=signal-C] Battery Capacity
display-current-capacity=[virtual-signal=signal-E] Battery Charge
display-remaining-surface-area=[virtual-signal=signal-R] Remaining Surface
display-item-solar-tooltip=The amount of Solar Panel
display-item-accumulator-tooltip=The amount of Accumulator
display-current-production-tooltip=Current Power Production
display-sustained-production-tooltip=Sustained Power Production
display-max-capacity-tooltip=Battery Max Capacity
display-current-capacity-tooltip=Battery Current Charge
display-remaining-surface-area-tooltip=Remaining Surface. Insert Landfill to increase
steel-chest-detect=No steel chest detected
steel-chest-empty=Chest is not emptied
control-refresh=Refresh List
control-see=See Interface
control-build=Build Interface
control-remove=Remove Interface
result-build=built
result-remove=removed
result-interface-location=__1__ interface was located at __2__
interface-result=__1__ __2__ __4__ interface at __3__
result-unable=Unable to build __1__ interface, because of __2__
result-multiple=Multiple steel chest detected
result-limit=Max limit exceeded
result-space=Not enough space
control-type-energy=Energy
control-type-circuit=Circuit
control-type-storage-input=Storage Input
control-type-storage-output=Storage Output
[module]
main-tooltip=Module GUI
[landfill]
main-tooltip=Blueprint Landfill GUI
cursor-none=You need to hold the blueprint in cursor
[production]
main-tooltip=Production GUI
label-prod=Production
label-con=Consumption
label-bal=Balance (sec)
[surveillance]
main-tooltip=Surveillance GUI
status-enable=Enable
status-disable=Disable
func-set=Set
type-player=Player
type-static=Static
type-player-loop=Player loop
[toolbar]
main-caption=Toolbox
main-tooltip=Toolbox Settings\nUse the checkboxs to select facourites
reset=Reset All
toggle=Toggle Favourites
move-up=Move Up
move-down=Move Down

View File

@@ -0,0 +1,2 @@
[language]
local-language=English

View File

@@ -0,0 +1,2 @@
[language]
local-language=Esperanto

View File

@@ -0,0 +1,2 @@
[language]
local-language=Spanish (Spain)

View File

@@ -0,0 +1,2 @@
[language]
local-language=Estonian

View File

@@ -0,0 +1,2 @@
[language]
local-language=Basque

View File

@@ -0,0 +1,2 @@
[language]
local-language=Persian

View File

@@ -0,0 +1,2 @@
[language]
local-language=Finnish

View File

@@ -0,0 +1,2 @@
[language]
local-language=Filipino

View File

@@ -0,0 +1,2 @@
[language]
local-language=French

View File

@@ -0,0 +1,2 @@
[language]
local-language=Frysian (Netherlands)

View File

@@ -0,0 +1,2 @@
[language]
local-language=Irish (Ireland)

View File

@@ -0,0 +1,2 @@
[language]
local-language=Hebrew

View File

@@ -0,0 +1,2 @@
[language]
local-language=Croatian

View File

@@ -0,0 +1,2 @@
[language]
local-language=Hungarian

View File

@@ -0,0 +1,2 @@
[language]
local-language=Indonesian

View File

@@ -0,0 +1,2 @@
[language]
local-language=Icelandic

View File

@@ -0,0 +1,2 @@
[language]
local-language=Italian

View File

@@ -0,0 +1,2 @@
[language]
local-language=Japanese

View File

@@ -0,0 +1,2 @@
[language]
local-language=Georgian

View File

@@ -0,0 +1,2 @@
[language]
local-language=Kazakh

View File

@@ -0,0 +1,2 @@
[language]
local-language=Korean

View File

@@ -0,0 +1,2 @@
[language]
local-language=Lithuanian

View File

@@ -0,0 +1,2 @@
[language]
local-language=Latvian

Some files were not shown because too many files have changed in this diff Show More