Moved All Code Out Of Locale

This commit is contained in:
Cooldude2606
2017-12-16 00:33:27 +00:00
parent 4f12463c73
commit 592761c4af
17 changed files with 4 additions and 4 deletions

View File

@@ -1,69 +0,0 @@
--[[
Explosive Gaming
This file can be used with permission but this and the credit below must remain in the file.
Contact a member of management on our discord to seek permission to use our code.
Any changes that you may make to the code are yours but that does not make the script yours.
Discord: https://discord.gg/r6dC2uK
]]
--Please Only Edit Below This Line-----------------------------------------------------------
--[[
How to use groups:
name the name that you can use to refence it.
disallow if present then all ranks in this group will have this added to their disallow.
allow if present then all ranks in this group will have this added to their allow.
highest is asigned by the script to show the highest rank in this group.
lowest is asigned by the script to show the lowest rank in this group.
How to add ranks:
Name is what will be used in the scripts and is often the best choice for display in text.
short_hand is what can be used when short on space but the rank still need to be displayed.
tag is the tag the player will gain when moved to the rank, it can be nil.
time is used for auto-rank feature where you are moved to the rank after a certain play time in minutes.
colour is the RGB value that can be used to emphasise GUI elements based on rank.
power is asigned by the script based on their index in ranks, you can insert new ranks between current ones.
group is asigned by the script to show the group this rank is in
disallow is a list containing input actions that the user can not perform.
allow is a list of custom commands and effects that that rank can use, all defined in the sctips.
For allow, add the allow as the key and the value as true
Example: test for 'server-interface' => allow['server-interface'] = true
For disallow, add to the list the end part of the input action
Example: defines.input_action.drop_item -> 'drop_item'
http://lua-api.factorio.com/latest/defines.html#defines.input_action
--]]
-- see ExpCore/ranks.lua for examples - you add your own and edit pre-made ones here.
local groups = Ranking._groups(true)
local ranks = Ranking._ranks(true)
groups['Root']:edit('allow',false,{
['testing']=true
})
ranks['Root']:edit('test',true,'testing')
groups['User']:add_rank{
name='Veteran',
short_hand='Vet',
tag='[Veteran]',
time=600,
colour={r=140,g=120,b=200},
power=8
}
groups['User']:add_rank{
name='Regular',
short_hand='Reg',
tag='[Regular]',
time=180,
colour={r=24,g=172,b=188},
power=10
}
Ranking._base_preset{
['badgamernl']='Owner',
['arty714']='Community Manager',
['cooldude2606']='Developer',
['mark9064']='Admin'
}

View File

@@ -1,280 +0,0 @@
--[[
Explosive Gaming
This file can be used with permission but this and the credit below must remain in the file.
Contact a member of management on our discord to seek permission to use our code.
Any changes that you may make to the code are yours but that does not make the script yours.
Discord: https://discord.gg/r6dC2uK
]]
local inputs = {}
inputs._input = {}
-- these are just so you can have short cuts to this
inputs.events = {
error='error',
state=defines.events.on_gui_checked_state_changed,
click=defines.events.on_gui_click,
elem=defines.events.on_gui_elem_changed,
selection=defines.events.on_gui_selection_state_changed,
text=defines.events.on_gui_text_changed,
slider=defines.events.on_gui_value_changed
}
--- Sets the input to trigger on an certain event
-- @usage button:on_event(defines.events.on_gui_click,player_return)
-- @param event the event to raise callback on | can be number of the event | can be a key of inputs.events
-- @tparam function callback the function you want to run on the event
-- @treturn table returns self so you can chain together
function inputs._input:on_event(event,callback)
if not is_type(callback,'function') then return self end
if inputs.events[event] then event = inputs.events[event] end
if event == 'error' then self._error = callback return self end
self.events[event] = callback
return self
end
--- Draw the input into the root element
-- @usage button:draw(frame)
-- @param root the element you want to add the input to
-- @return returns the element that was added
function inputs._input:draw(root)
if is_type(self.draw_data.caption,'string') and game.player.gui.is_valid_sprite_path(self.draw_data.caption) then
local data = table.deepcopy(self.draw_data)
data.type = 'sprite-button'
return root.add(data)
elseif is_type(self.draw_data.sprite,'string') and game.player.gui.is_valid_sprite_path(self.draw_data.sprite) then
local data = table.deepcopy(self.draw_data)
data.type = 'sprite-button'
return root.add(data)
elseif is_type(self.data._state,'function') then
local data = table.deepcopy(self.draw_data)
local success, err = pcall(self.data._state,root)
if success then data.state = err else error(err) end
return root.add(data)
else
return root.add(self.draw_data)
end
end
--- Add a new input, this is the same as doing frame.add{} but returns a diffrent object
-- @usage Gui.inputs.add{type='button',name='test',caption='Test'}
-- @tparam table obj the new element to add if caption is a sprite path then sprite is used
-- @treturn table the custom input object
function inputs.add(obj)
if not is_type(obj,'table') then return end
if not is_type(obj.type,'string') then return end
local type = obj.type
if type == 'button' or
type == 'sprite-button' or
type == 'choose-elem-button' or
type == 'checkbox' or
type == 'radiobutton' or
type == 'textfield' or
type == 'text-box' or
type == 'slider'
then else return end
if obj.type == 'button' or obj.type == 'sprite-button' then obj.style = mod_gui.button_style end
obj.draw_data = table.deepcopy(obj)
obj.data = {}
obj.events = {}
setmetatable(obj,{__index=inputs._input})
Gui._add_data('inputs_'..type,obj.name,obj)
return obj
end
-- this just runs the events given to inputs
function inputs._event_handler(event)
local elements = Gui._get_data('inputs_'..event.element.type) or {}
local element = elements[event.element.name]
if not element and event.element.type == 'sprite-button' then
elements = Gui._get_data('inputs_button') or {}
element = elements[event.element.name]
end
if element then
if not is_type(element.events[event.name],'function') then return end
local success, err = pcall(element.events[event.name],event)
if not success then
if is_type(element._error,'function') then pcall(element._error)
else error(err) end
end
end
end
Event.register(inputs.events.state,inputs._event_handler)
Event.register(inputs.events.click,inputs._event_handler)
Event.register(inputs.events.elem,inputs._event_handler)
Event.register(inputs.events.state,inputs._event_handler)
Event.register(inputs.events.text,inputs._event_handler)
-- the folwing functions are just to make inputs easier but if what you want is not include use inputs.add(obj)
--- Used to define a button, can have many function
-- @usage Gui.inputs.add_button('test','Test','Just for testing',{{condition,callback},...})
-- @tparam string name the name of this button
-- @tparam string the display for this button, either text or sprite path
-- @tparam string tooltip the tooltip to show on the button
-- @param callbacks can either be a single function or a list of function pairs see exaplmes at bottom
-- @treturn table the button object that was made, to allow a custom error event if wanted
function inputs.add_button(name,display,tooltip,callbacks)
local button = inputs.add{
type='button',
name=name,
caption=display,
tooltip=tooltip
}
button.data._callbacks = callbacks
button:on_event('click',function(event)
local player = Game.get_player(event)
local mouse = event.button
local keys = {alt=event.alt,ctrl=event.control,shift=event.shift}
local element = event.element
local callbacks = button.data._callbacks
if is_type(callbacks,'function') then callbacks = {function(...) return true end,callbacks} end
for _,data in pairs(callbacks) do
if is_type(data[1],'function') and is_type(data[2],'function') then
local success, err = pcall(data[1],player,mouse,keys,event)
if success and err == true then
local success, err = pcall(data[2],player,element,event)
if not success then error(err) end
elseif not success then error(err) end
else error('Invalid Callback Condition Format') end
end
end)
return button
end
--- Used to define a choose-elem-button callback only on elem_changed
-- @usage Gui.inputs.add_elem_button('test','Test','Just for testing',function)
-- @tparam string name the name of this button
-- @tparam string the display for this button, either text or sprite path
-- @tparam string tooltip the tooltip to show on the button
-- @tparam function the callback to call on change function(player,element,elem)
-- @treturn table the button object that was made, to allow a custom error event if wanted
function inputs.add_elem_button(name,elem_type,tooltip,callback)
local button = inputs.add{
type='choose-elem-button',
name=name,
elem_type=elem_type,
tooltip=tooltip
}
button.data._callback = callback
button:on_event('elem',function(event)
local player = Game.get_player(event)
local element = event.element or {elem_type=nil,elem_value=nil}
local elem = {type=element.elem_type,value=element.elem_value}
if is_type(button.data._callback,'function') then
local success, err = pcall(button.data._callback,player,element,elem)
if not success then error(err) end
else error('Invalid Callback') end
end)
return button
end
--- Used to define a checkbox callback only on state_changed
-- @usage Gui.inputs.add_checkbox('test',false,'Just for testing',function,function,funvtion)
-- @tparam string name the name of this button
-- @tparam string the display for this button, either text or sprite path
-- @tparam string tooltip the tooltip to show on the button
-- @tparam function the callback to call on change function(player,element,elem)
-- @treturn table the button object that was made, to allow a custom error event if wanted
function inputs.add_checkbox(name,radio,display,default,callback_true,callback_false)
local type = 'checkbox'; if radio then type='radiobutton' end
local state = false; if is_type(default,'boolean') then state = default end
local checkbox = inputs.add{
type=type,
name=name,
caption=display,
state=state
}
if is_type(default,'function') then checkbox.data._state = default end
checkbox.data._true = callback_true
checkbox.data._false = callback_false
checkbox:on_event('state',function(event)
local player = Game.get_player(event)
local state = event.element.state
if state then
if is_type(checkbox.data._true,'function') then
local success, err = pcall(checkbox.data._true,player,event.element)
if not success then error(err) end
else error('Invalid Callback') end
else
if is_type(checkbox.data._false,'function') then
local success, err = pcall(checkbox.data._false,player,event.element)
if not success then error(err) end
else error('Invalid Callback') end
end
end)
return checkbox
end
--- Used to reset the state of radio buttons, recomened to be called on_state_change to reset any radio buttons it is ment to work with.
-- @usage Gui.inputs.reset_radio{radio1,radio2,...}
-- @param elements can be a list of elements or a single element
function inputs.reset_radio(elements)
if #elements > 0 then
for _,element in pairs(elements) do
if element.valid then
local _elements = Gui._get_data('inputs_'..element.type) or {}
local _element = _elements[element.name]
local state = false
local success, err = pcall(_element.data._state,element.parent)
if success then state = err else error(err) end
element.state = state
end
end
else
if elements.valid then
local _elements = Gui._get_data('inputs_'..elements.type) or {}
local _element = _elements[elements.name]
local state = false
local success, err = pcall(_element.data._state,elements.parent)
if success then state = err else error(err) end
element.state = state
end
end
end
return inputs
--[[
Input Example
-- Basic Button Using Gui.inputs.add
local test = Gui.inputs.add{
name='test-button',
type='button',
caption='Test'
}
test:on_event(Gui.inputs.events.click,function(event) game.print('test') end)
-- then later in code
local frame = player.gui.top.add{name='test',type='frame'}
test:draw(frame)
-- Mutly Function Button Using Gui.inputs.add_button
Gui.inputs.add_button('test-inputs','Try RMB','alt,ctrl,shift and mouse buttons',{
{
function(player,mouse,keys) return mouse == defines.mouse_button_type.left and keys.alt end,
function(player,element) player_return('Left: Alt',nil,player) end
},
{
function(player,mouse,keys) return mouse == defines.mouse_button_type.left and keys.ctrl end,
function(player,element) player_return('Left: Ctrl',nil,player) end
},
{
function(player,mouse,keys) return mouse == defines.mouse_button_type.left and keys.shift end,
function(player,element) player_return('Left: Shift',nil,player) end
},
{
function(player,mouse,keys) return mouse == defines.mouse_button_type.right and keys.alt end,
function(player,element) player_return('Right: Alt',nil,player) end
},
{
function(player,mouse,keys) return mouse == defines.mouse_button_type.right and keys.ctrl end,
function(player,element) player_return('Right: Ctrl',nil,player) end
},
{
function(player,mouse,keys) return mouse == defines.mouse_button_type.right and keys.shift end,
function(player,element) player_return('Right: Shift',nil,player) end
}
}):on_event('error',function(err) game.print('this is error handliling') end)
]]

View File

@@ -1,48 +0,0 @@
--[[
Explosive Gaming
This file can be used with permission but this and the credit below must remain in the file.
Contact a member of management on our discord to seek permission to use our code.
Any changes that you may make to the code are yours but that does not make the script yours.
Discord: https://discord.gg/r6dC2uK
]]
local toolbar = {}
--- Add a button to the toolbar, ranks need to be allowed to use these buttons if ranks is preset
-- @usage toolbar.add('foo','Foo','Test',function() game.print('test') end)
-- @tparam string name the name of the button
-- @tparam string caption can be a sprite path or text to show
-- @tparma string tooltip the help to show for the button
-- @tparam function callback the function which is called on_click
-- @treturn table the button object that was made
function toolbar.add(name,caption,tooltip,callback)
local button = Gui.inputs.add{type='button',name=name,caption=caption,tooltip=tooltip}
button:on_event(Gui.inputs.events.click,callback)
Gui._add_data('toolbar',name,button)
return button
end
--- Draws the toolbar for a certain player
-- @usage toolbar.draw(1)
-- @param player the player to draw the tool bar of
function toolbar.draw(player)
local player = Game.get_player(player)
if not player then return end
local toolbar_frame = mod_gui.get_button_flow(player)
toolbar_frame.clear()
for name,button in pairs(Gui._get_data('toolbar')) do
if is_type(Ranking,'table') and Ranking._presets and Ranking._presets().meta.rank_count > 0 then
local rank = Ranking.get_rank(player)
if rank:allowed(name) then
button:draw(toolbar_frame)
end
else button:draw(toolbar_frame) end
end
end
if defines.events.rank_change then
Event.register(defines.events.rank_change,toolbar.draw)
end
return toolbar

View File

@@ -1,139 +0,0 @@
--[[
Explosive Gaming
This file can be used with permission but this and the credit below must remain in the file.
Contact a member of management on our discord to seek permission to use our code.
Any changes that you may make to the code are yours but that does not make the script yours.
Discord: https://discord.gg/r6dC2uK
]]
--Please Only Edit Below This Line-----------------------------------------------------------
local command_calls = {}
local command_data = {}
--- Uses a commands data to return the inputs as a string
-- @usage command = command_data[command_name]
-- command_inputs(command) -- returns "<input1> <input2> "
-- @tparam table the data for the command being run
-- @treturn string the inputs in string format
local function command_inputs(command)
if not is_type(command,'table') then return end
local inputs = ''
for _,input in pairs(command.inputs) do
if input == true then break end
inputs = inputs..'<'..input..'> '
end
return inputs
end
--- Uses the command data and the event to return a table of the args
-- @usage command = command_data[command_name]
-- command_args(event,command) -- return {input1='one',input2='two'}
-- @tparam defines.events.on_console_command event the event rasied by the command
-- @tparam table command the data for the command being run
-- @treturn table a table version of the event.parameter based on expected inputs
local function command_args(event,command)
if not event or not is_type(command,'table') then return end
local args = {}
-- haddles no parameters given
if not event.parameter then
if #command.inputs > 0 then return args, false
else return args, true
end
end
-- finds all the words and cheaks if the right number were given
local words = string.split(event.parameter,' ')
if table.last(command.inputs) == true then
if #words < #command.inputs-1 then return args, false end
else
if #words < #command.inputs then return args, false end
end
-- if it is the right number then process and return the args
for index,input in pairs(command.inputs) do
if command.inputs[index+1] == true then
args[input] = table.concat(words,' ',index)
break
else
args[input] = words[index]
end
end
return args, true
end
--- Used to return all the commands a player can use
-- @usage get_commands(1) -- return {{command data},{command data}}
-- @param player the player refreced by string|number|LuaPlayer|event
-- @treturn table a table containg all the commands the player can use
function get_commands(player)
local commands = {}
local player = Game.get_player(player)
if not player then return commands end
local rank = Ranking.get_rank(player)
for name,data in pairs(command_data) do
if Ranking.rank_allowed(rank,name) then table.insert(commands,data) end
end
return commands
end
--- Used to call the custom commands
-- @usage You dont its an internal command
-- @tparam defines.events.on_console_command event the event rasied by the command=
local function run_custom_command(command)
local command_data = command_data[command.name]
local player_name = Game.get_player(command) and Game.get_player(command).name or 'server'
-- is the player allowed to use this command
if is_type(Ranking,'table') and Ranking._presets and Ranking._presets().meta.rank_count > 0 and not Ranking.get_rank(player_name):allowed(command.name) then
player_return({'commands.unauthorized'},defines.text_color.crit)
if game.player then game.player.play_sound{path='utility/cannot_build'} end
game.write_file('commands.log','\n'..game.tick
..' Player: '..player_name
..' Failed to use command (Unauthorized): '..command.name
..' With args of: '..table.to_string(command_args(command,command_data))
, true, 0)
return
end
-- gets the args for the command
local args, valid = command_args(command,command_data)
if not valid then
player_return({'commands.invalid-inputs',command.name,command_inputs(command_data)},defines.text_color.high)
if game.player then game.player.play_sound{path='utility/deconstruct_big'} end
game.write_file('commands.log','\n'..game.tick
..' Player: '..player_name
..' Failed to use command (Invalid Args): '..command.name
..' With args of: '..table.to_string(args)
, true, 0)
return
end
-- runs the command
local success, err = pcall(command_calls[command.name],event,args)
if err then error(err) end
player_return({'commands.command-ran'},defines.text_color.info)
game.write_file('commands.log','\n'..game.tick
..' Player: '..player_name
..' Used command: '..command.name
..' With args of: '..table.to_string(args)
, true, 0)
end
commands._add_command = commands.add_command
commands._expgaming = true
--- Used to define commands
-- @usage inputs = {'player','reason',true}
-- commands.add_command('ban','bans a player',inputs,function() return end)
-- @tparam string name the name of the command
-- @tparam[opt='No Description'] string description the description of the command
-- @tparam[opt={'parameter',true}] table inputs a table of the inputs to be used, last index being true makes the last parameter open ended (longer than one word)
-- @tparam function event the function to call on the event
commands.add_command = function(name, description, inputs, event)
if command_calls[name] then return end
if not is_type(name,'string') then return end
if not is_type(event,'function') then return end
local description = is_type(description,'string') and description or 'No Description'
local inputs = is_type(inputs,'table') and inputs or {'parameter',true}
command_data[name] = {
name=name,
description=description,
inputs=inputs
}
command_calls[name] = event
commands._add_command(name,command_inputs(command_data[name])..description,run_custom_command)
end

View File

@@ -1,28 +0,0 @@
--[[
Explosive Gaming
This file can be used with permission but this and the credit below must remain in the file.
Contact a member of management on our discord to seek permission to use our code.
Any changes that you may make to the code are yours but that does not make the script yours.
Discord: https://discord.gg/r6dC2uK
]]
local Gui = {}
local Gui_data = {}
-- this is to enforce the read only propetry of the gui
function Gui._add_data(key,value_key,value)
if game then return end
if not Gui_data[key] then Gui_data[key] = {} end
Gui_data[key][value_key] = value
end
function Gui._get_data(key) return Gui_data[key] end
function Gui:_load_parts(parts)
for _,part in pairs(parts) do
self[part] = require('/GuiParts/'..part)
end
end
return Gui

View File

@@ -1,40 +0,0 @@
--[[
Explosive Gaming
This file can be used with permission but this and the credit below must remain in the file.
Contact a member of management on our discord to seek permission to use our code.
Any changes that you may make to the code are yours but that does not make the script yours.
Discord: https://discord.gg/r6dC2uK
]]
--[[
ExpCore
This file allow you to only require this one file to return the diffent libarys.
This file will return a function which can be used to access only the part you want.
Pass a table with the names of the objects you want and it will be return in that order
]]
local StdExpCoreLib = {}
require '/commands'
StdExpCoreLib.Server = require '/server'
StdExpCoreLib.Ranking = require '/ranking'
StdExpCoreLib.Gui = require '/gui'
StdExpCoreLib.Gui:_load_parts{
'inputs',
'toolbar',
--'center',
--'left',
--'popup'
}
return function(rtn)
local _return = {}
for _,name in pairs(rtn) do
if StdExpCoreLib[name] then
table.insert(_return,StdExpCoreLib[name])
end
end
return unpack(_return)
end

View File

@@ -1,312 +0,0 @@
--[[
Explosive Gaming
This file can be used with permission but this and the credit below must remain in the file.
Contact a member of management on our discord to seek permission to use our code.
Any changes that you may make to the code are yours but that does not make the script yours.
Discord: https://discord.gg/r6dC2uK
]]
--Please Only Edit Below This Line-----------------------------------------------------------
local Ranking = {}
defines.events.rank_change = script.generate_event_name()
Ranking._rank = {}
Ranking._group = {}
-- this function is to avoid errors - see /ranks.lua
function Ranking._ranks(names)
return {}
end
-- this function is to avoid errors - see /ranks.lua
function Ranking._groups(names)
return {}
end
-- this function is to avoid errors - see /ranks.lua
function Ranking._meta()
return {}
end
-- this function is to avoid errors - see addons/playerRanks.lua
function Ranking._base_preset(table)
Ranking._presets().current = table
end
-- this returns a global list
function Ranking._presets()
if not global.exp_core then global.exp_core = {} end
if not global.exp_core.ranking then global.exp_core.ranking = {meta=Ranking._meta(),old={},current={}} end
return global.exp_core.ranking
end
--- Returns a rank object given a player or rank name
-- @usage Ranking.get_rank(game.player)
-- Ranking.get_rank('admin')
-- @param mixed player|player index|player name|rank name|rank|'server'|'root' what rank to get
-- @treturn table the rank that is linked to mixed
function Ranking.get_rank(mixed)
if not mixed then return false end
local ranks = Ranking._ranks(true)
local _return = false
if is_type(mixed,'table') then
if mixed.index then
_return = game.players[mixed.index] and ranks[mixed.permission_group.name] or false
else
_return = mixed.group and mixed or false
end
else
_return = game.players[mixed] and ranks[game.players[mixed].permission_group.name]
or table.autokey(ranks,mixed) and table.autokey(ranks,mixed)
or string.contains(mixed,'server') and Ranking.get_rank(Ranking._presets().meta.root)
or string.contains(mixed,'root') and Ranking.get_rank(Ranking._presets().meta.root)
or false
end
return _return
end
--- Returns the group object used to sort ranks given group name or see Ranking.get_rank
-- @usage Ranking.get_group(game.player)
-- Ranking.get_group('root')
-- @param mixed player|player index|player name|rank name|rank|'server'|'root'|group name|group what group to get
-- @treturn table the group that is linked to mixed
function Ranking.get_group(mixed)
if not mixed then return false end
local groups = Ranking._groups(true)
local rank = Ranking.get_rank(mixed)
return rank and rank.group
or is_type(mixed,'table') and mixed.ranks and mixed
or is_type(mixed,'string') and table.autokey(groups,mixed) and table.autokey(groups,mixed)
or false
end
--- Prints to all rank of greater/lower power of the rank given
-- @usage Ranking.print('admin','We got a grifer')
-- @param rank_base the rank that acts as the cut off point (rank is always included)
-- @param rtn what do you want to return to the players
-- @tparam bolean below if true rank below base are printed to
function Ranking.print(rank_base,rtn,colour,below)
local colour = colour or defines.color.white
local rank_base = Ranking.get_rank(rank_base)
local ranks = Ranking._ranks()
if below then
for power,rank in pairs(ranks) do
if rank_base.power >= power then rank:print(rtn,colour) end
end
else
for power,rank in pairs(ranks) do
if rank_base.power <= power then rank:print(rtn,colour) end
end
end
end
--- Gives a user a rank
-- @usage Ranking.give_rank(1,'admin')
-- @param player the player to give the rank to
-- @param rank the rank to give to the player
-- @param[opt='server'] by_player the player who is giving the rank
-- @param[opt=game.tick] tick the tick that the rank is being given on
function Ranking.give_rank(player,rank,by_player,tick)
local print_colour = defines.text_color.info
local tick = tick or game.tick
local by_player_name = Game.get_player(by_player) and Game.get_player(by_player).name or game.player and game.player.name or 'server'
local rank = Ranking.get_rank(rank) or Ranking.get_rank(Ranking._presets().meta.default)
local player = Game.get_player(player) or error('No Player To Give Rank')
local old_rank = Ranking.get_rank(player) or Ranking.get_rank(Ranking._presets().meta.default)
local message = 'ranking.rank-down'
-- messaging
if old_rank.name == rank.name then return end
if rank.power < old_rank.power then message = 'ranking.rank-up' player.play_sound{path='utility/achievement_unlocked'}
else player.play_sound{path='utility/game_lost'} end
game.print({message,player.name,rank.name,by_player_name},print_colour)
if rank.group.name ~= 'User' then player_return({'ranking.rank-given',rank.name},print_colour,player) end
if player.tag ~= old_rank.tag then player_return({'ranking.tag-reset'},print_colour,player) end
-- rank change
player.permission_group = game.permissions.get_group(rank.name)
player.tag = rank.tag
if not old_rank.group.name == 'Jail' then Ranking._presets().old[player.index] = rank.name end
if defines.events.rank_change then
script.raise_event(defines.events.rank_change,{
name=defines.events.rank_change,
tick=tick,
player_index=player.index,
by_player_name=by_player_name,
new_rank=rank,
old_rank=old_rank
})
end
end
--- Revert the last change to a players rank
-- @usage Ranking.revert(1)
-- @param player the player to revert the rank of
-- @param[opt=nil] by_player the player who is doing the revert
function Ranking.revert(player,by_player)
local player = Game.get_player(player)
Ranking.give_rank(player,Ranking._presets().old[player.index],by_player)
end
--- Given the player has a rank in the preset table it is given
-- @usage Ranking.find_preset(1)
-- @param player the player to test for an auto rank
-- @tparam[opt=nil] tick the tick it happens on
function Ranking.find_preset(player,tick)
local presets = Ranking._presets().current
local meta_data = Ranking._presets().meta
local default = Ranking.get_rank(meta_data.default)
local player = Game.get_player(player)
local current_rank = Ranking.get_rank(player) or {power=-1,group={name='not jail'}}
local ranks = {default}
if current_rank.group.name == 'Jail' then return end
if presets[string.lower(player.name)] then
local rank = Ranking.get_rank(presets[string.lower(player.name)])
if current_rank.power >= rank.power then return end
table.insert(ranks,rank)
end
if current_rank.power < meta_data.time_highest and tick_to_min(player.online_time) > meta_data.time_lowest then
for _,rank_name in pairs(meta_data.time_ranks) do
local rank = Ranking.get_rank(rank_name)
if tick_to_min(player.online_time) > rank.time then
table.insert(ranks,rank)
end
end
end
local _rank = nil
for _,rank in pairs(ranks) do
if rank.power > current_rank.power then _rank = rank end
end
if _rank then
if _rank.name == default.name then
player.tag = _rank.tag
player.permission_group = game.permissions.get_group(_rank.name)
else
Ranking.give_rank(player,_rank,nil,tick)
end
end
end
-- this is the base rank object, do not store in global
--- Is this rank allowed to open this gui or use this command etc.
-- @usage rank:allowed('server-interface')
-- @tparam teh action to test for
-- @treturn bolean is it allowed
function Ranking._rank:allowed(action)
return self.allow[action] or self.is_root or false
end
--- Get all the players in this rank
-- @usage rank:get_players()
-- @tparam bolean online get only online players
-- @treturn table a table of all players in this rank
function Ranking._rank:get_players(online)
local players = game.permissions.get_group(rank.name).players
local _return = {}
if online then
for _,player in pairs(players) do
if player.connected then table.insert(_return,player) end
end
else
_return = players
end
return _return
end
--- Print a message to all players of this rank
-- @usage rank:print('foo')
-- @param rtn any value you want to return
function Ranking._rank:print(rtn,colour)
local colour = colour or defines.color.white
if not Server or not Server._thread then
for _,player in pairs(self:get_players()) do
player_return(rtn,colour,player)
end
else
-- using threads to make less lag
Server.new_thread{
data={rank=self,rtn=rtn}
}:on_event('resolve',function(thread)
return thread.data.rank:get_players(true)
end):on_event('success',function(thread,players)
for _,player in pairs(players) do
player_return(thread.data.rtn,colour,player)
end
end):queue()
end
end
-- this is used to edit a group once made key is what is being edited and set_value makes it over ride the current value
-- see Addons/playerRanks for examples
function Ranking._rank:edit(key,set_value,value)
if game then return end
if set_value then self[key] = value return end
if key == 'disallow' then
self.disallow = table.merge(self.disallow,value,true)
elseif key == 'allow' then
self.allow = table.merge(self.allow,value)
end
Ranking._update_rank(self)
end
-- this is the base group object, do not store in global, these cant be used in game
-- this makes a new group
-- {name='root',allow={},disallow={}}
function Ranking._group:create(obj)
if game then return end
if not is_type(obj.name,'string') then return end
setmetatable(obj,{__index=Ranking._group})
self.index = #Ranking._groups(names)+1
obj.ranks = {}
obj.allow = obj.allow or {}
obj.disallow = obj.disallow or {}
Ranking._add_group(obj)
return obj
end
-- this makes a new rank in side this group
-- {name='Root',short_hand='Root',tag='[Root]',time=nil,colour=defines.colors.white,allow={},disallow={}}
function Ranking._group:add_rank(obj)
if game then return end
if not is_type(obj.name,'string') or
not is_type(obj.short_hand,'string') or
not is_type(obj.tag,'string') or
not is_type(obj.colour,'table') then return end
setmetatable(obj,{__index=Ranking._rank})
obj.group = self
obj.allow = obj.allow or {}
obj.disallow = obj.disallow or {}
setmetatable(obj.allow,{__index=self.allow})
setmetatable(obj.disallow,{__index=self.disallow})
Ranking._add_rank(obj,obj.power)
Ranking._set_rank_power()
table.insert(self.ranks,obj)
if not self.highest or obj.power > self.highest.power then self.highest = obj end
if not self.lowest or obj.power < self.lowest.power then self.lowest = obj end
end
-- this is used to edit a group once made key is what is being edited and set_value makes it over ride the current value
-- see Addons/playerRanks for examples
function Ranking._group:edit(key,set_value,value)
if game then return end
if set_value then self[key] = value return end
if key == 'disallow' then
self.disallow = table.merge(self.disallow,value,true)
elseif key == 'allow' then
self.allow = table.merge(self.allow,value)
end
Ranking._update_group(self)
end
Event.register(defines.events.on_player_joined_game,function(event)
Ranking.find_preset(event.player_index)
end)
Event.register(-1,function(event)
for power,rank in pairs(Ranking._ranks()) do
local perm = game.permissions.create_group(rank.name)
for _,toRemove in pairs(rank.disallow) do
perm.set_allows_action(defines.input_action[toRemove],false)
end
end
end)
return Ranking

View File

@@ -1,237 +0,0 @@
--[[
Explosive Gaming
This file can be used with permission but this and the credit below must remain in the file.
Contact a member of management on our discord to seek permission to use our code.
Any changes that you may make to the code are yours but that does not make the script yours.
Discord: https://discord.gg/r6dC2uK
]]
--Please Only Edit Below This Line-----------------------------------------------------------
local groups = {}
local ranks = {}
function Ranking._add_group(group) if game then return end table.insert(groups,group) end
function Ranking._add_rank(rank,pos) if game then return end if pos then table.insert(ranks,pos,rank) else table.insert(ranks,rank) end end
function Ranking._set_rank_power() if game then return end for power,rank in pairs(ranks) do rank.power = power end end
function Ranking._update_rank(rank) if game then return end ranks[rank.power] = rank end
function Ranking._update_group(group) if game then return end groups[group.index] = group end
--[[
How to use groups:
name the name that you can use to refence it.
disallow if present then all ranks in this group will have this added to their disallow.
allow if present then all ranks in this group will have this added to their allow.
highest is asigned by the script to show the highest rank in this group.
lowest is asigned by the script to show the lowest rank in this group.
How to add ranks:
Name is what will be used in the scripts and is often the best choice for display in text.
short_hand is what can be used when short on space but the rank still need to be displayed.
tag is the tag the player will gain when moved to the rank, it can be nil.
time is used for auto-rank feature where you are moved to the rank after a certain play time in minutes.
colour is the RGB value that can be used to emphasise GUI elements based on rank.
power is asigned by the script based on their index in ranks, you can insert new ranks between current ones, lower is better
group is asigned by the script to show the group this rank is in
disallow is a list containing input actions that the user can not perform.
allow is a list of custom commands and effects that that rank can use, all defined in the sctips.
For allow, add the allow as the key and the value as true
Example: test for 'server-interface' => allow['server-interface'] = true
For disallow, add to the list the end part of the input action
Example: defines.input_action.drop_item -> 'drop_item'
http://lua-api.factorio.com/latest/defines.html#defines.input_action
--]]
-- If you wish to add more groups please use addons/playerRanks.lua
-- If you wish to add to these rank groups use addons/playerRanks.lua
-- Ranks will inherite from each other ie higher ranks can do everything lower ranks can
-- But groups do not inherite from each other
-- DO NOT REMOVE ANY OF THESE GROUPS
local root = Ranking._group:create{
name='Root',
allow={
['interface'] = true
},
disallow={}
}
local admin = Ranking._group:create{
name='Admin',
allow={},
disallow={
'set_allow_commands',
'edit_permission_group',
'delete_permission_group',
'add_permission_group'
}
}
local user = Ranking._group:create{
name='User',
allow={},
disallow={
'set_allow_commands',
'edit_permission_group',
'delete_permission_group',
'add_permission_group'
}
}
local jail = Ranking._group:create{
name='Jail',
allow={},
disallow={
'set_allow_commands',
'edit_permission_group',
'delete_permission_group',
'add_permission_group'
}
}
-- If you wish to add more ranks please use addons/playerRanks.lua
-- If you wish to add to these rank use addons/playerRanks.lua
root:add_rank{
name='Root',
short_hand='Root',
tag='[Root]',
colour=defines.color.white,
is_root=true
}
root:add_rank{
name='Owner',
short_hand='Owner',
tag='[Owner]',
time=nil,
colour={r=170,g=0,b=0}
}
root:add_rank{
name='Community Manager',
short_hand='Com Mngr',
tag='[Com Mngr]',
colour={r=150,g=68,b=161}
}
root:add_rank{
name='Developer',
short_hand='Dev',
tag='[Dev]',
colour={r=179,g=125,b=46}
}
admin:add_rank{
name='Admin',
short_hand='Admin',
tag='[Admin]',
colour={r=233,g=63,b=233}
}
admin:add_rank{
name='Mod',
short_hand='Mod',
tag='[Mod]',
colour={r=0,g=170,b=0},
disallow={
'server_command'
}
}
user:add_rank{
name='Donator',
short_hand='P2W',
tag='[P2W]',
colour={r=233,g=63,b=233}
}
user:add_rank{
name='Member',
short_hand='Mem',
tag='[Member]',
colour={r=24,g=172,b=188},
disallow={
'set_auto_launch_rocket',
'change_programmable_speaker_alert_parameters',
'drop_item'
}
}
user:add_rank{
name='Guest',
short_hand='',
tag='',
colour={r=255,g=159,b=27},
is_default=true,
disallow={
'build_terrain',
'remove_cables',
'launch_rocket',
'reset_assembling_machine',
'cancel_research'
}
}
jail:add_rank{
name='Jail',
short_hand='Jail',
tag='[Jail]',
colour={r=50,g=50,b=50},
disallow={
'open_character_gui',
'begin_mining',
'start_walking',
'player_leave_game'
}
}
function Ranking._auto_edit_ranks()
for power,rank in pairs(ranks) do
if ranks[power-1] then
rank:edit('disallow',false,ranks[power-1].disallow)
end
end
for power = #ranks, 1, -1 do
rank = ranks[power]
if ranks[power+1] then
rank:edit('allow',false,ranks[power+1].allow)
end
end
end
-- used to force rank to be read-only
function Ranking._groups(name)
if name then
if name then
local _return = {}
for power,group in pairs(groups) do
_return[group.name] = group
end
return _return
end
end
return groups
end
function Ranking._ranks(name)
if name then
local _return = {}
for power,rank in pairs(ranks) do
_return[rank.name] = rank
end
return _return
end
return ranks
end
-- used to save lag by doing some calculation at the start
function Ranking._meta()
local meta = {time_ranks={}}
for power,rank in pairs(ranks) do
meta.rank_count = power
if rank.is_default then
meta.default = rank.name
end
if rank.is_root then
meta.root = rank.name
end
if rank.time then
table.insert(meta.time_ranks,rank.name)
if not meta.time_highest or power < meta.time_highest then meta.time_highest = power end
if not meta.time_lowest or rank.time < meta.time_lowest then meta.time_lowest = power.time end
end
meta.time_highest = meta.time_highest or 0
meta.time_lowest = meta.time_lowest or 0
end
return meta
end

View File

@@ -1,421 +0,0 @@
--[[
Explosive Gaming
This file can be used with permission but this and the credit below must remain in the file.
Contact a member of management on our discord to seek permission to use our code.
Any changes that you may make to the code are yours but that does not make the script yours.
Discord: https://discord.gg/r6dC2uK
]]
--Please Only Edit Below This Line-----------------------------------------------------------
-- server allows control over threads and other features the devs missed out
local Server = {}
Server._thread = {}
--- Returns a un-used uuid (better system needed)
-- @usage obj.uuid = Server.new_uuid()
-- @treturn string the new uuid
function Server.new_uuid()
local uuid = tostring(Server._uuid()())
uuid = string.to_hex('uuid'..uuid)
return uuid
end
-- use this to change the location of the server uuids
function Server._uuid(reset)
global.exp_core = not reset and global.exp_core or {}
global.exp_core.uuids = not reset and global.exp_core.uuids or game.create_random_generator()
return global.exp_core.uuids
end
--- Returns either the number of threads or a able of threads
-- @usage Server.threads() -- return {...}
-- Server.threads(true) -- return int
-- @tparam[opt=nil] bolean count true to return the number of threads
-- @return either a list of threads or a number
function Server.threads(count)
return count and Server._threads().all._n or Server._threads().all
end
-- use this to change the location of the server threads
-- all stores the threads indexed uuid, the other three only store the uuid's to index in the all table
function Server._threads(reset)
global.exp_core = not reset and global.exp_core or {}
global.exp_core.threads = not reset and global.exp_core.threads or {print_to={},queue={},tick={},timeout={},events={},all={_n=0},paused={},named={}}
return global.exp_core.threads
end
-- see thread:create (this was done so thread can remain local)
function Server.new_thread(obj)
return Server._thread:create(obj)
end
--- Used to get a thread via it's uuid or by name if one is given
-- @usage Server.get_thread('decon') -- return thread
-- @param mixed either a uuid or the name given to a thread
-- @treturn table the thread by that name or uuid
function Server.get_thread(mixed)
local threads = Server._threads()
if threads.named[mixed] then return threads.all[threads.named[mixed]]
elseif threads.paused[mixed] then return threads.all[threads.paused[mixed]]
elseif threads.all[mixed] then return threads.all[mixed]
else return false end
end
--- Adds a thread into the resolve queue, can be used to lower lag
-- @usage Server.queue_thread(thread) -- return true/false
-- @tparam table the thread to add to the queue must have a resolve function (must be open)
-- @treturn bolean was the thread added
function Server.queue_thread(thread_to_queue)
if not thread_to_queue and not thread_to_queue.valid and not thread_to_queue:valid() then return false end
if not thread_to_queue._resolve then return false end
table.insert(Server._threads().queue,thread_to_queue.uuid)
return true
end
--- Closes all active threads, can use force if it causes errors
-- @usage Server.close_all_threads()
-- Server.close_all_threads(true) -- use if no force makes errors
-- @tparam bolean with_force use force when closing
function Server.close_all_threads(with_force)
if not with_force then
for uuid,next_thread in pairs(Server.threads()) do
if uuid ~= '_n' then next_thread:close() end
end
else
Server._threads(true)
end
end
--- Runs all the theads which have opened with an on_tick event
-- @ussage Server.run_tick_threads()
function Server.run_tick_threads()
table.each(Server._threads().tick,function(uuid)
local next_thread = Server.get_thread(uuid)
if next_thread and next_thread:valid() and next_thread._tick then
local success, err = pcall(next_thread._tick,next_thread)
if not success then next_thread:error(err) end
end
end)
end
--- Checks the timeout on all active timeout threads
-- @ussage Server.check_timeouts()
function Server.check_timeouts()
table.each(Server._threads().timeout,function(uuid)
local next_thread = Server.get_thread(uuid)
if next_thread and next_thread:valid() then
next_thread:check_timeout()
end
end)
end
-- for use in debuging
function Server._thread_handler_debuger(player,event,state)
local player = Game.get_player(player)
local print_to = Server._threads().print_to
print_to[player.index] = print_to[player.index] or {}
print_to[player.index][event] = state
end
--- Calles all threads on a certain game event (used with script.on_event)
-- @tparam table event the event that is called
function Server._thread_handler(event)
table.each(Server._threads().print_to,function(print_to,player_index,event)
if event.name == defines.events.on_tick then return true end
if print_to[event.name] then
player_return(event,defines.text_color.bg,player_index)
end
end,event)
local event_id = event.name
local threads = Server._threads().events[event_id]
if not threads then return end
table.each(threads,function(uuid)
local next_thread = Server.get_thread(uuid)
if next_thread and next_thread:valid() then
if is_type(next_thread._events[event_id],'function') then
local success, err = pcall(next_thread._events[event_id],next_thread,event)
if not success then next_thread:error(err) end
end
end
end)
end
for _,event in pairs(defines.events) do Event.register(event,Server._thread_handler) end
--[[ cant be used V
--- Adds a event handler to tell threads about events
-- @usage Server.add_thread_handler(defines.event)
-- @tparam number event the event to run the thread handler on
-- @treturn bolean if the handler was added
function Server.add_thread_handler(event)
if not is_type(event,'number') then return false end
local threads = Server._threads()
if not threads.events[event] then
threads.events[event] = {}
Event.register(event,Server._thread_handler)
return true
end
return false
end
]]
--- Given a string or function it will run that function and return any values
-- @usage Server.interface('local x = 1+1 print(x) return x') -- return 2
-- Server.interface('local x = 1+1 print(x)',thread) -- no return
-- @param callback either a function or string which will be ran via pcall
-- @param[opt] use_thread give a thread for the interface to run on (does not need to be open, but cant use on_resolve)
-- @param[opt] ... any args you want to pass to the function
function Server.interface(callback,use_thread,...)
if use_thread then
if use_thread == true then use_thread = Server.new_thread{data={callback,...}} end
use_thread:on_event('resolve',function(thread)
if is_type(thread.data[1],'function') then
local success, err = pcall(unpack(thread.data))
if not success then error(err) end
return err
else
local callback = table.remove(thread.data,1)
local success, err = pcall(loadstring(callback),unpack(thread.data))
if not success then error(err) end
return err
end
end)
use_thread:open()
Server.queue_thread(use_thread)
else
if is_type(callback,'function') then
local success, err = pcall(callback,...)
return success, err
else
local success, err = pcall(loadstring(callback),...)
return success, err
end
return false
end
end
if commands._expgaming then
commands.add_command('interface', 'Runs the given input from the script', {'code',true}, function(event,args)
local callback = args.code
if not string.find(callback,'%s') and not string.find(callback,'return') then callback = 'return '..callback end
if game.player then callback = 'local player, surface, force = game.player, game.player.surface, game.player.force '..callback end
if Ranking and Ranking.get_rank and game.player then callback = 'local rank = Ranking.get_rank(game.player) '..callback end
local success, err = Server.interface(callback)
player_return(err)
end)
end
-- thread allows you to run fuinction async to the main game
--- Returns a new thread object
-- @usage new_thread = thread:create()
-- @tparam[opt={}] table obj all are opt {timeout=int,name=str,data=any} advanced users can prefix with _function to avoid the on_function functions
-- @treturn table the new thread object
function Server._thread:create(obj)
local obj = obj or {}
setmetatable(obj,{__index=Server._thread})
obj.uuid = Server.new_uuid()
return obj
end
-- see Server.queue_thread - this just opens it first
function Server._thread:queue()
self:open()
return Server.queue_thread(self)
end
--- Test if the thread has all requied parts
-- @usage if thread:valid() then end
-- @tparam bolean skip_location_check true to skip the location check
-- @treturn bolean is the thread valid
function Server._thread:valid(skip_location_check)
local skip_location_check = skip_location_check or false
if is_type(self.uuid,'string') and
skip_location_check or is_type(self.opened,'number') and
skip_location_check or is_type(Server._threads().all[self.uuid],'table') and
is_type(self.timeout) or is_type(self.timeout,'number') and
is_type(self.name) or is_type(self.name,'string') and
is_type(self._close) or is_type(self._close,'function') and
is_type(self._timeout) or is_type(self._timeout,'function') and
is_type(self._tick) or is_type(self._tick,'function') and
is_type(self._resolve) or is_type(self._resolve,'function') and
is_type(self._success) or is_type(self._success,'function') and
is_type(self._error) or is_type(self._error,'function') then
return true
end
return false
end
--- Opens the thread by storing it in a place the server object can find it
-- @usage thread:open() -- return true
-- @treturn bolean if the thread was opened
function Server._thread:open()
if not self:valid(true) or self.opened then return false end
local threads = Server._threads()
local uuid = self.uuid
self.opened = game.tick
threads.all[uuid] = threads.all[uuid] or self
threads.all._n = threads.all._n+1
if is_type(self.timeout,'number') then table.insert(threads.timeout,uuid) end
if is_type(self._tick,'function') then table.insert(threads.tick,uuid) end
if is_type(self.name,'string') then threads.named[self.name] = threads.named[self.name] or self.uuid end
if is_type(self._events,'table') then
table.each(self._events,function(callback,event,threads,uuid)
-- cant be used V
--Server.add_thread_handler(event)
if not threads.events[event] then threads.events[event] = {} end
table.insert(threads.events[event],uuid)
end,threads,self.uuid)
end
return true
end
--- Inverse of thread:open() - it removes the thread and calles on_close
-- @usage thread:close() -- return true
-- @treturn bolean if the thread had a on_close function
function Server._thread:close()
local threads = Server._threads()
local uuid = self.uuid
local _return = false
if is_type(self._close,'function') then pcall(self._close,self) _return = true end
local value,key = table.find(threads.queue,function(v,k,uuid) return v == uuid end,uuid)
if key then table.remove(threads.queue,key) end
local value,key = table.find(threads.timeout,function(v,k,uuid) return v == uuid end,uuid)
if key then table.remove(threads.timeout,key) end
local value,key = table.find(threads.tick,function(v,k,uuid) return v == uuid end,uuid)
if key then table.remove(threads.tick,key) end
if is_type(self._events,'table') then
table.each(self._events,function(callback,event)
if threads.events[event] then
local value,key = table.find(threads.events[event],function(v,k,uuid) return v == uuid end,uuid)
if key then table.remove(threads.events[event],key) end
-- cant be used V
--if #threads.events[event] == 0 then Event.remove(event,Server.game_event) threads.events[event] = nil end
end
end)
end
if is_type(self.name,'string') then threads.paused[self.name] = self.uuid self.opened = nil
else threads.all[uuid] = nil threads.all._n = threads.all._n-1 end
return _return
end
--- Trigger the on_resolve function and closes the thread - error and success called based on result of pcall (useful for async)
-- @usage thread:resolve(x,y,z) -- return true
-- @param[opt] ... any arguments you want to pass to the resolve function
-- @treturn bolean true if the thread called on_success or on_error
function Server._thread:resolve(...)
local _return = false
if is_type(self._resolve,'function') then
local success, err = pcall(self._resolve,self,...)
if success then
if is_type(self._success,'function') then
Server.interface(function(thread,err)
local success,err = pcall(thread._success,thread,err)
if not success then thread:error(err) end
end,true,self,err)
_return = true
end
else
_return = self:error(err)
end
end
self:close()
return _return
end
--- Checks the timeout on a thread - if timedout then it calles on_timeout and closes
-- @usage thread:check_timeout() -- return true
-- @treturn bolean if the thread timedout
function Server._thread:check_timeout()
local _return = false
if not self:valid() then return false end
if is_type(self.timeout,'number') and game.tick >= (self.opened+self.timeout) then
if is_type(self._timeout,'function') then
pcall(self._timeout,self)
end
_return = true
self:close()
end
return _return
end
--- Rasies an error on this thread
-- @usage thread:error(err) -- return true
-- @param err the err to be rasied
-- @treturn bolean did the thread handdle the error
function Server._thread:error(err)
local _return = false
if is_type(self._error,'function') then
pcall(self._error,self,err)
_return = true
else
error(err)
end
return _return
end
--- Set function to run then an event is called on a thread, none of them are 'needed' but you are advised to have atleast one
-- @usage thread:on_event('close',function) -- return true
-- events = ['close','timeout','tick','resolve','success','error']
-- if event is a number then it is asumed to be a game event
-- @tparam string event the name of the event that it is called on
-- @tparam function callback the function which is called on the event
-- @treturn table returns self so that there can be chained
function Server._thread:on_event(event,callback)
local events = {'close','timeout','tick','resolve','success','error'}
local value = table.find(events,function(v,k,find) return v == string.lower(find) end,event)
if value and is_type(callback,'function') then
self['_'..value] = callback
elseif is_type(event,'number') and is_type(callback,'function') then
if not self._events then self._events = {} end
self._events[event] = callback
end
return self
end
Event.register(defines.events.on_tick,function(event)
local threads = Server._threads()
if #threads.tick > 0 then Server.run_tick_threads() end
if #threads.timeout > 0 then Server.check_timeouts() end
if #threads.queue > 0 then
local current_thread = threads.all[threads.queue[1]]
if current_thread and current_thread:valid() then current_thread:resolve() end
end
end)
Event.register(-2,function(event)
local threads = Server.threads()
for uuid,thread in pairs(threads) do
if uuid ~= '_n' then setmetatable(thread,{__index=Server._thread}) end
end
end)
return Server
--[[
Thread Example:
local thread = Server.new_thread{name='tree-decon',data={}}
-- user thread:on_event('tick') rather than thread:on_event(defines.events.on_tick) as it makes less lag
thread:on_event('tick',function(self)
local trees = self.data
if #trees == 0 then return end
local tree = table.remove(trees,1)
if tree.valid then tree.destroy() end
end)
thread:on_event('error',function(self,err)
-- cant see how this can cause an error
-- but this is where error handling goes
-- any event including on_resolve and on_tick can raise this
end)
thread:on_event(defines.events.on_marked_for_deconstruction,function(self,event)
if event.entity.type == 'tree' then
table.insert(self.data,event.entity)
end
end)
thread:open()
local thread = Server.new_thread{name='print-place',data={}}
thread:on_event(defines.events.on_built_entity,function(self,event)
game.print('Events')
end)
thread:open()
all on_event functions can be chained from the thread creation rather than use varibles
]]

View File

@@ -1,314 +0,0 @@
--- A defines module for retrieving colors by name.
-- Extends the Factorio defines table.
-- @usage require('stdlib/defines/color')
-- @module defines.color
-- @see Concepts.Color
-- defines table is automatically required in all mod loading stages.
-- luacheck: ignore 122/defines
-- Ignore assigning to read only defines table. defines table is not ready only, however
-- marking it this way allows warnings to be generated when trying to assign values
defines = defines or {} --luacheck: ignore defines (This is used for testing locally)
--- A table of colors allowing retrieval by color name.
-- @usage color = defines.color.red
-- @tfield Concepts.Color white
-- @tfield Concepts.Color black
-- @tfield Concepts.Color darkgrey
-- @tfield Concepts.Color grey
-- @tfield Concepts.Color lightgrey
-- @tfield Concepts.Color red
-- @tfield Concepts.Color darkred
-- @tfield Concepts.Color lightred
-- @tfield Concepts.Color green
-- @tfield Concepts.Color darkgreen
-- @tfield Concepts.Color lightgreen
-- @tfield Concepts.Color blue
-- @tfield Concepts.Color darkblue
-- @tfield Concepts.Color lightblue
-- @tfield Concepts.Color orange
-- @tfield Concepts.Color yellow
-- @tfield Concepts.Color pink
-- @tfield Concepts.Color purple
-- @tfield Concepts.Color brown
defines.color = {}
local colors = {
white = {r = 1.00, g = 1.00, b = 1.00},
black = {r = 0.00, g = 0.00, b = 0.00},
darkgrey = {r = 0.25, g = 0.25, b = 0.25},
grey = {r = 0.50, g = 0.50, b = 0.50},
lightgrey = {r = 0.75, g = 0.75, b = 0.75},
red = {r = 1.00, g = 0.00, b = 0.00},
darkred = {r = 0.50, g = 0.00, b = 0.00},
lightred = {r = 1.00, g = 0.50, b = 0.50},
green = {r = 0.00, g = 1.00, b = 0.00},
darkgreen = {r = 0.00, g = 0.50, b = 0.00},
lightgreen = {r = 0.50, g = 1.00, b = 0.50},
blue = {r = 0.00, g = 0.00, b = 1.00},
darkblue = {r = 0.00, g = 0.00, b = 0.50},
lightblue = {r = 0.50, g = 0.50, b = 1.00},
orange = {r = 1.00, g = 0.55, b = 0.10},
yellow = {r = 1.00, g = 1.00, b = 0.00},
pink = {r = 1.00, g = 0.00, b = 1.00},
purple = {r = 0.60, g = 0.10, b = 0.60},
brown = {r = 0.60, g = 0.40, b = 0.10}
}
--- Returns white for dark colors or black for lighter colors.
-- @tfield Concepts.Color green defines.color.black
-- @tfield Concepts.Color grey defines.color.black
-- @tfield Concepts.Color lightblue defines.color.black
-- @tfield Concepts.Color lightgreen defines.color.black
-- @tfield Concepts.Color lightgrey defines.color.black
-- @tfield Concepts.Color lightred defines.color.black
-- @tfield Concepts.Color orange defines.color.black
-- @tfield Concepts.Color white defines.color.black
-- @tfield Concepts.Color yellow defines.color.black
-- @tfield Concepts.Color black defines.color.white
-- @tfield Concepts.Color blue defines.color.white
-- @tfield Concepts.Color brown defines.color.white
-- @tfield Concepts.Color darkblue defines.color.white
-- @tfield Concepts.Color darkgreen defines.color.white
-- @tfield Concepts.Color darkgrey defines.color.white
-- @tfield Concepts.Color darkred defines.color.white
-- @tfield Concepts.Color pink defines.color.white
-- @tfield Concepts.Color purple defines.color.white
-- @tfield Concepts.Color red defines.color.white
defines.anticolor = {}
local anticolors = {
green = colors.black,
grey = colors.black,
lightblue = colors.black,
lightgreen = colors.black,
lightgrey = colors.black,
lightred = colors.black,
orange = colors.black,
white = colors.black,
yellow = colors.black,
black = colors.white,
blue = colors.white,
brown = colors.white,
darkblue = colors.white,
darkgreen = colors.white,
darkgrey = colors.white,
darkred = colors.white,
pink = colors.white,
purple = colors.white,
red = colors.white
}
--- Returns a lighter color of a named color.
-- @tfield Concepts.Color white defines.color.lightgrey
-- @tfield Concepts.Color grey defines.color.darkgrey
-- @tfield Concepts.Color lightgrey defines.color.grey
-- @tfield Concepts.Color red defines.color.lightred
-- @tfield Concepts.Color green defines.color.lightgreen
-- @tfield Concepts.Color blue defines.color.lightblue
-- @tfield Concepts.Color yellow defines.color.orange
-- @tfield Concepts.Color pink defines.color.purple
defines.lightcolor = {}
local lightcolors = {
white = colors.lightgrey,
grey = colors.darkgrey,
lightgrey = colors.grey,
red = colors.lightred,
green = colors.lightgreen,
blue = colors.lightblue,
yellow = colors.orange,
pink = colors.purple
}
-- added by cooldude2606
--- Returns a lighter color of a named color.
-- @tfield Concepts.Color info
-- @tfield Concepts.Color bg
-- @tfield Concepts.Color low
-- @tfield Concepts.Color med
-- @tfield Concepts.Color high
-- @tfield Concepts.Color crit
defines.text_color = {}
local text_color = {
info = {r = 0.21, g = 0.95, b = 1.00},
bg = {r = 0.00, g = 0.00, b = 0.00},
low = {r = 0.18, g = 0.77, b = 0.18},
med = {r = 1.00, g = 0.89, b = 0.26},
high = {r = 1.00, g = 0.33, b = 0.00},
crit = {r = 1.00, g = 0.00, b = 0.00}
}
local _mt = {
color = {
__index = function(_, c)
return colors[c]
and { r = colors[c]['r'], g=colors[c]['g'], b=colors[c]['b'], a = colors[c]['a'] }
or { r = 1, g = 1, b = 1, a = 1 }
end,
__pairs = function()
local k = nil
local c = colors
return function()
local v
k, v = next(c, k)
return k, (v and {r = v['r'], g = v['g'], b = v['b'], a = v['a']}) or nil
end
end
},
anticolor = {
__index = function(_, c)
return anticolors[c]
and { r = anticolors[c]['r'], g=anticolors[c]['g'], b=anticolors[c]['b'], a = anticolors[c]['a'] }
or { r = 1, g = 1, b = 1, a = 1 }
end,
__pairs = function()
local k = nil
local c = anticolors
return function()
local v
k, v = next(c, k)
return k, (v and {r = v['r'], g = v['g'], b = v['b'], a = v['a']}) or nil
end
end
},
lightcolor = {
__index = function(_, c)
return lightcolors[c]
and { r = lightcolors[c]['r'], g=lightcolors[c]['g'], b=lightcolors[c]['b'], a = lightcolors[c]['a'] }
or { r = 1, g = 1, b = 1, a = 1 }
end,
__pairs = function()
local k = nil
local c = lightcolors
return function()
local v
k, v = next(c, k)
return k, (v and {r = v['r'], g = v['g'], b = v['b'], a = v['a']}) or nil
end
end
},
text_color = { -- added by cooldude2606
__index = function(_, c)
return text_color[c]
and { r = text_color[c]['r'], g=text_color[c]['g'], b=text_color[c]['b'], a = text_color[c]['a'] }
or { r = 1, g = 1, b = 1, a = 1 }
end,
__pairs = function()
local k = nil
local c = text_color
return function()
local v
k, v = next(c, k)
return k, (v and {r = v['r'], g = v['g'], b = v['b'], a = v['a']}) or nil
end
end
}
}
setmetatable(defines.color, _mt.color)
setmetatable(defines.anticolor, _mt.anticolor)
setmetatable(defines.text_color, _mt.text_color)
setmetatable(defines.lightcolor, _mt.lightcolor)
--- For playing with colors.
-- @module Color
-- @usage local Color = require('stdlib/color/color')
--require 'stdlib/defines/color'
local fail_if_missing = require 'game'['fail_if_missing']
local Color = {} --luacheck: allow defined top
--- Set a value for the alpha channel in the given color table.
-- `color.a` represents the alpha channel in the given color table.
-- <ul>
-- <li>If ***alpha*** is given, set `color.a` to it.
-- <li>If ***alpha*** is not given, and if the given color table does not have a value for `color.a`, set `color.a` to 1.
-- <li>If ***alpha*** is not given, and if the given color table already has a value for `color.a`, then leave `color.a` alone.
-- </ul>
-- @tparam[opt=white] defines.color|Concepts.Color color the color to configure
-- @tparam[opt=1] float alpha the alpha value (*[0 - 1]*) to set for the given color
-- @treturn Concepts.Color a color table that has the specified value for the alpha channel
function Color.set(color, alpha)
color = color or defines.color.white
Color.to_table(color)
color.a = alpha or color.a or 1
return color
end
--- Converts a color in the array format to a color in the table format.
-- @tparam array c_arr the color to convert &mdash; { [1] = @{float}, [2] = @{float}, [3] = @{float}, [4] = @{float} }
-- @treturn Concepts.Color a converted color &mdash; { r = c\_arr[1], g = c\_arr[2], b = c\_arr[3], a = c\_arr[4] }
function Color.to_table(c_arr)
if #c_arr > 0 then
return {r = c_arr[1], g = c_arr[2], b = c_arr[3], a = c_arr[4]}
end
return c_arr
end
--- Converts a color in the rgb format to a color table
-- @tparam[opt=0] int r 0-255 red
-- @tparam[opt=0] int g 0-255 green
-- @tparam[opt=0] int b 0-255 blue
-- @tparam[opt=255] int a 0-255 alpha
-- @treturn Concepts.Color
function Color.from_rgb(r, g, b, a)
r = r or 0
g = g or 0
b = b or 0
a = a or 255
return {r = r/255, g = g/255, b = b/255, a = a/255}
end
--- Get a color table with a hexadecimal string.
-- Optionally provide the value for the alpha channel.
-- @tparam string hex hexadecimal color string (#ffffff, not #fff)
-- @tparam[opt=1] float alpha the alpha value to set; such that ***[ 0 &#8924; value &#8924; 1 ]***
-- @treturn Concepts.Color a color table with RGB converted from Hex and with alpha
function Color.from_hex(hex, alpha)
fail_if_missing(hex, "missing color hex value")
if hex:find("#") then hex = hex:sub(2) end
if not(#hex == 6) then error("invalid color hex value: "..hex) end
local number = tonumber(hex, 16)
return {
r = bit32.extract(number, 16, 8) / 255,
g = bit32.extract(number, 8, 8) / 255,
b = bit32.extract(number, 0, 8) / 255,
a = alpha or 1
}
end
--added by cooldude2606
--- Converts a color in the color table format to rgb
-- @tparam table color the color to convert
-- @treturn table the color as rgb
function Color.to_rgb(color)
local r = color.r or 0
local g = color.g or 0
local b = color.b or 0
local a = color.a or 0.5
return {r = r*255, g = g*255, b = b*255, a = a*255}
end
--added by cooldude2606
--- Converts a color in the color table format to hex
-- @tparam table color the color to convert
-- @treturn string the color as hex
function Color.to_hex(color)
local hexadecimal = '0x'
for key, value in pairs{math.floor(color.r*255),math.floor(color.g*255),math.floor(color.b*255)} do
local hex = ''
while(value > 0)do
local index = math.fmod(value, 16) + 1
value = math.floor(value / 16)
hex = string.sub('0123456789ABCDEF', index, index) .. hex
end
if string.len(hex) == 0 then hex = '00'
elseif string.len(hex) == 1 then hex = '0' .. hex
end
hexadecimal = hexadecimal .. hex
end
return hexadecimal
end
return Color

View File

@@ -1,173 +0,0 @@
--- Makes working with events in factorio a lot more simple.
-- <p>Factorio can only have one handler registered per event. This module
-- allows you to easily register multiple handlers for each event.
-- Using this module is as simple as replacing script.on_event(...) with Event.register(...)</p>
-- @module Event
-- @usage require('stdlib/event/event')
local fail_if_missing = require 'game'['fail_if_missing']
local Game = require 'game'
local Event = { --luacheck: allow defined top
_registry = {},
core_events = {
init = -1,
load = -2,
configuration_changed = -3,
_register = function(id)
if id == Event.core_events.init then
script.on_init(
function()
Event.dispatch({name = Event.core_events.init, tick = game.tick})
end
)
elseif id == Event.core_events.load then
script.on_load(
function()
Event.dispatch({name = Event.core_events.load, tick = -1})
end
)
elseif id == Event.core_events.configuration_changed then
script.on_configuration_changed(
function(event)
event.name = Event.core_events.configuration_changed
event.data = event -- for backwards compatibilty
Event.dispatch(event)
end
)
end
end
}
}
--[[ edit by cooldude2606 to allow change during run-time without desyncs -- still going to use this but FACTORIO NO LIKE
Event.__registry = Event._registry
Event._registry = function()
if game and global then
if not global.event_registry then global.event_registry = Event.__registry end
return global.event_registry
end
return Event.__registry
end]]
--- Registers a function for a given event. If a nil handler is passed remove all events and stop listening for that event.
-- Events are dispatched in the order they are registered.
-- @usage Event.register(defines.events.on_tick, function(event) print event.tick end)
-- -- creates an event that prints the current tick every tick.
-- @tparam defines.events|{defines.events,...} event events to register
-- @tparam function handler Function to call when event is triggered
-- @treturn Event
function Event.register(event, handler)
fail_if_missing(event, "missing event argument")
event = (type(event) == "table" and event) or {event}
for _, event_id in pairs(event) do
if not (type(event_id) == "number" or type(event_id) == "string") then
error("Invalid Event Id, Must be string or int, or array of strings and/or ints", 2)
end
if handler == nil then
Event._registry[event_id] = nil
script.on_event(event_id, nil)
else
if not Event._registry[event_id] then
Event._registry[event_id] = {}
if type(event_id) == "string" or event_id >= 0 then
script.on_event(event_id, Event.dispatch)
elseif event_id < 0 then
Event.core_events._register(event_id)
end
end
table.insert(Event._registry[event_id], handler)
end
end
return Event
end
--- Calls the registerd handlers
-- Will stop dispatching remaning handlers if any handler passes invalid event userdata.
-- Handlers are dispatched in the order they were created
-- @tparam table event LuaEvent as created by script.raise_event
-- @see https://forums.factorio.com/viewtopic.php?t=32039#p202158 Invalid Event Objects
function Event.dispatch(event)
if event then
local _registry = event.name and Event._registry[event.name] or event.input_name and Event._registry[event.input_name]
if _registry then
local force_crc = Event.force_crc
for idx, handler in ipairs(_registry) do
-- Check for userdata and stop processing further handlers if not valid
for _, val in pairs(event) do
if type(val) == "table" and val.__self == "userdata" and not val.valid then
return
end
end
setmetatable(event, { __index = { _handler = handler } })
-- Call the handler
local success, err = pcall(handler, event)
-- If the handler errors lets make sure someone notices
if not success then
if _G.game then -- may be nil in on_load
-- edit by cooldude2606 custom error haddle
--if Game.print_all(err) == 0 then
--error(err) -- no players received the message, force a real error so someone notices
--end
error(err)
else
error(err) -- no way to handle errors cleanly when the game is not up
end
-- continue processing the remaning handlers. In most cases they won't be related to the failed code.
end
-- force a crc check if option is enabled. This is a debug option and will hamper perfomance if enabled
if (force_crc or event.force_crc) and _G.game then
local msg = 'CRC check called for event '..event.name..' handler #'..idx
log(msg) -- log the message to factorio-current.log
game.force_crc()
end
-- if present stop further handlers for this event
if event.stop_processing then
return
end
end
end
else
error('missing event argument')
end
end
--- Removes the handler from the event. If it removes the last handler for an event stop listening for that event.
-- @tparam defines.events|{defines.events,...} event events to remove the handler for
-- @tparam function handler to remove
-- @return Event
function Event.remove(event, handler)
fail_if_missing(event, "missing event argument")
fail_if_missing(handler, "missing handler argument")
event = (type(event) == "table" and event) or {event}
for _, event_id in pairs(event) do
if not (type(event_id) == "number" or type(event_id) == "string") then
error("Invalid Event Id, Must be string or int, or array of strings and/or ints", 2)
end
if Event._registry[event_id] then
for i=#Event._registry[event_id], 1, -1 do
if Event._registry[event_id][i] == handler then
table.remove(Event._registry[event_id], i)
end
end
if #Event._registry[event_id] == 0 then
Event._registry[event_id] = nil
script.on_event(event_id, nil)
end
end
end
return Event
end
return Event

View File

@@ -1,103 +0,0 @@
--- The game module.
-- @module Game
-- @usage local Game = require('stdlib/game')
local Game = { --luacheck: allow defined top
VALID_FILTER = function(v)
return v and v.valid
end,
_protect = function(module_name)
return {
__newindex = function() error("Attempt to mutatate read-only "..module_name.." Module") end,
__metatable = true
}
end,
_concat = function(lhs, rhs)
--Sanatize to remove address
return tostring(lhs):gsub("(%w+)%: %x+", "%1: (ADDR)") .. tostring(rhs):gsub("(%w+)%: %x+", "%1: (ADDR)")
end,
_rawstring = function (t)
local m = getmetatable(t)
local f = m.__tostring
m.__tostring = nil
local s = tostring(t)
m.__tostring = f
return s
end
}
-- No Doc
-- This is a helper global and functions until .16
-- to set the name of your mod in control.lua set _stdlib_mod_name = 'name of your mod'
-- luacheck: ignore _stdlib_mod_name
function Game.get_mod_name()
local ok, mod_name = pcall(function() return script.mod_name end)
return ok and mod_name or _stdlib_mod_name or "stdlib"
end
--- Print msg if specified var evaluates to false.
-- @tparam Mixed var variable to evaluate
-- @tparam[opt="missing value"] string msg message
function Game.fail_if_missing(var, msg)
if not var then
error(msg or "Missing value", 3)
end
return false
end
--- Return a valid player object from event, index, string, or userdata
-- @tparam string|number|LuaPlayer|event mixed
-- @treturn LuaPlayer a valid player or nil
function Game.get_player(mixed)
if type(mixed) == "table" then
if mixed.__self then
return mixed and mixed.valid and mixed
elseif mixed.player_index then
local player = game.players[mixed.player_index]
return player and player.valid and player
end
elseif mixed then
local player = game.players[mixed]
return player and player.valid and player
end
end
--- Return a valid force object from event, string, or userdata
-- @tparam string|LuaForce|event mixed
-- @treturn LuaForce a valid force or nil
function Game.get_force(mixed)
if type(mixed) == "table" then
if mixed.__self then
return mixed and mixed.valid and mixed
elseif mixed.force then
return Game.get_force(mixed.force)
end
elseif type(mixed) == "string" then
local force = game.forces[mixed]
return (force and force.valid) and force
end
end
--- Messages all players currently connected to the game.
--> Offline players are not counted as having received the message.
-- If no players exist msg is stored in the `global._print_queue` table.
-- @tparam string msg the message to send to players
-- @tparam[opt] ?|nil|boolean condition the condition to be true for a player to be messaged
-- @treturn uint the number of players who received the message.
function Game.print_all(msg, condition)
local num = 0
if #game.players > 0 then
for _, player in pairs(game.players) do
if condition == nil or select(2, pcall(condition, player)) then
player.print(msg)
num = num + 1
end
end
return num
else
global._print_queue = global._print_queue or {}
global._print_queue[#global._print_queue + 1] = msg
end
end
return Game

View File

@@ -1,35 +0,0 @@
--[[
Explosive Gaming
This file can be used with permission but this and the credit below must remain in the file.
Contact a member of management on our discord to seek permission to use our code.
Any changes that you may make to the code are yours but that does not make the script yours.
Discord: https://discord.gg/r6dC2uK
]]
--[[
StdLib
This file allow you to only require this one file to return the diffent libarys.
This file will return a function which can be used to access only the part you want.
Pass a table with the names of the objects you want and it will be return in that order
]]
local StdLib = {}
require '/table'
require '/string'
require '/time'
StdLib.Color = require '/color'
StdLib.Game = require '/game'
StdLib.Event = require '/event'
return function(rtn)
local _return = {}
for _,name in pairs(rtn) do
if StdLib[name] then
table.insert(_return,StdLib[name])
end
end
return unpack(_return)
end

View File

@@ -1,86 +0,0 @@
--- Extends Lua 5.2 string.
-- @module string
-- @see string
-- luacheck: globals string (Allow mutating string)
--- Returns a copy of the string with any leading or trailing whitespace from the string removed.
-- @tparam string s the string to remove leading or trailing whitespace from
-- @treturn string a copy of the string without leading or trailing whitespace
function string.trim(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
--- Tests if a string starts with a given substring.
-- @tparam string s the string to check for the start substring
-- @tparam string start the substring to test for
-- @treturn boolean true if the start substring was found in the string
function string.starts_with(s, start)
return string.find(s, start, 1, true) == 1
end
--- Tests if a string ends with a given substring.
-- @tparam string s the string to check for the end substring
-- @tparam string ends the substring to test for
-- @treturn boolean true if the end substring was found in the string
function string.ends_with(s, ends)
return #s >= #ends and string.find(s, ends, #s - #ends + 1, true) and true or false
end
--- 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
function string.contains(s, contains)
return s and string.find(s, contains) ~= nil
end
--- Tests whether a string is empty.
-- @tparam string s the string to test
-- @treturn boolean true if the string is empty
function string.is_empty(s)
return s == nil or s == ''
end
--- Splits a string into an array.
-- *Note:* Empty split substrings are not included in the resulting table.
-- <p>For example, `string.split("foo.bar...", ".", false)` results in the table `{"foo", "bar"}`.
-- @tparam string s the string to split
-- @tparam[opt="."] string sep the separator to use.
-- @tparam[opt=false] boolean pattern whether to interpret the separator as a lua pattern or plaintext for the string split
-- @treturn {string,...} an array of strings
function string.split(s, sep, pattern)
sep = sep or "."
sep = sep ~= "" and sep or "."
sep = not pattern and string.gsub(sep, "([^%w])", "%%%1") or sep
local fields = {}
local start_idx, end_idx = string.find(s, sep)
local last_find = 1
while start_idx do
local substr = string.sub(s, last_find, start_idx - 1)
if string.len(substr) > 0 then
table.insert(fields, string.sub(s, last_find, start_idx - 1))
end
last_find = end_idx + 1
start_idx, end_idx = string.find(s, sep, end_idx + 1)
end
local substr = string.sub(s, last_find)
if string.len(substr) > 0 then
table.insert(fields, string.sub(s, last_find))
end
return fields
end
-- added by cooldude2606
--- Returns a string as a hex format (also a string)
-- @usage a = 'foo'
-- string.to_hex(a) -- return '666f6f'
-- @tparam string str the string to encode
-- @treturn string the hex format of the string
function string.to_hex(str)
if not is_type(str,'string') then return '' end
return str:gsub('.',function (c)
return string.format('%02X',string.byte(c))
end)
end

View File

@@ -1,497 +0,0 @@
--- Extends Lua 5.2 table.
-- @module table
-- @see table
-- luacheck: globals table (Allow mutating global table)
--- Given a mapping function, creates a transformed copy of the table
--- by calling the function for each element in the table, and using
--- the result as the new value for the key. Passes the index as second argument to the function.
--- @usage a= { 1, 2, 3, 4, 5}
---table.map(a, function(v) return v * 10 end) --produces: { 10, 20, 30, 40, 50 }
--- @usage a = {1, 2, 3, 4, 5}
---table.map(a, function(v, k, x) return v * k + x end, 100) --produces { 101, 104, 109, 116, 125}
-- @tparam table tbl the table to be mapped to the transform
-- @tparam function func the function to transform values
-- @param[opt] ... additional arguments passed to the function
-- @treturn table a new table containing the keys and mapped values
function table.map(tbl, func, ...)
local newtbl = {}
for i, v in pairs(tbl) do
newtbl[i] = func(v, i, ...)
end
return newtbl
end
--- Given a filter function, creates a filtered copy of the table
--- by calling the function for each element in the table, and
--- filtering out any key-value pairs for non-true results. Passes the index as second argument to the function.
--- @usage a= { 1, 2, 3, 4, 5}
---table.filter(a, function(v) return v % 2 == 0 end) --produces: { 2, 4 }
--- @usage a = {1, 2, 3, 4, 5}
---table.filter(a, function(v, k, x) return k % 2 == 1 end) --produces: { 1, 3, 5 }
-- @tparam table tbl the table to be filtered
-- @tparam function func the function to filter values
-- @param[opt] ... additional arguments passed to the function
-- @treturn table a new table containing the filtered key-value pairs
function table.filter(tbl, func, ...)
local newtbl = {}
local insert = #tbl > 0
for k, v in pairs(tbl) do
if func(v, k, ...) then
if insert then table.insert(newtbl, v)
else newtbl[k] = v end
end
end
return newtbl
end
--- Given a candidate search function, iterates over the table, calling the function
--- for each element in the table, and returns the first element the search function returned true.
--- Passes the index as second argument to the function.
--- @usage a= { 1, 2, 3, 4, 5}
---table.find(a, function(v) return v % 2 == 0 end) --produces: 2
--- @usage a = {1, 2, 3, 4, 5}
---table.find(a, function(v, k, x) return k % 2 == 1 end) --produces: 1
-- @tparam table tbl the table to be searched
-- @tparam function func the function to use to search for any matching element
-- @param[opt] ... additional arguments passed to the function
-- @treturn ?|nil|Mixed the first found value, or nil if none was found
function table.find(tbl, func, ...)
for k, v in pairs(tbl) do
if func(v, k, ...) then
return v, k
end
end
return nil
end
--- Given a candidate search function, iterates over the table, calling the function
-- for each element in the table, and returns true if search function returned true.
-- Passes the index as second argument to the function.
-- @see table.find
--- @usage a= { 1, 2, 3, 4, 5}
---table.any(a, function(v) return v % 2 == 0 end) --produces: true
--- @usage a = {1, 2, 3, 4, 5}
---table.any(a, function(v, k, x) return k % 2 == 1 end) --produces: true
-- @tparam table tbl the table to be searched
-- @tparam function func the function to use to search for any matching element
-- @param[opt] ... additional arguments passed to the function
-- @treturn boolean true if an element was found, false if none was found
function table.any(tbl, func, ...)
return table.find(tbl, func, ...) ~= nil
end
--- Given a function, apply it to each element in the table.
-- Passes the index as the second argument to the function.
-- <p>Iteration is aborted if the applied function returns true for any element during iteration.
-- @usage
-- a = {10, 20, 30, 40}
-- table.each(a, function(v) game.print(v) end) --prints 10, 20, 30, 40, 50
-- @tparam table tbl the table to be iterated
-- @tparam function func the function to apply to elements
-- @param[opt] ... additional arguments passed to the function
-- @treturn table the table where the given function has been applied to its elements
function table.each(tbl, func, ...)
for k, v in pairs(tbl) do
if func(v, k, ...) then
break
end
end
return tbl
end
--- Returns a new array that is a one-dimensional recursive flattening of the given array.
-- For every element that is an array, extract its elements into the new array.
-- <p>The optional level argument determines the level of recursion to flatten.
--> This function flattens an integer-indexed array, but not an associative array.
-- @tparam array tbl the array to be flattened
-- @tparam[opt] uint level recursive levels, or no limit to recursion if not supplied
-- @treturn array a new array that represents the flattened contents of the given array
function table.flatten(tbl, level)
local flattened = {}
table.each(tbl,
function(value)
if type(value) == "table" and #value > 0 then
if level then
if level > 0 then
table.merge(flattened, table.flatten(value, level - 1), true)
else
table.insert(flattened, value)
end
else
table.merge(flattened, table.flatten(value), true)
end
else
table.insert(flattened, value)
end
end
)
return flattened
end
--- Given an array, returns the first element or nil if no element exists.
-- @tparam array tbl the array
-- @treturn ?|nil|Mixed the first element
function table.first(tbl)
return tbl[1]
end
--- Given an array, returns the last element or nil if no elements exist.
-- @tparam array tbl the array
-- @treturn ?|nil|Mixed the last element or nil
function table.last(tbl)
local size = #tbl
if size == 0 then return nil end
return tbl[size]
end
--- Given an array of only numeric values, returns the minimum or nil if no element exists.
-- @tparam {number,...} tbl the array with only numeric values
-- @treturn ?|nil|number the minimum value
function table.min(tbl)
if #tbl == 0 then return nil end
local min = tbl[1]
for _, num in pairs(tbl) do
min = num < min and num or min
end
return min
end
---Given an array of only numeric values, returns the maximum or nil if no element exists.
-- @tparam {number,...} tbl the array with only numeric values
-- @treturn ?|nil|number the maximum value
function table.max(tbl)
if #tbl == 0 then return nil end
local max = tbl[1]
for _, num in pairs(tbl) do
max = num > max and num or max
end
return max
end
--- Given an array of only numeric values, return the sum of all values, or 0 for empty arrays.
-- @tparam {number,...} tbl the array with only numeric values
-- @treturn number the sum of the numbers or zero if the given array was empty
function table.sum(tbl)
local sum = 0
for _, num in pairs(tbl) do
sum = sum + num
end
return sum
end
--- Given an array of only numeric values, returns the average or nil if no element exists.
-- @tparam {number,...} tbl the array with only numeric values
-- @treturn ?|nil|number the average value
function table.avg(tbl)
local cnt = #tbl
return cnt ~= 0 and table.sum(tbl) / cnt or nil
end
--- Merges two tables &mdash; values from first get overwritten by the second.
--- @usage
-- function some_func(x, y, args)
-- args = table.merge({option1=false}, args)
-- if opts.option1 == true then return x else return y end
-- end
-- some_func(1,2) -- returns 2
-- some_func(1,2,{option1=true}) -- returns 1
-- @tparam table tblA first table
-- @tparam table tblB second table
-- @tparam[opt=false] boolean array_merge set to true to merge the tables as an array or false for an associative array
-- @treturn array|table an array or an associated array where tblA and tblB have been merged
function table.merge(tblA, tblB, array_merge)
if not tblB then
return tblA
end
if array_merge then
for _, v in pairs(tblB) do
table.insert(tblA, v)
end
else
for k, v in pairs(tblB) do
tblA[k] = v
end
end
return tblA
end
-- copied from factorio/data/core/luablib/util.lua
--- Creates a deep copy of table without copying Factorio objects.
-- @usage local copy = table.deepcopy[data.raw.["stone-furnace"]["stone-furnace"]] -- returns a copy of the stone furnace entity
-- @tparam table object the table to copy
-- @treturn table a copy of the table
function table.deepcopy(object)
local lookup_table = {}
local function _copy(this_object)
if type(this_object) ~= "table" then
return this_object
elseif this_object.__self then
return this_object
elseif lookup_table[this_object] then
return lookup_table[this_object]
end
local new_table = {}
lookup_table[this_object] = new_table
for index, value in pairs(this_object) do
new_table[_copy(index)] = _copy(value)
end
return setmetatable(new_table, getmetatable(this_object))
end
return _copy(object)
end
--- Returns a copy of all of the values in the table.
-- @tparam table tbl the table to copy the keys from, or an empty table if tbl is nil
-- @tparam[opt] boolean sorted whether to sort the keys (slower) or keep the random order from pairs()
-- @tparam[opt] boolean as_string whether to try and parse the values as strings, or leave them as their existing type
-- @treturn array an array with a copy of all the values in the table
function table.values(tbl, sorted, as_string)
if not tbl then return {} end
local valueset = {}
local n = 0
if as_string then --checking as_string /before/ looping is faster
for _, v in pairs(tbl) do
n = n + 1
valueset[n] = tostring(v)
end
else
for _, v in pairs(tbl) do
n = n + 1
valueset[n] = v
end
end
if sorted then
table.sort(valueset,
function(x, y) --sorts tables with mixed index types.
local tx = type(x) == 'number'
local ty = type(y) == 'number'
if tx == ty then
return x < y and true or false --similar type can be compared
elseif tx == true then
return true --only x is a number and goes first
else
return false --only y is a number and goes first
end
end
)
end
return valueset
end
--- Returns a copy of all of the keys in the table.
-- @tparam table tbl the table to copy the keys from, or an empty table if tbl is nil
-- @tparam[opt] boolean sorted whether to sort the keys (slower) or keep the random order from pairs()
-- @tparam[opt] boolean as_string whether to try and parse the keys as strings, or leave them as their existing type
-- @treturn array an array with a copy of all the keys in the table
function table.keys(tbl, sorted, as_string)
if not tbl then return {} end
local keyset = {}
local n = 0
if as_string then --checking as_string /before/ looping is faster
for k, _ in pairs(tbl) do
n = n + 1
keyset[n] = tostring(k)
end
else
for k, _ in pairs(tbl) do
n = n + 1
keyset[n] = k
end
end
if sorted then
table.sort(keyset,
function(x, y) --sorts tables with mixed index types.
local tx = type(x) == 'number'
local ty = type(y) == 'number'
if tx == ty then
return x < y and true or false --similar type can be compared
elseif tx == true then
return true --only x is a number and goes first
else
return false --only y is a number and goes first
end
end
)
end
return keyset
end
--- Removes keys from a table by setting the values associated with the keys to nil.
-- @usage local a = {1, 2, 3, 4}
--table.remove_keys(a, {1,3}) --returns {nil, 2, nil, 4}
-- @usage local b = {k1 = 1, k2 = 'foo', old_key = 'bar'}
--table.remove_keys(b, {'old_key'}) --returns {k1 = 1, k2 = 'foo'}
-- @tparam table tbl the table to remove the keys from
-- @tparam {Mixed,...} keys an array of keys that exist in the given table
-- @treturn table tbl without the specified keys
function table.remove_keys(tbl, keys)
for i = 1, #keys do
tbl[keys[i]] = nil
end
return tbl
end
--- Returns the number of keys in a table, if func is passed only count keys when the function is true.
-- @tparam table tbl to count keys
-- @tparam[opt] function func to incremement counter
-- @param[optchain] ... additional arguments passed to the function
-- @treturn number The number of keys matching the function or the number of all keys if func isn't passed
-- @treturn number The total number of keys
-- @usage local a = { 1, 2, 3, 4, 5}
-- table.count_keys(a) -- produces: 5, 5
-- @usage local a = {1, 2, 3, 4, 5}
-- table.count_keys(a, function(v, k) return k % 2 == 1 end) -- produces: 3, 5
function table.count_keys(tbl, func, ...)
if type(tbl) ~= 'table' then return 0, 0 end
local count, total = 0, 0
for k, v in pairs(tbl) do
total = total + 1
if func then
if func(v, k, ...) then
count = count + 1
end
else
count = count + 1
end
end
return count, total
end
--- Returns an inverted (***{[value] = key,...}***) copy of the given table. If the values are not unique, the assigned key depends on the order of pairs().
-- @usage local a = {k1 = 'foo', k2 = 'bar'}
--table.invert(a) --returns {'foo' = k1, 'bar' = k2}
-- @usage local b = {k1 = 'foo', k2 = 'bar', k3 = 'bar'}
--table.invert(b) --returns {'foo' = k1, 'bar' = ?}
-- @tparam table tbl the table to invert
-- @treturn table a new table with inverted mapping
function table.invert(tbl)
local inverted = {}
for k, v in pairs(tbl) do
inverted[v] = k
end
return inverted
end
--- Return the size of a table using built in table_size function
-- @function size
-- @tparam table table to use
-- @treturn int size of the table
table.size = table_size
--- For all string or number values in an array map them to a key = true table
-- @usage local a = {"v1", "v2"}
-- table.array_to_dict_bool(a) -- return {["v1"] = true, ["v2"]= true}
-- @tparam table tbl the table to convert
-- @treturn table the converted table
function table.arr_to_bool(tbl)
local newtbl = {}
for _, v in pairs(tbl) do
if type(v) == "string" or type(v) == "number" then
newtbl[v] = true
end
end
return newtbl
end
-- Any thing below here i (cooldude2606) have added and was not here by default
--- Returns a value in a form able to be read as a value
-- @usage local a = 'value'
-- table.val_to_str(a) -- return '"value"'
-- @param v value to convert
-- @treturn string the converted value
function table.val_to_str(v)
if "string" == type( v ) then
v = string.gsub(v,"\n","\\n")
if string.match(string.gsub(v,"[^'\"]",""),'^"+$') then
return "'"..v.."'"
end
return '"'..string.gsub(v,'"', '\\"' )..'"'
else
return "table" == type( v) and table.to_string(v) or
"function" == type(v) and '"cant-display-function"' or
"userdata" == type(v) and '"cant-display-userdata"' or
tostring(v)
end
end
--- Returns a value in a form able to be read as a key
-- @usage local a = 'key'
-- table.key_to_str(a) -- return '["key"]'
-- @param k key to convert
-- @treturn string the converted key
function table.key_to_str (k)
if "string" == type(k) and string.match(k,"^[_%player][_%player%d]*$") then
return k
else
return "["..table.val_to_str(k).."]"
end
end
--- Returns a table in a form able to be read as a table
-- @usage local a = {k1='foo',k2='bar'}
-- table.tostring(a) -- return '{["k1"]="foo",["k2"]="bar"}'
-- @tparam table tbl table to convert
-- @treturn string the converted table
function table.to_string(tbl)
local result, done = {}, {}
for k, v in ipairs(tbl) do
table.insert(result,table.val_to_str(v))
done[k] = true
end
for k, v in pairs(tbl) do
if not done[k] then
table.insert(result,
table.key_to_str(k).."="..table.val_to_str(v))
end
end
return "{"..table.concat(result,",") .."}"
end
--- Simmilar to table.to_string but converts a lua table to a json one
-- @usage local a = {k1='foo',k2='bar'}
-- talbe.json(a) -- return '{"k1":"foo","k2":"bar"}'
-- @tparam table lua_table the table to convert
-- @treturn string the table in a json format
function table.json(lua_table)
local result, done, only_indexs = {}, {}, true
for key,value in ipairs(lua_table) do
done[key] = true
if type(value) == 'table' then table.insert(result,table.json(value,true))
elseif type(value) == 'string' then table.insert(result,'"'..value..'"')
elseif type(value) == 'number' then table.insert(result,value)
elseif type(value) == 'boolean' then table.insert(result,tostring(value))
else table.insert(result,'null')
end
end
for key,value in pairs(lua_table) do
if not done[key] then
only_indexs = false
if type(value) == 'table' then table.insert(result,'"'..key..'":'..table.json(value,true))
elseif type(value) == 'string' then table.insert(result,'"'..key..'":"'..value..'"')
elseif type(value) == 'number' then table.insert(result,'"'..key..'":'..value)
elseif type(value) == 'boolean' then table.insert(result,'"'..key..'":'..tostring(value))
else table.insert(result,'"'..key..'":null')
end
end
end
if only_indexs then return "["..table.concat(result,",").."]"
else return "{"..table.concat(result,",").."}"
end
end
--- Returns the closest match to a key
-- @usage tbl = {foo=1,bar=2}
-- table.autokey(tbl,'f') -- return 1
function table.autokey(tbl,str)
local _return = {}
for key,value in pairs(tbl) do
if string.contains(string.lower(key),string.lower(str)) then table.insert(_return,value) end
end
return _return[1] or false
end

View File

@@ -1,31 +0,0 @@
--- A defines module for retrieving the number of ticks in 1 unit of time.
-- Extends the Factorio defines table.
-- @module defines.time
-- @usage require('stdlib/defines/time')
-- defines table is automatically required in all mod loading stages.
-- luacheck: ignore 122/defines
-- Ignore assigning to read only defines table. defines table is not read only, however
-- marking it this way allows warnings to be generated when trying to assign values.
defines = defines or {} --luacheck: ignore defines (This is used for testing locally)
local SECOND = 60
local MINUTE = SECOND * 60
local HOUR = MINUTE * 60
local DAY = HOUR * 24
local WEEK = DAY * 7
local MONTH = DAY * 30
local YEAR = DAY * 365
--- Returns the number of ticks in a second, minute, hour, day, week, month, or year.
-- @usage local ten_seconds = defines.time.second * 10
defines.time = {
second = SECOND, -- the number of Factorio ticks in a second
minute = MINUTE, -- the number of Factorio ticks in a second
hour = HOUR, -- the number of Factorio ticks in an hour
day = DAY, -- the number of Factorio ticks in an day
week = WEEK, -- the number of Factorio ticks in a week
month = MONTH, -- the number of Factorio ticks in a month (30 days)
year = YEAR, -- the number of Factorio ticks in a year (365 days)
}