diff --git a/FactorioSoftmodManager.lua b/FactorioSoftmodManager.lua deleted file mode 100644 index 5b41c124..00000000 --- a/FactorioSoftmodManager.lua +++ /dev/null @@ -1,754 +0,0 @@ ---- Factorio Softmod Manager --- @module FSM --- @alias Manager --- @author Cooldude2606 --- @usage Manager = require("FactorioSoftmodManager") -local moduleIndex = require("/modules/index") -local Manager = {} - --- this is a constant that is used to represent the server -SERVER = setmetatable({index=0,name='',online_time=0,afk_time=0,print=print,admin=true,valid=true,__self={}},{__index=function(tbl,key) if type(game.players[1][key]) == 'function' then return function() end else return nil end end}) - ---- Setup for metatable of the Manager to force read only nature --- @usage Manager() -- runs Manager.loadModdules() --- @usage Manager[name] -- returns module by that name --- @usage tostring(Manager) -- returns formated list of loaded modules -local ReadOnlyManager = setmetatable({},{ - __metatable=false, - __index=function(tbl,key) - -- first looks in manager and then looks in mander.loadModules - return rawget(Manager,key) ~= nil and rawget(Manager,key) or rawget(Manager.loadModules,key) - end, - __call=function(tbl) - -- if there are no modules loaded then it loads them - if #tbl.loadModules == 0 then - tbl.loadModules() - end - end, - __newindex=function(tbl,key,value) - -- provents the changing of any key that is not currentState - if key == 'currentState' then - -- provides a verbose that is always emited describing the change in state - Manager.verbose(string.rep('__',10)..'| Start: '..value..' |'..string.rep('__',10),true) - Manager.verbose('The verbose state is now: '..tostring(Manager.setVerbose[value]),true) - rawset(Manager,key,value) - else error('Manager is read only please use included methods',2) end - end, - __tostring=function(tbl) - -- acts as a redirect - return tostring(Manager.loadModules) - end -}) - -local function setupModuleName(name) - -- creates a table that acts like a string but is read only - return setmetatable({},{ - __index=function(tbl,key) return name end, - __newindex=function(tbl,key,value) error('Module Name Is Read Only') end, - __tostring=function(tbl) return name end, - __concat=function(val1,val2) return type(val1) == 'string' and val1..name or name..val2 end, - __metatable=false, - }) -end - -Manager.currentState = 'selfInit' --- selfInit > moduleLoad > moduleInit > modulePost > moduleEnv - ---- Default output for the verbose --- @usage Manager.verbose('Hello, World!') --- @tparam string rtn the value that will be returned though verbose output -Manager._verbose = function(rtn) - -- creates one file per game, ie clears file on reset - if game and Manager.setVerbose._output ~= true then Manager.setVerbose._output=true game.write_file('verbose.log',rtn) - elseif game then game.write_file('verbose.log','\n'..rtn,true) end - -- standard print and log, _log is a version of log which is ln1 of control.lua for shorter log lines - if print then print(rtn) end - if _log then _log(rtn) end -end - ---- Used to call the output of the verbose when the current state allows it --- @usage Manager.verbose('Hello, World!') --- @tparam string rtn the value that will be returned though verbose output --- @tparam string action is used to decide which verbose this is error || event etc -Manager.verbose = function(rtn,action) - local settings = Manager.setVerbose - local state = Manager.currentState - if Manager.error and state == Manager.error.__crash then return end - -- if ran in a module the the global moduleName is present - local rtn = type(rtn) == table and serpent.line(rtn) or tostring(rtn) - if moduleName then rtn='['..moduleName..'] '..rtn - else rtn='[FSM] '..rtn end - -- module_verbose is a local override for a file, action is used in the manager to describe an extra type, state is the current state - -- if action is true then it will always trigger verbose - if module_verbose or (action and (action == true or settings[action])) or (not action and settings[state]) then - if type(settings.output) == 'function' then - -- calls the output function, not pcalled as if this fails some thing is very wrong - settings.output(rtn) - else - error('Verbose set for: '..state..' but output can not be called',2) - end - end -end - ---- Main logic for allowing verbose at different stages though out the script --- @function Manager.setVerbose --- @usage Manager.setVerbose{output=log} --- @tparam newTbl settings the table that will be searched for settings to be updated --- @usage Manager.setVerbose[setting] -- returns the value of that setting --- @usage tostring(Manager.setVerbose) -- returns a formated list of the current settings -Manager.setVerbose = setmetatable( - --- Different verbose settings used for setVerbose - -- @table Manager.verboseSettings - -- @tfield boolean selfInit called while the manager is being set up - -- @tfield boolean moduleLoad when a module is required by the manager - -- @tfield boolean moduleInit when and within the initation of a module - -- @tfield boolean modulePost when and within the post of a module - -- @tfield boolean moduleEnv during module runtime, this is a global option set within each module(module_verbose=true ln:1) for fine control - -- @tfield boolean eventRegistered when a module registers its event handlers - -- @tfield boolean errorCaught when an error is caught during runtime - -- @tfield function output can be: print || log || or other function - -- @field _output a constant value that can used to store output data - { - selfInit=true, - moduleLoad=false, - moduleInit=false, - modulePost=false, - moduleEnv=false, - eventRegistered=false, - errorCaught=true, - output=Manager._verbose, - _output={} - }, - { - __metatable=false, - __call=function(tbl,settings) - -- does not allow any new keys, but will override any existing ones - for key,value in pairs(settings) do - if rawget(tbl,key) ~= nil then - Manager.verbose('Verbose for: "'..key..'" has been set to: '..tostring(value)) - rawset(tbl,key,value) - end - end - end, - __newindex=function(tbl,key,value) - -- stops creationg of new keys - error('New settings cannot be added during runtime',2) - end, - __index=function(tbl,key) - -- will always return a value, never nil - return rawget(tbl,key) or false - end, - __tostring=function(tbl) - -- a simple concat function for the settings - local rtn = '' - for key,value in pairs(tbl) do - if type(value) == 'boolean' then - rtn=rtn..key..': '..tostring(value)..', ' - end - end - return rtn:sub(1,-3) - end - } -) --- call to verbose to show start up, will always be present -Manager.verbose(string.rep('__',10)..'| Start: selfInit |'..string.rep('__',10),true) -Manager.verbose('The verbose state is: '..tostring(Manager.setVerbose.selfInit),true) - ---- Used to avoid conflicts in the global table --- @usage global[key] -- used like the normal global table --- @usage global{'foo','bar'} -- sets the default value --- @usage global(true) -- restores global to default --- @usage global(mopdule_name) -- returns that module's global --- @tparam[opt={}] ?table|string|true if table then the default for the global, if a string then the module to get the global of, if true then reset the global to default --- @treturn table the new global table for that module -Manager.global=setmetatable({__defaults={},__global={ - __call=function(tbl,default) return Manager.global(default) end, - __index=function(tbl,key) return rawget(Manager.global(),key) or tbl(key) end, - __newindex=function(tbl,key,value) rawset(Manager.global(),key,value) end, - __pairs=function(tbl) - local tbl = Manager.global() - local function next_pair(tbl,k) - k, v = next(tbl, k) - if type(v) ~= nil then return k,v end - end - return next_pair, tbl, nil - end -}},{ - __call=function(tbl,default,metatable_src) - -- creates varible link to global and module information, use of a metatable is for already formed globals - local Global = _G.global - local metatable = getmetatable(metatable_src) - local moduleName = type(default) == 'string' and default or metatable and metatable._moduleName or moduleName - local module_path = type(default) == 'string' and Manager.loadModules.__load[default] or metatable and metatable._module_path or module_path - -- if there is no name or path then it will return and unedited version of global - if not module_path or not moduleName then return _G.global end - -- edits the link to global to be the corrected dir, path varible is also created - local path = 'global' - for dir in module_path:gmatch('%a+') do - path = path..'.'..dir - if not rawget(Global,dir) then Manager.verbose('Added Global Dir: '..path) rawset(Global,dir,{}) end - Global = rawget(Global,dir) - end - -- the default value is set if there was a default given - if type(default) == 'table' then Manager.verbose('Default global has been set for: global'..string.sub(module_path:gsub('/','.')),2) rawset(rawget(tbl,'__defaults'),tostring(moduleName),default) end - -- if the default value is true then it will reset the global to its default - if default == true and rawget(rawget(tbl,'__defaults'),tostring(moduleName)) then - Manager.verbose('Reset Global Dir to default: '..path) - -- cant set it to be equle otherwise it will lose its global propeity - local function deepcopy(tbl) if type(tbl) ~= 'table' then return tbl end local rtn = {} for key,value in pairs(tbl) do rtn[key] = deepcopy(value) end return rtn end - for key,value in pairs(Global) do rawset(Global,key,nil) end - for key,value in pairs(rawget(rawget(tbl,'__defaults'),tostring(moduleName))) do rawset(Global,key,deepcopy(value)) end - end - -- the metatable is remade if not already present - metatable = metatable or { - __call=function(tbl,default) return Manager.global(default,tbl) end, - __index=function(tbl,key) return rawget(Manager.global(nil,tbl),key) or moduleIndex[key] and Manager.global(key) end, - __newindex=function(tbl,key,value) rawset(Manager.global(nil,tbl),key,value) end, - __pairs=function(tbl) - local tbl = Manager.global(nil,tbl) - local function next_pair(tbl,k) - k, v = next(tbl, k) - if type(v) ~= nil then return k,v end - end - return next_pair, tbl, nil - end, - _module_path=module_path,_moduleName=moduleName - } - return setmetatable(Global,metatable) - end, - __index=function(tbl,key) return rawget(tbl(),key) or rawget(_G.global,key) or moduleIndex[key] and Manager.global(key) end, - __newindex=function(tbl,key,value) rawset(tbl(),key,value) end, - __pairs=function(tbl) - local tbl = Manager.global() - local function next_pair(tbl,k) - k, v = next(tbl, k) - if type(v) ~= nil then return k,v end - end - return next_pair, tbl, nil - end -}) -setmetatable(global,Manager.global.__global) - ---- Creates a sand box envorment and runs a callback in that sand box; provents global pollution --- @function Manager.sandbox --- @usage Manager.sandbox(callback) -- return sandbox, success, other returns from callback --- @tparam function callback the function that will be ran in the sandbox --- @param[opt] env any other params that the function will use --- @usage Manager.sandbox() -- returns and empty sandbox --- @usage Manager.sandbox[key] -- returns the sand box value in that key -Manager.sandbox = setmetatable({ - -- can not use existing keys of _G - verbose=Manager.verbose, - loaded_modules={}, -- this is over riden later - module_verbose=false, - module_exports=false, - _no_error_verbose=true -},{ - __metatable=false, - __index=ReadOnlyManager, - __call=function(tbl,callback,env,...) - if type(callback) == 'function' then - -- creates a new sandbox env - local sandbox = tbl() - local env = type(env) == 'table' and env or {} - local _G_mt = getmetatable(_G) - -- creates a new ENV where it will look in the provided env then the sand box and then _G, new indexes saved to sandbox - local tmp_env = setmetatable({},{__index=function(tbl,key) return env[key] or sandbox[key] or rawget(_G,key) end,newindex=sandbox}) - tmp_env._ENV = tmp_env - tmp_env._G_mt = _G_mt - -- sets the upvalues for the function - local i = 1 - while true do - local name, value = debug.getupvalue(callback,i) - if not name then break else if not value and tmp_env[name] then debug.setupvalue(callback,i,tmp_env[name]) end end - i=i+1 - end - -- runs the callback - setmetatable(_G,{__index=tmp_env,newindex=sandbox}) - local rtn = {pcall(callback,...)} - local success = table.remove(rtn,1) - setmetatable(_G,_G_mt) - -- this is to allow modules to be access with out the need of using Mangaer[name] also keeps global clean - if success then return success, rtn, sandbox - else return success, rtn[1], sandbox end - else return setmetatable({},{__index=tbl}) end - end -}) - ---- Allows access to modules via require and collections are returned as one object --- @function Manager.require --- @usage local Module = Manager.require(ModuleName) --- @usage local Module = Manager.require[ModuleName] --- @usage local SrcData = Manager.require(path) --- @treturn table the module that was required, one object containg submodules for a -Manager.require = setmetatable({ - __require=require -},{ - __metatable=false, - __index=function(tbl,key) return tbl(key,nil,true) end, - __call=function(tbl,path,env,mute,noLoad) - local raw_require = rawget(tbl,'__require') - local env = env or {} - -- runs in a sand box becuase sandbox everything - local success, data = Manager.sandbox(raw_require,env,path) - -- if there was no error then it assumed the path existed and returns the data - if success then return unpack(data) - else - if type(path) ~= 'string' then error('Path supplied must be a string; got: '..type(path),2) return end - local override = {} - local softmod = override - local path = path:find('@') and path:sub(1,path:find('@')-1) or path - -- tries to load the module from the modeul index - if moduleIndex[path] and not noLoad or Manager.loadModules.__load[path] then softmod = Manager.loadModules[path] end - -- will then look for any submodules if there are any; only once every module is loaded - for moduleName,subpath in pairs(moduleIndex) do - if moduleName:find(path) == 1 and moduleName ~= path then - local start, _end = moduleName:find(path) - local subname = moduleName:sub(_end+2) - -- does not add the module if it is a subsubmodule; or the key already exitsts - if not softmod then softmod = {} end - if not subname:find('.',nil,true) and not softmod[subname] then softmod[subname] = Manager.require(moduleName,nil,true,true) end - end - end - -- if there is any keys in the softmod it is returned else the errors with the require error - if override ~= softmod then return softmod end - if mute then return false else error(data,2) end - end - end -}) -require = Manager.require - ---- Loads the modules that are present in the index list --- @function Manager.loadModules --- @usage Manager.loadModules() -- loads all moddules in the index list --- @usage #Manager.loadModules -- returns the number of modules loaded --- @usage tostring(Manager.loadModules) -- returns a formatted list of all modules loaded --- @usage pairs(Manager.loadModules) -- loops over the loaded modules moduleName, module -Manager.loadModules = setmetatable({ - __load=setmetatable({},{__call=function(self,moduleName) - -- check to provent multiple calls - if self[moduleName] then return end - self[moduleName] = true - self = Manager.loadModules - -- loads the module and its dependices if there are not loaded - local load = moduleIndex[moduleName] - if not load then return end - local path = table.remove(load,1) - Manager.verbose('Loading module: "'..moduleName..'"; path: '..path) - -- loads the parent module - if moduleName:find('.',nil,true) then - local revModuleName = moduleName:reverse() - local start, _end = revModuleName:find('.',nil,true) - local parentName = revModuleName:sub(_end+1):reverse() - Manager.verbose('Loading module parent: "'..parentName..'" for: "'..moduleName..'"; path: '..path) - self.__load(parentName) - end - -- loads the dependices - Manager.verbose('Loading module dependices for: "'..moduleName..'"; path: '..path) - for _,depName in pairs(load) do self.__load(depName) end - self.__load[moduleName] = path - -- runs the module in a sandbox env - local success, module, sandbox = Manager.sandbox(Manager.require.__require,{moduleName=setupModuleName(moduleName),module_path=path},path..'/control') - -- extracts the module into a global index table for later use - if success then - -- verbose to notifie of any globals that were attempted to be created - local globals = '' - for key,value in pairs(sandbox) do globals = globals..key..', ' end - if globals ~= '' then Manager.verbose('Globals caught in "'..moduleName..'": '..globals:sub(1,-3),'errorCaught') end - Manager.verbose('Successfully loaded: "'..moduleName..'"; path: '..path) - -- if it is not a table or nil then it will set up a metatable on it - local currentType = type(rawget(self,moduleName)) - if currentType ~= 'nil' and currentType ~= 'table' then - -- if it is a function then it is still able to be called even if more keys are going to be added - -- if it is a string then it will act like one; if it is a number well thats too many metatable indexs - self[moduleName] = setmetatable({__old=self[moduleName]},{ - __call=function(self,...) if type(self.__old) == 'function' then self.__old(...) else return self.__old end end, - __tostring=function(self) return self.__old end, - __concat=function(self,val) return self.__old..val end - }) - end - -- if you prefere module_exports can be used rather than returning the module - local appendAs = sandbox.module_exports or table.remove(module,1) - if not self[moduleName] then self[moduleName] = appendAs -- if nil it just sets the value - else for key,value in pairs(appendAs) do self[moduleName][key] = value end end -- else it appends the new values - -- if there is a module by this name in _G ex table then it will be indexed to the new module - if rawget(_G,moduleName) and type(rawget(self,moduleName)) == 'table' then setmetatable(rawget(_G,moduleName),{__index=self[moduleName]}) end - if type(rawget(self,moduleName)) == 'table' then self[moduleName]._module_path = path self[moduleName]._moduleName = moduleName end - -- loads the submodule for this softmod - Manager.verbose('Loading submodules for: "'..moduleName..'"; path: '..path) - for subModName,_ in pairs(moduleIndex) do - if subModName:find(moduleName) == 1 and subModName ~= moduleName then self.__load(subModName) end - end - else - Manager.verbose('Failed load: "'..moduleName..'"; path: '..path..' ('..module..')','errorCaught') - for event_name,callbacks in pairs(Manager.event) do Manager.verbose('Removed Event Handler: "'..moduleName..'/'..Manager.event.names[event_name],'eventRegistered') callbacks[moduleName] = nil end - end - end}), - __init=setmetatable({},{__call=function(self,moduleName) - -- check to provent multiple calls - if self[moduleName] or not Manager.loadModules.__load[moduleName] then return end - self[moduleName] = true - self = Manager.loadModules - -- calls on_init for each module - -- looks for init so that init or on_init can be used - local data = self[moduleName] - if type(data) == 'table' and data.init and data.on_init == nil then data.on_init = data.init data.init = nil end - if type(data) == 'table' and data.on_init and type(data.on_init) == 'function' then - Manager.verbose('Initiating module: "'..moduleName..'"') - local success, err = Manager.sandbox(data.on_init,{moduleName=setupModuleName(moduleName),module_path=Manager.loadModules.__load[tostring(moduleName)]},data) - if success then - Manager.verbose('Successfully Initiated: "'..moduleName..'"') - else - Manager.verbose('Failed Initiation: "'..moduleName..'" ('..err..')','errorCaught') - end - -- clears the init function so it cant be used in runtime - data.on_init = nil - end - end}), - __post=setmetatable({},{__call=function(self,moduleName) - -- check to provent multiple calls - if self[moduleName] or not Manager.loadModules.__init[moduleName] then return end - self[moduleName] = true - self = Manager.loadModules - -- calls on_post for each module - -- looks for post so that post or on_post can be used - local data = self[moduleName] - if type(data) == 'table' and data.post and data.on_post == nil then data.on_post = data.post data.post = nil end - if type(data) == 'table' and data.on_post and type(data.on_post) == 'function' then - Manager.verbose('Post for module: "'..moduleName..'"') - local success, err = Manager.sandbox(data.on_post,{moduleName=setupModuleName(moduleName),module_path=Manager.loadModules.__load[tostring(moduleName)]},data) - if success then - Manager.verbose('Successful post: "'..moduleName..'"') - else - Manager.verbose('Failed post: "'..moduleName..'" ('..err..')','errorCaught') - end - -- clears the post function so it cant be used in runtime - data.on_post = nil - end - end}) - }, - { - __metatable=false, - __index=function(self,moduleName) - -- will load one module if it is not already loaded, will not init during load state or post - self.__load(moduleName) - if (ReadOnlyManager.currentState == 'moduleLoad') then return end - self.__init(moduleName) - if (ReadOnlyManager.currentState == 'moduleInit') then return end - self.__post(moduleName) - return rawget(self,moduleName) - end, - __call=function(self) - -- goes though the index looking for modules to load - ReadOnlyManager.currentState = 'moduleLoad' - for moduleName,path in pairs(moduleIndex) do self.__load(moduleName) end - -- runs though all loaded modules looking for on_init function; all other modules have been loaded use this to load extra code based on opttial dependies - ReadOnlyManager.currentState = 'moduleInit' - for moduleName,path in pairs(self) do - if moduleName ~= '__load' and moduleName ~= '__init' and moduleName ~= '__post' then self.__init(moduleName) end - end - -- runs though all loaded modules looking for on_post function; all other modules have been loaded and inited, do not load extra code in this time only altar your own data - ReadOnlyManager.currentState = 'modulePost' - for moduleName,path in pairs(self) do - if moduleName ~= '__load' and moduleName ~= '__init' and moduleName ~= '__post' then self.__post(moduleName) end - end - ReadOnlyManager.currentState = 'moduleEnv' - end, - __len=function(tbl) - -- counts the number of loaded modules - local rtn = 0 - for key,value in pairs(tbl) do - rtn = rtn + 1 - end - return rtn-3 - end, - __tostring=function(tbl) - -- a concat of all the loaded modules - local rtn = 'Load Modules: ' - for key,value in pairs(tbl) do - if key ~= '__load' and key ~= '__init' and key ~= '__post' then rtn=rtn..key..', ' end - end - return rtn:sub(1,-3) - end - } -) -Manager.sandbox.loaded_modules = Manager.loadModules - ---- A more detailed replacement for the lua error function to allow for handlers to be added; repleaces default error so error can be used instead of Manager.error --- @function Manager.error --- @usage Manager.error(err) -- calls all error handlers that are set or if none then prints to game and if that fails crashs game --- @usage Manager.error() -- returns an error constant that can be used to crash game --- @usage Manager.error(Manager.error()) -- crashs the game --- @usage Manager.error.addHandler(name,callback) -- adds a new handler if handler returns Manager.error() then game will crash --- @tparam[2] ?string|fucntion err the string to be passed to handlers; if a function it will register a handler --- @tparam[2] function callback if given the err param will be used to given the handler a name --- @usage Manager.error[name] -- returns the handler of that name if present --- @usage #Manager.error -- returns the number of error handlers that are present --- @usage pairs(Manager.error) -- loops over only the error handlers handler_name,hander -Manager.error = setmetatable({ - __crash=false, - __error_call=error, - __error_const={}, - __error_handler=function(handler_name,callback) - -- when handler_name is a string it is expeced that callback is a function; other wise handler_name must be a function - if type(handler_name) == 'string' and type(callback) == 'function' then Manager.error[handler_name]=callback - elseif type(handler_name) == 'function' then table.insert(Manager.error,handler_name) - else Manager.error('Handler is not a function',2) end - end, - in_pcall=function(level) - local level = level and level+1 or 2 - while true do - if not debug.getinfo(level) then return false end - if debug.getinfo(level).name == 'pcall' then return level end - level=level+1 - end - end -},{ - __metatalbe=false, - __call=function(tbl,err,...) - -- if no params then return the error constant - if err == nil then return rawget(tbl,'__error_const') end - -- if the error constant is given crash game - if err == rawget(tbl,'__error_const') then Manager.verbose('Force Crash','errorCaught') rawset(tbl,'__crash',true) rawget(tbl,'__error_call')('Force Crash',2) end - -- other wise treat the call as if its been passed an err string - if not _no_error_verbose or Manager.currentState ~= 'moduleEnv' then Manager.verbose('An error has occurred: '..err,'errorCaught') end - if #tbl > 0 then - -- there is at least one error handler loaded; loops over the error handlers - for handler_name,callback in pairs(tbl) do - local success, err = pcall(callback,err,...) - if not success then Manager.verbose('Error handler: "'..handler_name..'" failed to run ('..err..')','errorCaught') end - -- if the error constant is returned from the handler then crash the game - if err == rawget(tbl,'__error_const') then Manager.verbose('Force Stop by: '..handler_name,'errorCaught') rawset(tbl,'__crash',true) rawget(tbl,'__error_call')('Force Stop by: '..handler_name) end - end - elseif game then - -- there are no handlers loaded so it will print to the game if loaded - Manager.verbose('No error handlers loaded; Default game print used','errorCaught') - game.print(err) - else - -- all else fails it will crash the game with the error code - Manager.verbose('No error handlers loaded; Game not loaded; Forced crash: '..err,'errorCaught') - rawset(tbl,'__crash',true) - rawget(tbl,'__error_call')(err,2) - end - local args = {...} - local trace = args[1] and type(args[1]) == 'number' and args[1]+1 or 2 - if tbl.in_pcall(2) then rawget(tbl,'__error_call')(err,trace) end - end, - __index=function(tbl,key) - -- this allows the __error_handler to be called from many different names - if type(key) ~= 'string' then return end - if key:lower() == 'addhandler' or key:lower() == 'sethandler' or key:lower() == 'handler' or key:lower() == 'register' then return rawget(tbl,'__error_handler') - else rawget(tbl,'__error_call')('Invalid index for error handler; please use build in methods.') end - end, - __newindex=function(tbl,key,value) - -- making a new index adds it as a handler - if type(value) == 'function' then - Manager.verbose('Added Error Handler: "'..key..'"','eventRegistered') - rawset(tbl,key,value) - end - end, - __len=function(tbl) - -- counts the number of handlers there are - local rtn=0 - for handler_name,callback in pairs(tbl) do - rtn=rtn+1 - end - return rtn - end, - __pairs=function(tbl) - -- will not return any of the three core values as part of pairs - local function next_pair(tbl,k) - local v - k, v = next(tbl, k) - if k == '__error_call' or k == '__error_const' or k == '__error_handler' or k == '__crash' or k == 'in_pcall' then return next_pair(tbl,k) end - if type(v) == 'function' then return k,v end - end - return next_pair, tbl, nil - end -}) --- overrides the default error function -error=Manager.error - --- event does work a bit differnt from error, and if event breaks error is the fallback - ---- Event handler that modules can use, each module can register one function per event --- @function Manager.event --- @usage Manager.event[event_name] = callback -- sets the callback for that event --- @usage Manager.event[event_name] = nil -- clears the callback for that event --- @usage Manager.event(event_name,callback) -- sets the callback for that event --- @usage Manager.event[event_name] -- returns the callback for that event or the event id if not registered --- @usage Manager.event(event_name) -- runs all the call backs for that event --- @tparam ?int|string event_name that referes to an event --- @tparam function callback the function that will be set for that event --- @usage Manager.event() -- returns the stop value for the event proccessor, if returned during an event will stop all other callbacks --- @usage #Manager.event -- returns the number of callbacks that are registered --- @usage pairs(Manager.events) -- returns event_id,table of callbacks -Manager.event = setmetatable({ - __stop={}, - __events={}, - __event=script.on_event, - __generate=script.generate_event_name, - __get_handler=script.get_event_handler, - __raise=script.raise_event, - __init=script.on_init, - __load=script.on_load, - __config=script.on_configuration_changed, - events=defines.events, - error_cache={} -},{ - __metatable=false, - __call=function(tbl,event_name,new_callback,...) - if Manager.error.__crash then Manager.error.__error_call('No error handlers loaded; Game not loaded; Forced crash: '..tostring(Manager.error.__crash)) end - -- if no params then return the stop constant - if event_name == nil then return rawget(tbl,'__stop') end - -- if the event name is a table then loop over each value in that table - if type(event_name) == 'table' then - for key,_event_name in pairs(event_name) do tbl(_event_name,new_callback,...) end return - end - -- convert the event name to a number index - event_name = tonumber(event_name) or tbl.names[event_name] - -- if there is a callback present then set new callback rather than raise the event - if type(new_callback) == 'function' then - Manager.event[event_name] = new_callback return - end - -- other wise raise the event and call every callback; no use of script.raise_event due to override - local event_functions = tbl.__events[event_name] - if type(event_functions) == 'table' then - for moduleName,callback in pairs(event_functions) do - -- loops over the call backs and which module it is from - if type(callback) ~= 'function' then error('Invalid Event Callback: "'..event_name..'/'..moduleName..'"') end - local success, err = Manager.sandbox(callback,{moduleName=setupModuleName(moduleName),module_path=Manager.loadModules.__load[tostring(moduleName)]},new_callback,...) - if not success then - local cache = tbl.error_cache - local error_message = 'Event Failed: "'..moduleName..'/'..tbl.names[event_name]..'" ('..err..')' - if not cache[error_message] then Manager.verbose(error_message,'errorCaught') error(error_message) end - if tbl.names[event_name] == 'on_tick' then - if not cache[error_message] then cache[error_message] = {game.tick,1} end - if cache[error_message][1] >= game.tick-10 then cache[error_message] = {game.tick,cache[error_message][2]+1} - else cache[error_message] = nil end - if cache[error_message] and cache[error_message][2] > 100 then - Manager.verbose('There was an error happening every tick for 100 ticks, the event handler has been removed!','errorCaught') - event_functions[moduleName] = nil - end - end - end - -- if stop constant is returned then stop further processing - if err == rawget(tbl,'__stop') then Manager.verbose('Event Haulted By: "'..moduleName..'"','errorCaught') break end - end - end - end, - __newindex=function(tbl,key,value) - -- handles the creation of new event handlers - if type(value) ~= 'function' and type(value) ~= nil then error('Attempted to set a non function value to an event',2) end - -- checks for a global module name that is present - local moduleName = moduleName or 'FSM' - -- converts the key to a number index for the event - Manager.verbose('Added Handler: "'..tbl.names[key]..'"','eventRegistered') - -- checks that the event has a valid table to store callbacks; if its not valid it will creat it and register a real event handler - if not rawget(rawget(tbl,'__events'),key) then - if key == -1 or key == -2 then -- this already has a handler - elseif key < 0 then rawget(tbl,tbl.names[key])(function(...) tbl(key,...) end) - else rawget(tbl,'__event')(key,function(...) tbl(key,...) end) end - rawset(rawget(tbl,'__events'),key,{}) end - -- adds callback to Manager.event.__events[event_id][moduleName] - rawset(rawget(rawget(tbl,'__events'),key),tostring(moduleName),value) - end, - __index=function(tbl,key) - -- few redirect key - local redirect={register=tbl,dispatch=tbl,remove=function(event_id) tbl[event_name]=nil end} - if rawget(redirect,key) then return rawget(redirect,key) end - -- proforms different look ups depentding weather the current module has an event handler registered - if moduleName then - -- first looks for the event callback table and then under the module name; does same but converts the key to a number; no handler regisered so returns the converted event id - return rawget(rawget(tbl,'__events'),key) and rawget(rawget(rawget(tbl,'__events'),key),tostring(moduleName)) - or rawget(rawget(tbl,'__events'),rawget(tbl,'names')[key]) and rawget(rawget(rawget(tbl,'__events'),rawget(tbl,'names')[key]),tostring(moduleName)) - or rawget(tbl,'names')[key] - else - -- if there is no module present then it will return the full list of regisered handlers; or other wise the converted event id - return rawget(rawget(tbl,'__events'),key) or rawget(rawget(tbl,'__events'),rawget(tbl,'names')[key]) or rawget(tbl,'names')[key] - end - end, - __len=function(tbl) - -- counts every handler that is regised not just the the number of events with handlers - local rtn=0 - for event,callbacks in pairs(tbl) do - for module,callback in pairs(callbacks) do - rtn=rtn+1 - end - end - return rtn - end, - __pairs=function(tbl) - -- will loops over the event handlers and not Manager.event - local function next_pair(tbl,k) - k, v = next(rawget(tbl,'__events'), k) - if type(v) == 'table' then return k,v end - end - return next_pair, tbl, nil - end -}) - ---- Sub set to Manger.event and acts as a coverter between event_name and event_id --- @table Manager.event.names --- @usage Manager.event[event_name] -rawset(Manager.event,'names',setmetatable({},{ - __index=function(tbl,key) - if type(key) == 'number' or tonumber(key) then - -- if it is a number then it will first look in the cache - if rawget(tbl,key) then return rawget(tbl,key) end - -- if it is a core event then it will simply return - if key == -1 then rawset(tbl,key,'__init') - elseif key == -2 then rawset(tbl,key,'__load') - elseif key == -3 then rawset(tbl,key,'__config') - else - -- if it is not a core event then it does a value look up on Manager.events aka defines.events - for event,id in pairs(rawget(Manager.event,'events')) do - if id == key then rawset(tbl,key,event) end - end - end - -- returns the value from the cache after being loaded in - return rawget(tbl,key) - -- if it is a string then no reverse look up is required - else - if key == 'on_init' or key == 'init' or key == '__init' then return -1 - elseif key == 'on_load' or key == 'load' or key == '__load' then return -2 - elseif key == 'on_configuration_changed' or key == 'configuration_changed' or key == '__config' then return -3 - else return rawget(rawget(Manager.event,'events'),key) end - end - end -})) - -script.on_init(function(...) - Manager.verbose('____________________| SubStart: script.on_init |____________________') - setmetatable(global,Manager.global.__global) - local names = {} - for name,default in pairs(Manager.global.__defaults) do table.insert(names,name) end - Manager.verbose('Global Tables: '..table.concat(names,', ')) - for name,default in pairs(Manager.global.__defaults) do global(name)(true) end - Manager.event(-1,...) - Manager.verbose('____________________| SubStop: script.on_init |____________________') -end) - -script.on_load(function(...) - Manager.verbose('____________________| SubStart: script.on_load |____________________') - setmetatable(global,Manager.global.__global) - local names = {} - for name,default in pairs(Manager.global.__defaults) do table.insert(names,name) end - Manager.verbose('Global Tables: '..table.concat(names,', ')) - --for name,default in pairs(Manager.global.__defaults) do Manager.verbose('Global '..name..' = '..serpent.line(Manager.global(name))) end - Manager.event(-2,...) - Manager.verbose('____________________| SubStop: script.on_load |____________________') -end) ---over rides for the base values; can be called though Event -Event=setmetatable({},{__call=Manager.event,__index=function(tbl,key) return Manager.event[key] or script[key] or error('Invalid Index To Table Event') end}) -script.mod_name = setmetatable({},{__index=_G.moduleName}) -script.on_event=Manager.event -script.raise_event=Manager.event -script.on_init=function(callback) Manager.event(-1,callback) end -script.on_load=function(callback) Manager.event(-2,callback) end -script.on_configuration_changed=function(callback) Manager.event(-3,callback) end -script.get_event_handler=function(event_name) return type(Manager.event[event_name]) == 'function' and Manager.event[event_name] or nil end -script.generate_event_name=function(event_name) local event_id = Manager.event.__generate() local event_name = event_name or event_id Manager.event.events[event_name]=event_id return event_id end --- to do set up nth tick - -return ReadOnlyManager \ No newline at end of file diff --git a/container.lua b/container.lua deleted file mode 100644 index 256dfff2..00000000 --- a/container.lua +++ /dev/null @@ -1,212 +0,0 @@ -local Container = { - files={}, -- file paths which get loaded - -- these will become globals that are used to keep softmod modules working together - handlers={ - --event - --global - --error=error - --logging=log - --debug - --tableToString=serpent.line - }, - _raw={}, -- any values that are replaced by handlers are moved here - _loaded={}, - defines={ - errorLoad='ERRLOAD', -- error when loading a file - errorNotFound='ERRNOTFOUND', -- error when file not found - logAlways=0, -- will always be logged - logBasic=1, -- should be logged but not required - logDebug=2, -- longer logs of debugging - logEvents=3, -- logs which take place very often such as frequent event triggers if no other filters - logVerbose=4, -- basically a log of any thing useful - logAll=5 -- what ever is left to log weather you see a current need or not - }, - -- to prevent desyncs during on_load any change to the following must be updated - -- example: runtime change to logLevel must be applied during on_load to avoid desyncs - safeError=true, -- when true then errors are logged not raised - debug=false, -- if debug functions are triggered see Container.inDebug - logLevel=1 -- what level of details is given by logs -} - -function Container.log(level,...) - if level <= Container.logLevel then Container.stdout(...) end -end -function Container.stdout(...) - local msg = '' - for _,value in pairs({...}) do - msg = msg..' '..Container.tostring(value) - end - if Container.handlers.logging then - Container.handlers.logging(msg) - else - log(msg) - end -end - -function Container.error(...) - if Container.safeError then Container.stdout('ERROR',...) else Container.stderr(...) end -end -function Container.stderr(type,...) - local msg = 'ERROR: '..tostring(type) - for _,value in pairs({...}) do - msg = msg..' '..Container.tostring(value) - end - if Container.handlers.error then - Container.handlers.error(msg) - else - error(msg) - end -end - -function Container.type(value,test) - if not test then return type(value) end - return value and type(value) == test -end - -function Container.isLocaleString(locale) - if Container.type(locale,'table') then - local _string = locale[1] - -- '.+[.].+' this is a check for the key value pair - -- '%s' this is a check for any white space - return Container.type(_string,'string') and _string:find('.+[.].+') and not _string:find('%s') - end - return false -end - -function Container.isUserdata(userdata) - if Container.type(userdata,'table') then - return Container.type(userdata.__self,'userdata') - end - return false -end - -function Container.tostring(value) - local _type = type(value) - if _type == 'table' then - if Container.isUserdata(value) then - -- the value is userdata - return '' - elseif getmetatable(rtn) ~= nil and not tostring(rtn):find('table: 0x') then - -- the value is a table but contains the metamethod __tostring - return tostring(value) - else - -- the value is a table - if Container.handlers.tableToString then - return Container.handlers.tableToString(value) - else - return serpent.line(value) - end - end - elseif Container.type(value,'function') then - -- the value is a function and the function name is given - local name = debug.getinfo(value,'n').name or 'ANON' - return '' - else - -- all other values: number, string and boolean tostring is save to use - return tostring(value) - end -end - ---- Sandboxs a function into the container and the given env, will load upvalues if provied in the given env --- @usage container:sandbox(print,{},'hello from the sandbox') --- @tparam callback function the function that will be run in the sandbox --- @tparam env table the env which the function will run in, place upvalues in this table --- @param[opt] any args you want to pass to the function --- @treturn boolean did the function run without error --- @treturn string|table returns error message or the returns from the function --- @treturn table returns back the env as new values may have been saved -function Container.sandbox(callback,env,...) - -- creates a sandbox env which will later be loaded onto _G - local sandbox_env = setmetatable(env,{ - __index=function(tbl,key) - return rawget(_G,key) - end - }) - sandbox_env._ENV = sandbox_env - sandbox_env._MT_G = getmetatable(_G) - -- sets any upvalues on the callback - local i = 1 - while true do - local name, value = debug.getupvalue(callback,i) - if not name then break end - if not value and sandbox_env[name] then - debug.setupvalue(callback,i,sandbox_env[name]) - end - i=i+1 - end - -- adds the sandbox to _G - setmetatable(_G,{__index=sandbox_env,__newindex=sandbox_env}) - local rtn = {pcall(callback,...)} - local success = table.remove(rtn,1) - setmetatable(_G,_MT_G) - -- returns values from the callback, if error then it returns the error - if success then return success, rtn, sandbox_env - else return success, rtn[1], sandbox_env end -end - -function Container.loadFile(filePath) - if Container._loaded[filePath] then return Container._loaded[filePath] end - local success,file = pcall(require,filePath) - if not success then return Container.error(Container.defines.errorLoad,filePath,file) end - -- if the file was not found then it returns an error from require which does not trip pcall, tested for here - if Container.type(file,'string') and file:find('no such file') then - -- tries with modules. appended to the front of the path and .control on the end - local success,_file = pcall(require,'modules.'..filePath..'.control') - if not success then return Container.error(Container.defines.errorLoad,filePath,_file) end - -- again tests for the error not caught by pcall - if Container.type(_file,'string') and _file:find('no such file') then return Container.error(Container.defines.errorNotFound,filePath) end - Container.log(Container.defines.logDebug,'Loaded file:',filePath) - Container._loaded[filePath] = _file - return _file - end - Container.log(Container.defines.logDebug,'Loaded file:',filePath) - Container._loaded[filePath] = file - return file -end - -function Container.loadHandlers() - Container.log(Container.defines.logAlways,'Loading Container Handlers') - for key,value in pairs(Container.handlers) do - if Container.type(value,'string') then - -- if it is a string then it is treated as a file path - Container.handlers[key] = Container.loadFile(value) - end - if _G[key] then - -- if the key exists then it is moved to _raw before being over ridden - Container._raw[key] = _G[key] - -- it is also moved to _R for global access - if not _R then _R = {} end - _R[key] = _G[key] - end - rawset(_G,key,Container.handlers[key]) - end -end - -function Container.loadFiles() - Container.log(Container.defines.logAlways,'Loading Container Files') - for _,filePath in pairs(Container.files) do - Container.loadFile(filePath) - end -end - -function Container.initFiles() - Container.log(Container.defines.logAlways,'Initiating Container Files') - for filePath,file in pairs(Container._loaded) do - if file.on_init then - file.on_init() - Container.log(Container.defines.logDebug,'Initiated file:',filePath) - end - end -end - -function Container.postFiles() - Container.log(Container.defines.logAlways,'POSTing Container Files') - for filePath,file in pairs(Container._loaded) do - if file.on_post then - file.on_post() - Container.log(Container.defines.logDebug,'POSTed file:',filePath) - end - end -end - -return Container \ No newline at end of file diff --git a/control.lua b/control.lua index 29962032..6a233b62 100644 --- a/control.lua +++ b/control.lua @@ -1,103 +1,38 @@ ---[[ not_luadoc=true -function _log(...) log(...) end -- do not remove this is used for smaller verbose lines -Manager = require("FactorioSoftmodManager") -Manager.setVerbose{ - selfInit=true, -- called while the manager is being set up - moduleLoad=false, -- when a module is required by the manager - moduleInit=false, -- when and within the initation of a module - modulePost=false, -- when and within the post of a module - moduleEnv=false, -- during module runtime, this is a global option set within each module for fine control - eventRegistered=false, -- when a module registers its event handlers - errorCaught=true, -- when an error is caught during runtime - output=Manager._verbose -- can be: can be: print || log || other function -} -Manager() -- can be Manager.loadModules() if called else where -]] +-- If you're looking to configure anything, you want config.lua. Nearly everything in this file is dictated by the config. +-- Info on the data lifecycle and how we use it: https://github.com/Refactorio/RedMew/wiki/The-data-lifecycle +require 'resources.data_stages' +_LIFECYCLE = _STAGE.control -- Control stage -require 'utils.data_stages' -Container = require 'container' -Container.debug=false -Container.logLevel=Container.defines.logAll -Container.safeError=true -Container.handlers = { - require=function(path,env,...) - env = env or {} - local success, rtn, sandbox_env = Container.sandbox(_R.require,env,path,...) - return rtn - end, - Event='utils.event', - Global='utils.global', - --error=error, - logging=function(...) log(...) end, - tableToString=serpent.line +-- Overrides the _G.print function +require 'utils.print_override' + +-- Omitting the math library is a very bad idea +require 'utils.math' + +-- Global Debug and make sure our version file is registered +Debug = require 'utils.debug' +require 'resources.version' + +local files = { + 'modules.test' } -Container.loadHandlers() -Container.files = { - 'AdvancedStartingItems', - 'ChatPopup', - 'DamagePopup', - 'DeathMarkers', - 'DeconControl', - 'ExpGamingAdmin', - 'ExpGamingBot', - 'ExpGamingCommands', - 'ExpGamingCore', - 'ExpGamingInfo', - 'ExpGamingLib', - 'ExpGamingPlayer', - 'FactorioStdLib', - 'GameSettingsGui', - 'GuiAnnouncements', - 'PlayerAutoColor', - 'SpawnArea', - 'WarpPoints', - 'WornPaths', - 'ExpGamingAdmin.Gui', - 'ExpGamingAdmin.Ban', - 'ExpGamingAdmin.Reports', - 'ExpGamingAdmin.ClearInventory', - 'ExpGamingAdmin.TempBan', - 'ExpGamingAdmin.Teleport', - 'ExpGamingAdmin.Commands', - 'ExpGamingAdmin.Jail', - 'ExpGamingAdmin.Warnings', - 'ExpGamingAdmin.Kick', - 'ExpGamingBot.autoMessage', - 'ExpGamingBot.discordAlerts', - 'ExpGamingBot.autoChat', - 'ExpGamingCommands.cheatMode', - 'ExpGamingCommands.repair', - 'ExpGamingCommands.tags', - 'ExpGamingCommands.home', - 'ExpGamingCore.Command', - 'ExpGamingCommands.teleport', - 'ExpGamingCommands.bonus', - 'ExpGamingCommands.kill', - 'ExpGamingCore.Server', - 'ExpGamingCore.Gui', - 'ExpGamingInfo.Science', - 'ExpGamingPlayer.playerList', - 'ExpGamingCore.Sync', - 'ExpGamingCore.Role', - 'ExpGamingInfo.Readme', - 'ExpGamingInfo.Rockets', - 'ExpGamingCore.Group', - 'ExpGamingInfo.Tasklist', - 'ExpGamingPlayer.playerInfo', - 'ExpGamingPlayer.afkKick', - 'FactorioStdLib.Table', - 'ExpGamingPlayer.polls', - 'FactorioStdLib.Color', - 'FactorioStdLib.Game', - 'FactorioStdLib.String', - 'ExpGamingPlayer.inventorySearch', - 'ExpGamingCore.Gui.center', - 'ExpGamingCore.Gui.popup', - 'ExpGamingCore.Gui.toolbar', - 'ExpGamingCore.Gui.left', - 'ExpGamingCore.Gui.inputs' -} -Container.loadFiles() -Container.initFiles() -Container.postFiles() \ No newline at end of file + +-- Loads all files in array above and logs progress +local total_files = string.format('%3d',#files) +local errors = {} +for index,path in pairs(files) do + log(string.format('[INFO] Loading files %3d/%s',index,total_files)) + local success,file = pcall(require,path) + -- error checking + if not success then + log('[ERROR] Failed to load file: '..path) + log('[ERROR] '..file) + table.insert(errors,'[ERROR] '..path..' :: '..file) + elseif type(file) == 'string' and file:find('not found') then + log('[ERROR] File not found: '..path) + table.insert(errors,'[ERROR] '..path..' :: Not Found') + end +end +log('[INFO] All files loaded with '..#errors..' errors:') +for _,error in pairs(errors) do log(error) end -- logs all errors again to make it make it easy to find \ No newline at end of file diff --git a/expcore/commands.lua b/expcore/commands.lua new file mode 100644 index 00000000..da456014 --- /dev/null +++ b/expcore/commands.lua @@ -0,0 +1,548 @@ +--- Factorio command making module that makes commands with better parse and more modularity +-- @author Cooldude2606 +-- @module Commands +--[[ +>>>>Example Authenticator + + -- adds an admin only authenticator where if a command has the tag admin_only: true + -- then will only allow admins to use this command + Commands.add_authenticator(function(player,command,tags,reject) + if tags.admin_only then -- the command has the tag admin_only set to true + if player.admin then -- the player is an admin + return true -- no return is needed for success but is useful to include + else -- the player is not admin + -- you must return to block a command, they are a few ways to do this: + -- return false -- most basic and has no custom error message + -- return reject -- sill no error message and is here in case people dont know its a function + -- reject() -- rejects the player, return not needed but please return if possible + -- return reject() -- rejects the player and has a failsafe return to block command + -- reject('This command is for admins only!') -- reject but with custom error message, return not needed but please return if possible + return reject('This command is for admins only!') -- reject but with custom error message and has return failsafe + end + else -- command does not require admin + return true -- no return is needed for success but is useful to include + end + end) + +>>>>Example Parse + + -- adds a parse that will cover numbers within the given range + -- input, player and reject are common to all parse functions + -- range_min and range_max are passed to the function from add_param + Commands.add_parse('number_range_int',function(input,player,reject,range_min,range_max) + local rtn = tonumber(input) or nil -- converts input to number + rtn = type(rtn) == 'number' and math.floor(rtn) or nil -- floor the number + if not rtn or rtn < range_min or rtn > range_max then -- check if it is nil or out of the range + -- invalid input for we will reject the input, they are a few ways to do this: + -- dont return anything -- will print generic input error + -- return false -- this WILL NOT reject the input as false can be a valid output + -- return reject -- will print generic input error + -- return reject() -- will print generic input error with no please check type message + -- reject() -- if you do not return the value then they will be a duplicate message + return reject('Number entered is not in range: '..range_min..', '..range_max) -- reject with custom error + else + return rtn -- returns the number value this will be passed to the command callback + end + end) + +>>>>Example Command + + -- adds a command that will print the players name a given number of times + -- and can only be used by admin to show how auth works + Commands.add_command('repeat-name','Will repeat you name a number of times in chat.') -- creates the new command with the name "repeat-name" and a help message + :add_param('repeat-count',false,'number_range_int',1,5) -- adds a new param called "repeat-count" that is required and is type "number_range_int" the name can be used here as add_parse was used + :add_param('smiley',true,function(input,player,reject) -- this param is optional and has a custom parse function where add_parse was not used before hand + if not input then return false end -- here you can see the default check + if input:lower() == 'true' or input:lower() == 'yes' then + return true -- the value is truthy so true is returned + else + -- it should be noted that this function will be ran even when the param is not present + -- in this case input is nil and so a default can be returned, see above + return false -- false is returned other wise + end + end) + :add_tag('admin_only',true) -- adds the tag admin_only: true which because of the above authenticator means you must be added to use this command + :add_alias('name','rname') -- adds two aliases "name" and "rname" for this command which will work as if the ordinal name was used + --:auto_concat() -- cant be used due to optional param here, but this will make all user input params after the last expected one be added to the last expected one + :register(function(player,repeat_count,smiley,raw) -- this registers the command to the game, notice the params are what were defined above + -- prints the raw input to show that it can be used + game.print(player.name..' used a command with input: '..raw) + -- some smiley logic + local msg + if smiley then + msg = ':) '..player.name + else + msg = ') '..player.name + end + -- prints your name alot + for i = 1,repeat_count do + Commands.print(i..msg) -- this command is an alias for ("expcore.common").player_return it will print any value to the player/server not just strings + end + -- if you wanted to you can return some values here + -- no return -- only success message is printed + -- Commands.error('optional message here') -- prints an error message + -- return Commands.error('optional message here') -- prints an error message, and stops success message being printed + -- Commands.success('optional message here') -- same as below but success message is printed twice DONT DO this + -- return Commands.success('optional message here') -- prints your message and then the success message + end) + +>>>>Examples With No Comments (for example formatting) + + Commands.add_authenticator(function(player,command,tags,reject) + if tags.admin_only then + if player.admin then + return true + else + return reject('This command is for admins only!') + end + else + return true + end + end) + + Commands.add_parse('number_range_int',function(input,player,reject,range_min,range_max) + local rtn = tonumber(input) or nil + rtn = type(rtn) == 'number' and math.floor(rtn) or nil + if not rtn or rtn < range_min or rtn > range_max then + return reject('Number entered is not in range: '..range_min..', '..range_max) + else + return rtn + end + end) + + Commands.add_command('repeat-name','Will repeat you name a number of times in chat.') + :add_param('repeat-count',false,'number_range_int',1,5) + :add_param('smiley',true,function(input,player,reject) + if not input then return false end + if input:lower() == 'true' or input:lower() == 'yes' then + return true + else + return false + end + end) + :add_tag('admin_only',true) + :add_alias('name','rname') + :register(function(player,repeat_count,smiley,raw) + game.print(player.name..' used a command with input: '..raw) + local msg = ') '..player.name + if smiley then + msg = ':'..msg + end + for i = 1,repeat_count do + Commands.print(i..msg) + end + end) +]] + +local Game = require 'utils.game' +local player_return = require('expcore.common').player_return + +local Commands = { + defines={ + -- common values are stored error like signals + error='CommandError', + unauthorized='CommandErrorUnauthorized', + success='CommandSuccess' + }, + commands={ + -- custom command data will be stored here + }, + authorization_fail_on_error=false, -- set due to have authorize fail if a callback fails to run, more secure + authorization={ + -- custom function are stored here which control who can use what commands + }, + _prototype={ + -- used to store functions which gets added to new custom commands + }, + parse={ + -- used to store default functions which are common parse function such as player or number in range + }, + print=player_return -- short cut so player_return does not need to be required in every module +} + +--- Adds an authorization callback, function used to check if a player if allowed to use a command +-- @see Commands.authorize +-- @tparam callback function the callback you want to register as an authenticator +-- callback param - player: LuaPlayer - the player who is trying to use the command +-- callback param - command: string - the name of the command which is being used +-- callback param - tags: table - any tags which have been set for the command +-- callback param - reject: function(error_message?: string) - call to fail authorize with optional error message +-- @treturn number the index it was inserted at use to remove the callback, if anon function used +function Commands.add_authenticator(callback) + return table.insert(Commands.authorization,callback) +end + +--- Removes an authorization callback +-- @see Commands.add_authenticator +-- @tparam callback function|number the callback to remove, an index returned by add_authenticator can be passed +-- @treturn boolean was the callback found and removed +function Commands.remove_authenticator(callback) + if type(callback) == 'number' then + -- if a number is passed then it is assumed to be the index + if Commands.authorization[callback] then + table.remove(Commands.authorization,callback) + return true + end + else + -- will search the array and remove the key + local index + for key,value in pairs(Commands.authorization) do + if value == callback then + index = key + break + end + end + -- if the function was found it is removed + if index then + table.remove(Commands.authorization,index) + return true + end + end + return false +end + +--- Mostly used internally, calls all authorization callbacks, returns if the player is authorized +-- @tparam player LuaPlayer the player that is using the command, passed to callbacks +-- @tparam command_name string the command that is being used, passed to callbacks +-- @treturn[1] boolean true player is authorized +-- @treturn[1] string commands const for success +-- @treturn[2] boolean false player is unauthorized +-- @treturn[2] string|locale_string the reason given by the authenticator +function Commands.authorize(player,command_name) + local failed + local command_data = Commands.commands[command_name] + if not command_data then return false end + + -- function passed to authorization callback to make it simpler to use + local auth_fail = function(error_message) + failed = error_message or {'ExpGamingCore_Command.unauthorized'} + return Commands.defines.unauthorized + end + + -- loops over each authorization callback if any return false or unauthorized command will fail + for _,callback in pairs(Commands.authorization) do + -- callback(player: LuaPlayer, command: string, tags: table, reject: function(error_message?: string)) + local success, rtn = pcall(callback,player,command_name,command_data.tags,auth_fail) + -- error handler + if not success then + -- the callback failed to run + log('[ERROR] Authorization failed: '..rtn) + if Commands.authorization_fail_on_error then + failed = 'Internal Error' + end + elseif rtn == false or rtn == Commands.defines.unauthorized or rtn == auth_fail or failed then + -- the callback returned unauthorized, failed be now be set if no value returned + failed = failed or {'ExpGamingCore_Command.unauthorized'} + break + end + end + + -- checks if the authorization failed + if failed then + return false, failed + else + return true, Commands.defines.success + end +end + +--- Adds a common parse that can be called by name when it wants to be used +-- nb: this is not needed as you can use the callback directly this just allows it to be called by name +-- @tparam name string the name of the parse, should be the type like player or player_alive, must be unique +-- @tparam callback function the callback that is ran to prase the input +-- parse param - input: string - the input given by the user for this param +-- parse param - player: LuaPlayer - the player who is using the command +-- parse param - reject: function(error_message) - use this function to send a error to the user and fail running +-- parse return - the value that will be passed to the command callback, must not be nil and if reject then command is not run +-- @treturn boolean was the parse added will be false if the name is already used +function Commands.add_parse(name,callback) + if Commands.parse[name] then + return false + else + Commands.parse[name] = callback + return true + end +end + +--- Creates a new command object to added details to, note this does not register the command to the game +-- @tparam name string the name of the command to be created +-- @tparam help string the help message for the command +-- @treturn Commands._prototype this will be used with other functions to generate the command functions +function Commands.add_command(name,help) + local command = setmetatable({ + name=name, + help=help, + callback=function() Commands.internal_error(false,name,'No callback registered') end, + auto_concat=false, + min_param_count=0, + max_param_count=0, + tags={ + -- stores tags that can be used by auth + }, + aliases={ + -- n = name: string + }, + params={ + -- [param_name] = {optional: boolean, parse: function} + } + }, { + __index= Commands._prototype + }) + Commands.commands[name] = command + return command +end + +--- Adds a new param to the command this will be displayed in the help and used to parse the input +-- @tparam name string the name of the new param that is being added to the command +-- @tparam[opt=true] optional is this param required for this command, these must be after all required params +-- @tparam[opt=pass through] parse function this function will take the input and return a new (or same) value +-- @param[opt] ... extra args you want to pass to the parse function; for example if the parse is general use +-- parse param - input: string - the input given by the user for this param +-- parse param - player: LuaPlayer - the player who is using the command +-- parse param - reject: function(error_message) - use this function to send a error to the user and fail running +-- parse return - the value that will be passed to the command callback, must not be nil and if reject then command is not run +-- @treturn Commands._prototype pass through to allow more functions to be called +function Commands._prototype:add_param(name,optional,parse,...) + if optional ~= false then optional = true end + parse = parse or function(string) return string end + self.params[name] = { + optional=optional, + parse=parse, + parse_args={...} + } + self.max_param_count = self.max_param_count+1 + if not optional then + self.min_param_count = self.min_param_count+1 + end + return self +end + +--- Adds a tag to the command which is passed via the tags param to the authenticators, can be used to assign command roles or type +-- @tparam name string the name of the tag to be added; used to keep tags separate +-- @tparam value any the tag that you want can be anything that the authenticators are expecting +-- nb: if value is nil then name will be assumed as the value and added at a numbered index +-- @treturn Commands._prototype pass through to allow more functions to be called +function Commands._prototype:add_tag(name,value) + if not value then + -- value not given so name is the value + table.insert(self.tags,name) + else + -- name is given so its key: value + self.tags[name] = value + end + return self +end + +--- Adds an alias or multiple that will also be registered with the same callback, eg /teleport can be /tp with both working +-- @usage command:add_alias('aliasOne','aliasTwo','etc') +-- @tparam ... string any amount of aliases that you want this command to be callable with +-- @treturn Commands._prototype pass through to allow more functions to be called +function Commands._prototype:add_alias(...) + for _,alias in pairs({...}) do + table.insert(self.aliases,alias) + --Commands.alias_map[alias] = self.name + end + return self +end + +--- Enables auto concatenation of any params on the end so quotes are not needed for last param +-- nb: this will disable max param checking as they will be concated onto the end of that last param +-- this can be useful for reasons or longs text, can only have one per command +-- @treturn Commands._prototype pass through to allow more functions to be called +function Commands._prototype:auto_concat() + self.auto_concat = true + return self +end + +--- Adds the callback to the command and registers all aliases, params and help message with the game +-- nb: this must be the last function ran on the command and must be done for the command to work +-- @tparam callback function the callback for the command, will receive the player running command, and params added with add_param +-- callback param - player: LuaPlayer - the player who used the command +-- callback param - ... - any params which were registered with add_param in the order they where registered +-- callback param - raw: string - the raw input from the user, comes after every param added with add_param +function Commands._prototype:register(callback) + -- generates a description to be used + self.callback = callback + local params = self.params + local description = '' + for param_name,param_details in pairs(self.params) do + if param_details.optional then + description = string.format('%s [%s]',description,param_name) + else + description = string.format('%s <%s>',description,param_name) + end + end + self.description = description + -- registers the command under its own name + commands.add_command(self.name,description..' '..self.help,function(command_event) + local success, err = pcall(Commands.run_command,command_event) + if not success then log('[ERROR] command/'..self.name..' :: '..err) end + end) + -- adds any aliases that it has + for _,alias in pairs(self.aliases) do + if not commands.commands[alias] and not commands.game_commands[alias] then + commands.add_command(alias,description..' '..self.help,function(command_event) + command_event.name = self.name + local success, err = pcall(Commands.run_command,command_event) + Commands.internal_error(success,self.name,err) + end) + end + end +end + +--- Sends an error message to the player and returns a constant to return to command handler to exit execution +-- nb: this is for non fatal errors meaning there is no log of this event +-- nb: if reject is giving as a param to the callback use that instead +-- @usage return Commands.error() +-- @tparam[opt] error_message string an optional error message that can be sent to the user +-- @tparam[opt] play_sound string the sound to play for the error +-- @treturn Commands.defines.error return this to command handler to exit execution +function Commands.error(error_message,play_sound) + error_message = error_message or '' + player_return({'ExpGamingCore_Command.command-fail',error_message},'orange_red') + if play_sound ~= false then + play_sound = play_sound or 'utility/wire_pickup' + if game.player then game.player.play_sound{path=play_sound} end + end + return Commands.defines.error +end + +--- Sends an error to the player and logs the error, used with pcall within command handler please avoid direct use +-- nb: use error(error_message) within your callback to trigger do not trigger directly as the handler may still continue +-- @tparam success boolean the success value returned from pcall, or just false to trigger error +-- @tparam command_name string the name of the command this is used within the log +-- @tparam error_message string the error returned by pcall or some other error, this is logged and not returned to player +-- @treturn boolean the opposite of success so true means to cancel execution, used internally +function Commands.internal_error(success,command_name,error_message) + if not success then + Commands.error('Internal Error, Please contact an admin','utility/cannot_build') + log('[ERROR] command/'..command_name..' :: '..error_message) + end + return not success +end + +--- Sends a value to the player, followed by a command complete message +-- nb: either return a value from your callback to trigger or return the return of this to prevent two messages +-- @tparam[opt] value any the value to return to the player, if nil then only success message returned +-- @treturn Commands.defines.success return this to the command handler to prevent two success messages +function Commands.success(value) + if value then player_return(value) end + player_return({'ExpGamingCore_Command.command-ran'},'cyan') + return Commands.defines.success +end + +--- Main event function that is ran for all commands, used internally please avoid direct use +-- @tparam command_event table passed directly from command event from the add_command function +function Commands.run_command(command_event) + local command_data = Commands.commands[command_event.name] + local player = Game.get_player_by_index(command_event.player_index) + + -- checks if player is allowed to use the command + local authorized, auth_fail = Commands.authorize(player,command_data.name) + if not authorized then + Commands.error(auth_fail,'utility/cannot_build') + return + end + + -- null param check + if command_data.min_param_count > 0 and not command_event.parameter then + Commands.error({'ExpGamingCore_Command.invalid-inputs',command_data.name,command_data.description}) + return + end + + -- splits the arguments + local input_string = command_event.parameter + local quote_params = {} -- stores any " " params + input_string = input_string:gsub('"[^"]-"',function(w) + -- finds all " " params are removes spaces for the next part + local no_qoutes = w:sub(2,-2) + local no_spaces = no_qoutes:gsub('%s','_') + quote_params[no_spaces]=no_qoutes + if command_data.auto_concat then + -- if auto concat then dont remove quotes as it should be included later + quote_params[w:gsub('%s','_')]=w + end + return no_spaces + end) + + local raw_params = {} -- stores all params + local param_number = 0 + local last_index = 0 + for word in input_string:gmatch('%S+') do + param_number = param_number + 1 + if param_number > command_data.max_param_count then + -- there are too many params given to the command + if not command_data.auto_concat then + -- error as they should not be more + Commands.error({'ExpGamingCore_Command.invalid-inputs',command_data.name,command_data.description}) + return + else + -- concat to the last param + if quote_params[word] then + -- if it was a " " param then the spaces are re added now + raw_params[last_index]=raw_params[last_index]..' '..quote_params[word] + else + raw_params[last_index]=raw_params[last_index]..' '..word + end + end + else + -- new param that needs to be added + -- all words are added to an array + if quote_params[word] then + -- if it was a " " param then the spaces are re added now + last_index = table.insert(raw_params,quote_params[word]) + else + last_index = table.insert(raw_params,word) + end + end + end + + -- checks param count + local param_count = #raw_params + if param_count < command_data.min_param_count then + Commands.error({'ExpGamingCore_Command.invalid-inputs',command_data.name,command_data.description}) + return + end + + -- parses the arguments + local index = 1 + local params = {} + for param_name, param_data in pairs(command_data.params) do + local parse_callback = param_data.parse + if type(parse_callback) == 'string' then + -- if its a string this allows it to be pulled from the common store + parse_callback = Commands.parse[parse_callback] + end + if not type(parse_callback) == 'function' then + -- if its not a function throw and error + Commands.internal_error(success,command_data.name,'Invalid param parse '..tostring(param_data.parse)) + return + end + -- used below as the reject function + local parse_fail = function(error_message) + error_message = error_message or '' + Commands.error('Invalid Param "'..param_name..'"; '..error_message) + return + end + -- input: string, player: LuaPlayer, reject: function, ... extra args + local success,param_parsed = pcall(parse_callback,raw_params[index],player,parse_fail,unpack(param_data.parse_args)) + if Commands.internal_error(success,command_data.name,param_parsed) then return end + -- param_data.optional == false is so that optional parses are still ran even when not present + if (param_data.optional == false and param_parsed == nil) or param_parsed == Commands.defines.error or param_parsed == parse_fail then + -- no value was returned or error was returned, if nil then give error + if not param_parsed == Commands.defines.error then Commands.error('Invalid Param "'..param_name..'"; please make sure it is the correct type') end + return + end + -- adds the param to the table to be passed to the command callback + table.insert(params,param_parsed) + index=index+1 + end + + -- runs the command + -- player: LuaPlayer, ... command params, raw: string + table.insert(params,input_string) + local success, err = pcall(command_data.callback,player,unpack(params)) + if Commands.internal_error(success,command_data.name,err) then return end + if err ~= Commands.defines.error and err ~= Commands.defines.success then Commands.success(err) end +end + +return Commands \ No newline at end of file diff --git a/expcore/common.lua b/expcore/common.lua new file mode 100644 index 00000000..23d3678d --- /dev/null +++ b/expcore/common.lua @@ -0,0 +1,57 @@ +local Colours = require 'resources.color_presets' +local Game = require 'utils.game' + +local Public = {} + +--- Compare types faster for faster validation of prams +-- @usage is_type('foo','string') -- return true +-- @usage is_type('foo') -- return false +-- @param v the value to be tested +-- @tparam[opt=nil] string test_type the type to test for if not given then it tests for nil +-- @treturn boolean is v of type test_type +function Public.type_check(value,test_type) + return test_type and value and type(value) == test_type or not test_type and not value or false +end + +--- Will return a value of any type to the player/server console, allows colour for in-game players +-- @usage player_return('Hello, World!') -- returns 'Hello, World!' to game.player or server console +-- @usage player_return('Hello, World!','green') -- returns 'Hello, World!' to game.player with colour green or server console +-- @usage player_return('Hello, World!',nil,player) -- returns 'Hello, World!' to the given player +-- @param value any value of any type that will be returned to the player or console +-- @tparam[opt=defines.colour.white] ?defines.color|string colour the colour of the text for the player, ignored when printing to console +-- @tparam[opt=game.player] LuaPlayer player the player that return will go to, if no game.player then returns to server +function Public.player_return(value,colour,player) + colour = Public.type_check(colour,'table') and colour or Colours[colour] ~= Colours.white and Colours[colour] or Colours.white + player = player or game.player + -- converts the value to a string + local returnAsString + if Public.type_check(value,'table') then + if Public.type_check(value.__self,'userdata') then + -- value is userdata + returnAsString = 'Cant Display Userdata' + elseif Public.type_check(value[1],'string') and string.find(value[1],'.+[.].+') and not string.find(value[1],'%s') then + -- value is a locale string + returnAsString = value + elseif getmetatable(value) ~= nil and not tostring(value):find('table: 0x') then + -- value has a tostring meta method + returnAsString = tostring(value) + else + -- value is a table + returnAsString = serpent.block(value) + end + elseif Public.type_check(value,'function') then + -- value is a function + returnAsString = 'Cant Display Functions' + else returnAsString = tostring(value) end + -- returns to the player or the server + if player then + -- allows any valid player identifier to be used + player = Game.get_player_from_any(player) + if not player then error('Invalid Player given to player_return',2) end + -- plays a nice sound that is different to normal message sound + player.play_sound{path='utility/scenario_message'} + player.print(returnAsString,colour) + else rcon.print(returnAsString) end +end + +return Public \ No newline at end of file diff --git a/modules/ExpGamingCore/Command/locale/de.cfg b/expcore/locale/de.cfg similarity index 100% rename from modules/ExpGamingCore/Command/locale/de.cfg rename to expcore/locale/de.cfg diff --git a/modules/ExpGamingCore/Command/locale/en.cfg b/expcore/locale/en.cfg similarity index 100% rename from modules/ExpGamingCore/Command/locale/en.cfg rename to expcore/locale/en.cfg diff --git a/modules/ExpGamingCore/Command/locale/fr.cfg b/expcore/locale/fr.cfg similarity index 100% rename from modules/ExpGamingCore/Command/locale/fr.cfg rename to expcore/locale/fr.cfg diff --git a/modules/ExpGamingCore/Command/locale/nl.cfg b/expcore/locale/nl.cfg similarity index 100% rename from modules/ExpGamingCore/Command/locale/nl.cfg rename to expcore/locale/nl.cfg diff --git a/modules/ExpGamingCore/Command/locale/sv-SE.cfg b/expcore/locale/sv-SE.cfg similarity index 100% rename from modules/ExpGamingCore/Command/locale/sv-SE.cfg rename to expcore/locale/sv-SE.cfg diff --git a/modules/test.lua b/modules/test.lua index 29814085..0834d962 100644 --- a/modules/test.lua +++ b/modules/test.lua @@ -1,7 +1,91 @@ +local Event = require 'utils.event' + function thisIsATestFunction(...) game.print(serpent.line({...})) end Event.add(defines.events.on_console_chat,function(event) if event.player_index then game.print('Message: '..event.message) end +end) + + + +local Commands = require 'expcore.commands' -- require the Commands module + +-- adds an admin only authenticator where if a command has the tag admin_only: true +-- then will only allow admins to use this command +Commands.add_authenticator(function(player,command,tags,reject) + if tags.admin_only then -- the command has the tag admin_only set to true + if player.admin then -- the player is an admin + return true -- no return is needed for success but is useful to include + else -- the player is not admin + -- you must return to block a command, they are a few ways to do this: + -- return false -- most basic and has no custom error message + -- return reject -- sill no error message and is here in case people dont know its a function + -- reject() -- rejects the player, return not needed but please return if possible + -- return reject() -- rejects the player and has a failsafe return to block command + -- reject('This command is for admins only!') -- reject but with custom error message, return not needed but please return if possible + return reject('This command is for admins only!') -- reject but with custom error message and has return failsafe + end + else -- command does not require admin + return true -- no return is needed for success but is useful to include + end +end) + +-- adds a parse that will cover numbers within the given range +-- input, player and reject are common to all parse functions +-- range_min and range_max are passed to the function from add_param +Commands.add_parse('number_range_int',function(input,player,reject,range_min,range_max) + local rtn = tonumber(input) or nil -- converts input to number + rtn = type(rtn) == 'number' and math.floor(rtn) or nil -- floor the number + if not rtn or rtn < range_min or rtn > range_max then -- check if it is nil or out of the range + -- invalid input for we will reject the input, they are a few ways to do this: + -- dont return anything -- will print generic input error + -- return false -- this WILL NOT reject the input as false can be a valid output + -- return reject -- will print generic input error + -- return reject() -- will print generic input error with no please check type message + -- reject() -- if you do not return the value then they will be a duplicate message + return reject('Number entered is not in range: '..range_min..', '..range_max) -- reject with custom error + else + return rtn -- returns the number value this will be passed to the command callback + end +end) + +-- adds a command that will print the players name a given number of times +-- and can only be used by admin to show how auth works +Commands.add_command('repeat-name','Will repeat you name a number of times in chat.') -- creates the new command with the name "repeat-name" and a help message +:add_param('repeat-count',false,'number_range_int',1,5) -- adds a new param called "repeat-count" that is required and is type "number_range_int" the name can be used here as add_parse was used +:add_param('smiley',true,function(input,player,reject) -- this param is optional and has a custom parse function where add_parse was not used before hand + if not input then return false end -- here you can see the default check + if input:lower() == 'true' or input:lower() == 'yes' then + return true -- the value is truthy so true is returned + else + -- it should be noted that this function will be ran even when the param is not present + -- in this case input is nil and so a default can be returned, see above + return false -- false is returned other wise + end +end) +:add_tag('admin_only',true) -- adds the tag admin_only: true which because of the above authenticator means you must be added to use this command +:add_alias('name','rname') -- adds two aliases "name" and "rname" for this command which will work as if the ordinal name was used +--:auto_concat() -- cant be used due to optional param here, but this will make all user input params after the last expected one be added to the last expected one +:register(function(player,repeat_count,smiley,raw) -- this registers the command to the game, notice the params are what were defined above + -- prints the raw input to show that it can be used + game.print(player.name..' used a command with input: '..raw) + -- some smiley logic + local msg + if smiley then + msg = ':) '..player.name + else + msg = ') '..player.name + end + -- prints your name alot + for i = 1,repeat_count do + Commands.print(i..msg) -- this command is an alias for ("expcore.common").player_return it will print any value to the player/server not just strings + end + -- if you wanted to you can return some values here + -- no return -- only success message is printed + -- Commands.error('optional message here') -- prints an error message + -- return Commands.error('optional message here') -- prints an error message, and stops success message being printed + -- Commands.success('optional message here') -- same as below but success message is printed twice DONT DO this + -- return Commands.success('optional message here') -- prints your message and then the success message end) \ No newline at end of file diff --git a/modules/AdvancedStartingItems/control.lua b/old/modules/AdvancedStartingItems/control.lua similarity index 100% rename from modules/AdvancedStartingItems/control.lua rename to old/modules/AdvancedStartingItems/control.lua diff --git a/modules/AdvancedStartingItems/softmod.json b/old/modules/AdvancedStartingItems/softmod.json similarity index 100% rename from modules/AdvancedStartingItems/softmod.json rename to old/modules/AdvancedStartingItems/softmod.json diff --git a/modules/ChatPopup/control.lua b/old/modules/ChatPopup/control.lua similarity index 100% rename from modules/ChatPopup/control.lua rename to old/modules/ChatPopup/control.lua diff --git a/modules/ChatPopup/softmod.json b/old/modules/ChatPopup/softmod.json similarity index 100% rename from modules/ChatPopup/softmod.json rename to old/modules/ChatPopup/softmod.json diff --git a/modules/DamagePopup/control.lua b/old/modules/DamagePopup/control.lua similarity index 100% rename from modules/DamagePopup/control.lua rename to old/modules/DamagePopup/control.lua diff --git a/modules/DamagePopup/softmod.json b/old/modules/DamagePopup/softmod.json similarity index 100% rename from modules/DamagePopup/softmod.json rename to old/modules/DamagePopup/softmod.json diff --git a/modules/DeathMarkers/control.lua b/old/modules/DeathMarkers/control.lua similarity index 100% rename from modules/DeathMarkers/control.lua rename to old/modules/DeathMarkers/control.lua diff --git a/modules/DeathMarkers/softmod.json b/old/modules/DeathMarkers/softmod.json similarity index 100% rename from modules/DeathMarkers/softmod.json rename to old/modules/DeathMarkers/softmod.json diff --git a/modules/DeconControl/control.lua b/old/modules/DeconControl/control.lua similarity index 100% rename from modules/DeconControl/control.lua rename to old/modules/DeconControl/control.lua diff --git a/modules/DeconControl/locale/de.cfg b/old/modules/DeconControl/locale/de.cfg similarity index 100% rename from modules/DeconControl/locale/de.cfg rename to old/modules/DeconControl/locale/de.cfg diff --git a/modules/DeconControl/locale/en.cfg b/old/modules/DeconControl/locale/en.cfg similarity index 100% rename from modules/DeconControl/locale/en.cfg rename to old/modules/DeconControl/locale/en.cfg diff --git a/modules/DeconControl/locale/fr.cfg b/old/modules/DeconControl/locale/fr.cfg similarity index 100% rename from modules/DeconControl/locale/fr.cfg rename to old/modules/DeconControl/locale/fr.cfg diff --git a/modules/DeconControl/locale/nl.cfg b/old/modules/DeconControl/locale/nl.cfg similarity index 100% rename from modules/DeconControl/locale/nl.cfg rename to old/modules/DeconControl/locale/nl.cfg diff --git a/modules/DeconControl/locale/sv-SE.cfg b/old/modules/DeconControl/locale/sv-SE.cfg similarity index 100% rename from modules/DeconControl/locale/sv-SE.cfg rename to old/modules/DeconControl/locale/sv-SE.cfg diff --git a/modules/DeconControl/softmod.json b/old/modules/DeconControl/softmod.json similarity index 100% rename from modules/DeconControl/softmod.json rename to old/modules/DeconControl/softmod.json diff --git a/modules/ExpGamingAdmin/Ban/control.lua b/old/modules/ExpGamingAdmin/Ban/control.lua similarity index 100% rename from modules/ExpGamingAdmin/Ban/control.lua rename to old/modules/ExpGamingAdmin/Ban/control.lua diff --git a/modules/ExpGamingAdmin/Ban/softmod.json b/old/modules/ExpGamingAdmin/Ban/softmod.json similarity index 100% rename from modules/ExpGamingAdmin/Ban/softmod.json rename to old/modules/ExpGamingAdmin/Ban/softmod.json diff --git a/modules/ExpGamingAdmin/ClearInventory/control.lua b/old/modules/ExpGamingAdmin/ClearInventory/control.lua similarity index 100% rename from modules/ExpGamingAdmin/ClearInventory/control.lua rename to old/modules/ExpGamingAdmin/ClearInventory/control.lua diff --git a/modules/ExpGamingAdmin/ClearInventory/softmod.json b/old/modules/ExpGamingAdmin/ClearInventory/softmod.json similarity index 100% rename from modules/ExpGamingAdmin/ClearInventory/softmod.json rename to old/modules/ExpGamingAdmin/ClearInventory/softmod.json diff --git a/modules/ExpGamingAdmin/Commands/control.lua b/old/modules/ExpGamingAdmin/Commands/control.lua similarity index 100% rename from modules/ExpGamingAdmin/Commands/control.lua rename to old/modules/ExpGamingAdmin/Commands/control.lua diff --git a/modules/ExpGamingAdmin/Commands/softmod.json b/old/modules/ExpGamingAdmin/Commands/softmod.json similarity index 100% rename from modules/ExpGamingAdmin/Commands/softmod.json rename to old/modules/ExpGamingAdmin/Commands/softmod.json diff --git a/modules/ExpGamingAdmin/Commands/src/clear.lua b/old/modules/ExpGamingAdmin/Commands/src/clear.lua similarity index 100% rename from modules/ExpGamingAdmin/Commands/src/clear.lua rename to old/modules/ExpGamingAdmin/Commands/src/clear.lua diff --git a/modules/ExpGamingAdmin/Commands/src/jail.lua b/old/modules/ExpGamingAdmin/Commands/src/jail.lua similarity index 100% rename from modules/ExpGamingAdmin/Commands/src/jail.lua rename to old/modules/ExpGamingAdmin/Commands/src/jail.lua diff --git a/modules/ExpGamingAdmin/Commands/src/reports.lua b/old/modules/ExpGamingAdmin/Commands/src/reports.lua similarity index 100% rename from modules/ExpGamingAdmin/Commands/src/reports.lua rename to old/modules/ExpGamingAdmin/Commands/src/reports.lua diff --git a/modules/ExpGamingAdmin/Commands/src/tempban.lua b/old/modules/ExpGamingAdmin/Commands/src/tempban.lua similarity index 100% rename from modules/ExpGamingAdmin/Commands/src/tempban.lua rename to old/modules/ExpGamingAdmin/Commands/src/tempban.lua diff --git a/modules/ExpGamingAdmin/Commands/src/warnings.lua b/old/modules/ExpGamingAdmin/Commands/src/warnings.lua similarity index 100% rename from modules/ExpGamingAdmin/Commands/src/warnings.lua rename to old/modules/ExpGamingAdmin/Commands/src/warnings.lua diff --git a/modules/ExpGamingAdmin/Gui/control.lua b/old/modules/ExpGamingAdmin/Gui/control.lua similarity index 100% rename from modules/ExpGamingAdmin/Gui/control.lua rename to old/modules/ExpGamingAdmin/Gui/control.lua diff --git a/modules/ExpGamingAdmin/Gui/softmod.json b/old/modules/ExpGamingAdmin/Gui/softmod.json similarity index 100% rename from modules/ExpGamingAdmin/Gui/softmod.json rename to old/modules/ExpGamingAdmin/Gui/softmod.json diff --git a/modules/ExpGamingAdmin/Jail/control.lua b/old/modules/ExpGamingAdmin/Jail/control.lua similarity index 100% rename from modules/ExpGamingAdmin/Jail/control.lua rename to old/modules/ExpGamingAdmin/Jail/control.lua diff --git a/modules/ExpGamingAdmin/Jail/softmod.json b/old/modules/ExpGamingAdmin/Jail/softmod.json similarity index 100% rename from modules/ExpGamingAdmin/Jail/softmod.json rename to old/modules/ExpGamingAdmin/Jail/softmod.json diff --git a/modules/ExpGamingAdmin/Kick/control.lua b/old/modules/ExpGamingAdmin/Kick/control.lua similarity index 100% rename from modules/ExpGamingAdmin/Kick/control.lua rename to old/modules/ExpGamingAdmin/Kick/control.lua diff --git a/modules/ExpGamingAdmin/Kick/softmod.json b/old/modules/ExpGamingAdmin/Kick/softmod.json similarity index 100% rename from modules/ExpGamingAdmin/Kick/softmod.json rename to old/modules/ExpGamingAdmin/Kick/softmod.json diff --git a/modules/ExpGamingAdmin/Reports/control.lua b/old/modules/ExpGamingAdmin/Reports/control.lua similarity index 100% rename from modules/ExpGamingAdmin/Reports/control.lua rename to old/modules/ExpGamingAdmin/Reports/control.lua diff --git a/modules/ExpGamingAdmin/Reports/softmod.json b/old/modules/ExpGamingAdmin/Reports/softmod.json similarity index 100% rename from modules/ExpGamingAdmin/Reports/softmod.json rename to old/modules/ExpGamingAdmin/Reports/softmod.json diff --git a/modules/ExpGamingAdmin/Teleport/control.lua b/old/modules/ExpGamingAdmin/Teleport/control.lua similarity index 100% rename from modules/ExpGamingAdmin/Teleport/control.lua rename to old/modules/ExpGamingAdmin/Teleport/control.lua diff --git a/modules/ExpGamingAdmin/Teleport/softmod.json b/old/modules/ExpGamingAdmin/Teleport/softmod.json similarity index 100% rename from modules/ExpGamingAdmin/Teleport/softmod.json rename to old/modules/ExpGamingAdmin/Teleport/softmod.json diff --git a/modules/ExpGamingAdmin/TempBan/control.lua b/old/modules/ExpGamingAdmin/TempBan/control.lua similarity index 100% rename from modules/ExpGamingAdmin/TempBan/control.lua rename to old/modules/ExpGamingAdmin/TempBan/control.lua diff --git a/modules/ExpGamingAdmin/TempBan/softmod.json b/old/modules/ExpGamingAdmin/TempBan/softmod.json similarity index 100% rename from modules/ExpGamingAdmin/TempBan/softmod.json rename to old/modules/ExpGamingAdmin/TempBan/softmod.json diff --git a/modules/ExpGamingAdmin/Warnings/control.lua b/old/modules/ExpGamingAdmin/Warnings/control.lua similarity index 100% rename from modules/ExpGamingAdmin/Warnings/control.lua rename to old/modules/ExpGamingAdmin/Warnings/control.lua diff --git a/modules/ExpGamingAdmin/Warnings/locale/de.cfg b/old/modules/ExpGamingAdmin/Warnings/locale/de.cfg similarity index 100% rename from modules/ExpGamingAdmin/Warnings/locale/de.cfg rename to old/modules/ExpGamingAdmin/Warnings/locale/de.cfg diff --git a/modules/ExpGamingAdmin/Warnings/locale/en.cfg b/old/modules/ExpGamingAdmin/Warnings/locale/en.cfg similarity index 100% rename from modules/ExpGamingAdmin/Warnings/locale/en.cfg rename to old/modules/ExpGamingAdmin/Warnings/locale/en.cfg diff --git a/modules/ExpGamingAdmin/Warnings/locale/fr.cfg b/old/modules/ExpGamingAdmin/Warnings/locale/fr.cfg similarity index 100% rename from modules/ExpGamingAdmin/Warnings/locale/fr.cfg rename to old/modules/ExpGamingAdmin/Warnings/locale/fr.cfg diff --git a/modules/ExpGamingAdmin/Warnings/locale/nl.cfg b/old/modules/ExpGamingAdmin/Warnings/locale/nl.cfg similarity index 100% rename from modules/ExpGamingAdmin/Warnings/locale/nl.cfg rename to old/modules/ExpGamingAdmin/Warnings/locale/nl.cfg diff --git a/modules/ExpGamingAdmin/Warnings/locale/sv-SE.cfg b/old/modules/ExpGamingAdmin/Warnings/locale/sv-SE.cfg similarity index 100% rename from modules/ExpGamingAdmin/Warnings/locale/sv-SE.cfg rename to old/modules/ExpGamingAdmin/Warnings/locale/sv-SE.cfg diff --git a/modules/ExpGamingAdmin/Warnings/softmod.json b/old/modules/ExpGamingAdmin/Warnings/softmod.json similarity index 100% rename from modules/ExpGamingAdmin/Warnings/softmod.json rename to old/modules/ExpGamingAdmin/Warnings/softmod.json diff --git a/modules/ExpGamingAdmin/control.lua b/old/modules/ExpGamingAdmin/control.lua similarity index 100% rename from modules/ExpGamingAdmin/control.lua rename to old/modules/ExpGamingAdmin/control.lua diff --git a/modules/ExpGamingAdmin/locale/de.cfg b/old/modules/ExpGamingAdmin/locale/de.cfg similarity index 100% rename from modules/ExpGamingAdmin/locale/de.cfg rename to old/modules/ExpGamingAdmin/locale/de.cfg diff --git a/modules/ExpGamingAdmin/locale/en.cfg b/old/modules/ExpGamingAdmin/locale/en.cfg similarity index 100% rename from modules/ExpGamingAdmin/locale/en.cfg rename to old/modules/ExpGamingAdmin/locale/en.cfg diff --git a/modules/ExpGamingAdmin/locale/fr.cfg b/old/modules/ExpGamingAdmin/locale/fr.cfg similarity index 100% rename from modules/ExpGamingAdmin/locale/fr.cfg rename to old/modules/ExpGamingAdmin/locale/fr.cfg diff --git a/modules/ExpGamingAdmin/locale/nl.cfg b/old/modules/ExpGamingAdmin/locale/nl.cfg similarity index 100% rename from modules/ExpGamingAdmin/locale/nl.cfg rename to old/modules/ExpGamingAdmin/locale/nl.cfg diff --git a/modules/ExpGamingAdmin/locale/sv-SE.cfg b/old/modules/ExpGamingAdmin/locale/sv-SE.cfg similarity index 100% rename from modules/ExpGamingAdmin/locale/sv-SE.cfg rename to old/modules/ExpGamingAdmin/locale/sv-SE.cfg diff --git a/modules/ExpGamingAdmin/softmod.json b/old/modules/ExpGamingAdmin/softmod.json similarity index 100% rename from modules/ExpGamingAdmin/softmod.json rename to old/modules/ExpGamingAdmin/softmod.json diff --git a/modules/ExpGamingBot/autoChat/control.lua b/old/modules/ExpGamingBot/autoChat/control.lua similarity index 100% rename from modules/ExpGamingBot/autoChat/control.lua rename to old/modules/ExpGamingBot/autoChat/control.lua diff --git a/modules/ExpGamingBot/autoChat/locale/de.cfg b/old/modules/ExpGamingBot/autoChat/locale/de.cfg similarity index 100% rename from modules/ExpGamingBot/autoChat/locale/de.cfg rename to old/modules/ExpGamingBot/autoChat/locale/de.cfg diff --git a/modules/ExpGamingBot/autoChat/locale/en.cfg b/old/modules/ExpGamingBot/autoChat/locale/en.cfg similarity index 100% rename from modules/ExpGamingBot/autoChat/locale/en.cfg rename to old/modules/ExpGamingBot/autoChat/locale/en.cfg diff --git a/modules/ExpGamingBot/autoChat/locale/fr.cfg b/old/modules/ExpGamingBot/autoChat/locale/fr.cfg similarity index 100% rename from modules/ExpGamingBot/autoChat/locale/fr.cfg rename to old/modules/ExpGamingBot/autoChat/locale/fr.cfg diff --git a/modules/ExpGamingBot/autoChat/locale/nl.cfg b/old/modules/ExpGamingBot/autoChat/locale/nl.cfg similarity index 100% rename from modules/ExpGamingBot/autoChat/locale/nl.cfg rename to old/modules/ExpGamingBot/autoChat/locale/nl.cfg diff --git a/modules/ExpGamingBot/autoChat/locale/sv-SE.cfg b/old/modules/ExpGamingBot/autoChat/locale/sv-SE.cfg similarity index 100% rename from modules/ExpGamingBot/autoChat/locale/sv-SE.cfg rename to old/modules/ExpGamingBot/autoChat/locale/sv-SE.cfg diff --git a/modules/ExpGamingBot/autoChat/softmod.json b/old/modules/ExpGamingBot/autoChat/softmod.json similarity index 100% rename from modules/ExpGamingBot/autoChat/softmod.json rename to old/modules/ExpGamingBot/autoChat/softmod.json diff --git a/modules/ExpGamingBot/autoMessage/control.lua b/old/modules/ExpGamingBot/autoMessage/control.lua similarity index 100% rename from modules/ExpGamingBot/autoMessage/control.lua rename to old/modules/ExpGamingBot/autoMessage/control.lua diff --git a/modules/ExpGamingBot/autoMessage/locale/de.cfg b/old/modules/ExpGamingBot/autoMessage/locale/de.cfg similarity index 100% rename from modules/ExpGamingBot/autoMessage/locale/de.cfg rename to old/modules/ExpGamingBot/autoMessage/locale/de.cfg diff --git a/modules/ExpGamingBot/autoMessage/locale/en.cfg b/old/modules/ExpGamingBot/autoMessage/locale/en.cfg similarity index 100% rename from modules/ExpGamingBot/autoMessage/locale/en.cfg rename to old/modules/ExpGamingBot/autoMessage/locale/en.cfg diff --git a/modules/ExpGamingBot/autoMessage/locale/fr.cfg b/old/modules/ExpGamingBot/autoMessage/locale/fr.cfg similarity index 100% rename from modules/ExpGamingBot/autoMessage/locale/fr.cfg rename to old/modules/ExpGamingBot/autoMessage/locale/fr.cfg diff --git a/modules/ExpGamingBot/autoMessage/locale/nl.cfg b/old/modules/ExpGamingBot/autoMessage/locale/nl.cfg similarity index 100% rename from modules/ExpGamingBot/autoMessage/locale/nl.cfg rename to old/modules/ExpGamingBot/autoMessage/locale/nl.cfg diff --git a/modules/ExpGamingBot/autoMessage/locale/sv-SE.cfg b/old/modules/ExpGamingBot/autoMessage/locale/sv-SE.cfg similarity index 100% rename from modules/ExpGamingBot/autoMessage/locale/sv-SE.cfg rename to old/modules/ExpGamingBot/autoMessage/locale/sv-SE.cfg diff --git a/modules/ExpGamingBot/autoMessage/softmod.json b/old/modules/ExpGamingBot/autoMessage/softmod.json similarity index 100% rename from modules/ExpGamingBot/autoMessage/softmod.json rename to old/modules/ExpGamingBot/autoMessage/softmod.json diff --git a/modules/ExpGamingBot/discordAlerts/control.lua b/old/modules/ExpGamingBot/discordAlerts/control.lua similarity index 100% rename from modules/ExpGamingBot/discordAlerts/control.lua rename to old/modules/ExpGamingBot/discordAlerts/control.lua diff --git a/modules/ExpGamingBot/discordAlerts/softmod.json b/old/modules/ExpGamingBot/discordAlerts/softmod.json similarity index 100% rename from modules/ExpGamingBot/discordAlerts/softmod.json rename to old/modules/ExpGamingBot/discordAlerts/softmod.json diff --git a/modules/ExpGamingBot/softmod.json b/old/modules/ExpGamingBot/softmod.json similarity index 100% rename from modules/ExpGamingBot/softmod.json rename to old/modules/ExpGamingBot/softmod.json diff --git a/modules/ExpGamingCommands/bonus/control.lua b/old/modules/ExpGamingCommands/bonus/control.lua similarity index 100% rename from modules/ExpGamingCommands/bonus/control.lua rename to old/modules/ExpGamingCommands/bonus/control.lua diff --git a/modules/ExpGamingCommands/bonus/softmod.json b/old/modules/ExpGamingCommands/bonus/softmod.json similarity index 100% rename from modules/ExpGamingCommands/bonus/softmod.json rename to old/modules/ExpGamingCommands/bonus/softmod.json diff --git a/modules/ExpGamingCommands/cheatMode/control.lua b/old/modules/ExpGamingCommands/cheatMode/control.lua similarity index 100% rename from modules/ExpGamingCommands/cheatMode/control.lua rename to old/modules/ExpGamingCommands/cheatMode/control.lua diff --git a/modules/ExpGamingCommands/cheatMode/softmod.json b/old/modules/ExpGamingCommands/cheatMode/softmod.json similarity index 100% rename from modules/ExpGamingCommands/cheatMode/softmod.json rename to old/modules/ExpGamingCommands/cheatMode/softmod.json diff --git a/modules/ExpGamingCommands/home/control.lua b/old/modules/ExpGamingCommands/home/control.lua similarity index 100% rename from modules/ExpGamingCommands/home/control.lua rename to old/modules/ExpGamingCommands/home/control.lua diff --git a/modules/ExpGamingCommands/home/locale/en.cfg b/old/modules/ExpGamingCommands/home/locale/en.cfg similarity index 100% rename from modules/ExpGamingCommands/home/locale/en.cfg rename to old/modules/ExpGamingCommands/home/locale/en.cfg diff --git a/modules/ExpGamingCommands/home/softmod.json b/old/modules/ExpGamingCommands/home/softmod.json similarity index 100% rename from modules/ExpGamingCommands/home/softmod.json rename to old/modules/ExpGamingCommands/home/softmod.json diff --git a/modules/ExpGamingCommands/kill/control.lua b/old/modules/ExpGamingCommands/kill/control.lua similarity index 100% rename from modules/ExpGamingCommands/kill/control.lua rename to old/modules/ExpGamingCommands/kill/control.lua diff --git a/modules/ExpGamingCommands/kill/softmod.json b/old/modules/ExpGamingCommands/kill/softmod.json similarity index 100% rename from modules/ExpGamingCommands/kill/softmod.json rename to old/modules/ExpGamingCommands/kill/softmod.json diff --git a/modules/ExpGamingCommands/repair/control.lua b/old/modules/ExpGamingCommands/repair/control.lua similarity index 100% rename from modules/ExpGamingCommands/repair/control.lua rename to old/modules/ExpGamingCommands/repair/control.lua diff --git a/modules/ExpGamingCommands/repair/softmod.json b/old/modules/ExpGamingCommands/repair/softmod.json similarity index 100% rename from modules/ExpGamingCommands/repair/softmod.json rename to old/modules/ExpGamingCommands/repair/softmod.json diff --git a/modules/ExpGamingCommands/repair/src/tempban.lua b/old/modules/ExpGamingCommands/repair/src/tempban.lua similarity index 100% rename from modules/ExpGamingCommands/repair/src/tempban.lua rename to old/modules/ExpGamingCommands/repair/src/tempban.lua diff --git a/modules/ExpGamingCommands/softmod.json b/old/modules/ExpGamingCommands/softmod.json similarity index 100% rename from modules/ExpGamingCommands/softmod.json rename to old/modules/ExpGamingCommands/softmod.json diff --git a/modules/ExpGamingCommands/tags/control.lua b/old/modules/ExpGamingCommands/tags/control.lua similarity index 100% rename from modules/ExpGamingCommands/tags/control.lua rename to old/modules/ExpGamingCommands/tags/control.lua diff --git a/modules/ExpGamingCommands/tags/softmod.json b/old/modules/ExpGamingCommands/tags/softmod.json similarity index 100% rename from modules/ExpGamingCommands/tags/softmod.json rename to old/modules/ExpGamingCommands/tags/softmod.json diff --git a/modules/ExpGamingCommands/teleport/control.lua b/old/modules/ExpGamingCommands/teleport/control.lua similarity index 100% rename from modules/ExpGamingCommands/teleport/control.lua rename to old/modules/ExpGamingCommands/teleport/control.lua diff --git a/modules/ExpGamingCommands/teleport/softmod.json b/old/modules/ExpGamingCommands/teleport/softmod.json similarity index 100% rename from modules/ExpGamingCommands/teleport/softmod.json rename to old/modules/ExpGamingCommands/teleport/softmod.json diff --git a/modules/ExpGamingCore/Command/control.lua b/old/modules/ExpGamingCore/Command/control.lua similarity index 100% rename from modules/ExpGamingCore/Command/control.lua rename to old/modules/ExpGamingCore/Command/control.lua diff --git a/modules/ExpGamingCore/Command/softmod.json b/old/modules/ExpGamingCore/Command/softmod.json similarity index 100% rename from modules/ExpGamingCore/Command/softmod.json rename to old/modules/ExpGamingCore/Command/softmod.json diff --git a/modules/ExpGamingCore/Group/config.lua b/old/modules/ExpGamingCore/Group/config.lua similarity index 100% rename from modules/ExpGamingCore/Group/config.lua rename to old/modules/ExpGamingCore/Group/config.lua diff --git a/modules/ExpGamingCore/Group/control.lua b/old/modules/ExpGamingCore/Group/control.lua similarity index 100% rename from modules/ExpGamingCore/Group/control.lua rename to old/modules/ExpGamingCore/Group/control.lua diff --git a/modules/ExpGamingCore/Group/softmod.json b/old/modules/ExpGamingCore/Group/softmod.json similarity index 100% rename from modules/ExpGamingCore/Group/softmod.json rename to old/modules/ExpGamingCore/Group/softmod.json diff --git a/modules/ExpGamingCore/Gui/center/control.lua b/old/modules/ExpGamingCore/Gui/center/control.lua similarity index 100% rename from modules/ExpGamingCore/Gui/center/control.lua rename to old/modules/ExpGamingCore/Gui/center/control.lua diff --git a/modules/ExpGamingCore/Gui/center/softmod.json b/old/modules/ExpGamingCore/Gui/center/softmod.json similarity index 100% rename from modules/ExpGamingCore/Gui/center/softmod.json rename to old/modules/ExpGamingCore/Gui/center/softmod.json diff --git a/modules/ExpGamingCore/Gui/control.lua b/old/modules/ExpGamingCore/Gui/control.lua similarity index 100% rename from modules/ExpGamingCore/Gui/control.lua rename to old/modules/ExpGamingCore/Gui/control.lua diff --git a/modules/ExpGamingCore/Gui/inputs/control.lua b/old/modules/ExpGamingCore/Gui/inputs/control.lua similarity index 100% rename from modules/ExpGamingCore/Gui/inputs/control.lua rename to old/modules/ExpGamingCore/Gui/inputs/control.lua diff --git a/modules/ExpGamingCore/Gui/inputs/softmod.json b/old/modules/ExpGamingCore/Gui/inputs/softmod.json similarity index 100% rename from modules/ExpGamingCore/Gui/inputs/softmod.json rename to old/modules/ExpGamingCore/Gui/inputs/softmod.json diff --git a/modules/ExpGamingCore/Gui/left/control.lua b/old/modules/ExpGamingCore/Gui/left/control.lua similarity index 100% rename from modules/ExpGamingCore/Gui/left/control.lua rename to old/modules/ExpGamingCore/Gui/left/control.lua diff --git a/modules/ExpGamingCore/Gui/left/order_config.lua b/old/modules/ExpGamingCore/Gui/left/order_config.lua similarity index 100% rename from modules/ExpGamingCore/Gui/left/order_config.lua rename to old/modules/ExpGamingCore/Gui/left/order_config.lua diff --git a/modules/ExpGamingCore/Gui/left/softmod.json b/old/modules/ExpGamingCore/Gui/left/softmod.json similarity index 100% rename from modules/ExpGamingCore/Gui/left/softmod.json rename to old/modules/ExpGamingCore/Gui/left/softmod.json diff --git a/modules/ExpGamingCore/Gui/locale/de.cfg b/old/modules/ExpGamingCore/Gui/locale/de.cfg similarity index 100% rename from modules/ExpGamingCore/Gui/locale/de.cfg rename to old/modules/ExpGamingCore/Gui/locale/de.cfg diff --git a/modules/ExpGamingCore/Gui/locale/en.cfg b/old/modules/ExpGamingCore/Gui/locale/en.cfg similarity index 100% rename from modules/ExpGamingCore/Gui/locale/en.cfg rename to old/modules/ExpGamingCore/Gui/locale/en.cfg diff --git a/modules/ExpGamingCore/Gui/locale/fr.cfg b/old/modules/ExpGamingCore/Gui/locale/fr.cfg similarity index 100% rename from modules/ExpGamingCore/Gui/locale/fr.cfg rename to old/modules/ExpGamingCore/Gui/locale/fr.cfg diff --git a/modules/ExpGamingCore/Gui/locale/nl.cfg b/old/modules/ExpGamingCore/Gui/locale/nl.cfg similarity index 100% rename from modules/ExpGamingCore/Gui/locale/nl.cfg rename to old/modules/ExpGamingCore/Gui/locale/nl.cfg diff --git a/modules/ExpGamingCore/Gui/locale/sv-SE.cfg b/old/modules/ExpGamingCore/Gui/locale/sv-SE.cfg similarity index 100% rename from modules/ExpGamingCore/Gui/locale/sv-SE.cfg rename to old/modules/ExpGamingCore/Gui/locale/sv-SE.cfg diff --git a/modules/ExpGamingCore/Gui/popup/control.lua b/old/modules/ExpGamingCore/Gui/popup/control.lua similarity index 100% rename from modules/ExpGamingCore/Gui/popup/control.lua rename to old/modules/ExpGamingCore/Gui/popup/control.lua diff --git a/modules/ExpGamingCore/Gui/popup/softmod.json b/old/modules/ExpGamingCore/Gui/popup/softmod.json similarity index 100% rename from modules/ExpGamingCore/Gui/popup/softmod.json rename to old/modules/ExpGamingCore/Gui/popup/softmod.json diff --git a/modules/ExpGamingCore/Gui/softmod.json b/old/modules/ExpGamingCore/Gui/softmod.json similarity index 100% rename from modules/ExpGamingCore/Gui/softmod.json rename to old/modules/ExpGamingCore/Gui/softmod.json diff --git a/modules/ExpGamingCore/Gui/src/server.lua b/old/modules/ExpGamingCore/Gui/src/server.lua similarity index 100% rename from modules/ExpGamingCore/Gui/src/server.lua rename to old/modules/ExpGamingCore/Gui/src/server.lua diff --git a/modules/ExpGamingCore/Gui/src/test.lua b/old/modules/ExpGamingCore/Gui/src/test.lua similarity index 100% rename from modules/ExpGamingCore/Gui/src/test.lua rename to old/modules/ExpGamingCore/Gui/src/test.lua diff --git a/modules/ExpGamingCore/Gui/toolbar/control.lua b/old/modules/ExpGamingCore/Gui/toolbar/control.lua similarity index 100% rename from modules/ExpGamingCore/Gui/toolbar/control.lua rename to old/modules/ExpGamingCore/Gui/toolbar/control.lua diff --git a/modules/ExpGamingCore/Gui/toolbar/order_config.lua b/old/modules/ExpGamingCore/Gui/toolbar/order_config.lua similarity index 100% rename from modules/ExpGamingCore/Gui/toolbar/order_config.lua rename to old/modules/ExpGamingCore/Gui/toolbar/order_config.lua diff --git a/modules/ExpGamingCore/Gui/toolbar/softmod.json b/old/modules/ExpGamingCore/Gui/toolbar/softmod.json similarity index 100% rename from modules/ExpGamingCore/Gui/toolbar/softmod.json rename to old/modules/ExpGamingCore/Gui/toolbar/softmod.json diff --git a/modules/ExpGamingCore/Role/config.lua b/old/modules/ExpGamingCore/Role/config.lua similarity index 100% rename from modules/ExpGamingCore/Role/config.lua rename to old/modules/ExpGamingCore/Role/config.lua diff --git a/modules/ExpGamingCore/Role/control.lua b/old/modules/ExpGamingCore/Role/control.lua similarity index 100% rename from modules/ExpGamingCore/Role/control.lua rename to old/modules/ExpGamingCore/Role/control.lua diff --git a/modules/ExpGamingCore/Role/locale/en.cfg b/old/modules/ExpGamingCore/Role/locale/en.cfg similarity index 100% rename from modules/ExpGamingCore/Role/locale/en.cfg rename to old/modules/ExpGamingCore/Role/locale/en.cfg diff --git a/modules/ExpGamingCore/Role/softmod.json b/old/modules/ExpGamingCore/Role/softmod.json similarity index 100% rename from modules/ExpGamingCore/Role/softmod.json rename to old/modules/ExpGamingCore/Role/softmod.json diff --git a/modules/ExpGamingCore/Role/src/commands.lua b/old/modules/ExpGamingCore/Role/src/commands.lua similarity index 100% rename from modules/ExpGamingCore/Role/src/commands.lua rename to old/modules/ExpGamingCore/Role/src/commands.lua diff --git a/modules/ExpGamingCore/Role/src/sync.lua b/old/modules/ExpGamingCore/Role/src/sync.lua similarity index 100% rename from modules/ExpGamingCore/Role/src/sync.lua rename to old/modules/ExpGamingCore/Role/src/sync.lua diff --git a/modules/ExpGamingCore/Server/control.lua b/old/modules/ExpGamingCore/Server/control.lua similarity index 100% rename from modules/ExpGamingCore/Server/control.lua rename to old/modules/ExpGamingCore/Server/control.lua diff --git a/modules/ExpGamingCore/Server/softmod.json b/old/modules/ExpGamingCore/Server/softmod.json similarity index 100% rename from modules/ExpGamingCore/Server/softmod.json rename to old/modules/ExpGamingCore/Server/softmod.json diff --git a/modules/ExpGamingCore/Server/src/commands.lua b/old/modules/ExpGamingCore/Server/src/commands.lua similarity index 100% rename from modules/ExpGamingCore/Server/src/commands.lua rename to old/modules/ExpGamingCore/Server/src/commands.lua diff --git a/modules/ExpGamingCore/Sync/control.lua b/old/modules/ExpGamingCore/Sync/control.lua similarity index 100% rename from modules/ExpGamingCore/Sync/control.lua rename to old/modules/ExpGamingCore/Sync/control.lua diff --git a/modules/ExpGamingCore/Sync/softmod.json b/old/modules/ExpGamingCore/Sync/softmod.json similarity index 100% rename from modules/ExpGamingCore/Sync/softmod.json rename to old/modules/ExpGamingCore/Sync/softmod.json diff --git a/modules/ExpGamingCore/Sync/src/gui.lua b/old/modules/ExpGamingCore/Sync/src/gui.lua similarity index 100% rename from modules/ExpGamingCore/Sync/src/gui.lua rename to old/modules/ExpGamingCore/Sync/src/gui.lua diff --git a/modules/ExpGamingCore/Sync/src/logo.png b/old/modules/ExpGamingCore/Sync/src/logo.png similarity index 100% rename from modules/ExpGamingCore/Sync/src/logo.png rename to old/modules/ExpGamingCore/Sync/src/logo.png diff --git a/modules/ExpGamingCore/softmod.json b/old/modules/ExpGamingCore/softmod.json similarity index 100% rename from modules/ExpGamingCore/softmod.json rename to old/modules/ExpGamingCore/softmod.json diff --git a/modules/ExpGamingInfo/Readme/control.lua b/old/modules/ExpGamingInfo/Readme/control.lua similarity index 100% rename from modules/ExpGamingInfo/Readme/control.lua rename to old/modules/ExpGamingInfo/Readme/control.lua diff --git a/modules/ExpGamingInfo/Readme/locale/de.cfg b/old/modules/ExpGamingInfo/Readme/locale/de.cfg similarity index 100% rename from modules/ExpGamingInfo/Readme/locale/de.cfg rename to old/modules/ExpGamingInfo/Readme/locale/de.cfg diff --git a/modules/ExpGamingInfo/Readme/locale/en.cfg b/old/modules/ExpGamingInfo/Readme/locale/en.cfg similarity index 100% rename from modules/ExpGamingInfo/Readme/locale/en.cfg rename to old/modules/ExpGamingInfo/Readme/locale/en.cfg diff --git a/modules/ExpGamingInfo/Readme/locale/fr.cfg b/old/modules/ExpGamingInfo/Readme/locale/fr.cfg similarity index 100% rename from modules/ExpGamingInfo/Readme/locale/fr.cfg rename to old/modules/ExpGamingInfo/Readme/locale/fr.cfg diff --git a/modules/ExpGamingInfo/Readme/locale/nl.cfg b/old/modules/ExpGamingInfo/Readme/locale/nl.cfg similarity index 100% rename from modules/ExpGamingInfo/Readme/locale/nl.cfg rename to old/modules/ExpGamingInfo/Readme/locale/nl.cfg diff --git a/modules/ExpGamingInfo/Readme/locale/sv-SE.cfg b/old/modules/ExpGamingInfo/Readme/locale/sv-SE.cfg similarity index 100% rename from modules/ExpGamingInfo/Readme/locale/sv-SE.cfg rename to old/modules/ExpGamingInfo/Readme/locale/sv-SE.cfg diff --git a/modules/ExpGamingInfo/Readme/softmod.json b/old/modules/ExpGamingInfo/Readme/softmod.json similarity index 100% rename from modules/ExpGamingInfo/Readme/softmod.json rename to old/modules/ExpGamingInfo/Readme/softmod.json diff --git a/modules/ExpGamingInfo/Readme/src/sync.lua b/old/modules/ExpGamingInfo/Readme/src/sync.lua similarity index 100% rename from modules/ExpGamingInfo/Readme/src/sync.lua rename to old/modules/ExpGamingInfo/Readme/src/sync.lua diff --git a/modules/ExpGamingInfo/Rockets/control.lua b/old/modules/ExpGamingInfo/Rockets/control.lua similarity index 100% rename from modules/ExpGamingInfo/Rockets/control.lua rename to old/modules/ExpGamingInfo/Rockets/control.lua diff --git a/modules/ExpGamingInfo/Rockets/locale/de.cfg b/old/modules/ExpGamingInfo/Rockets/locale/de.cfg similarity index 100% rename from modules/ExpGamingInfo/Rockets/locale/de.cfg rename to old/modules/ExpGamingInfo/Rockets/locale/de.cfg diff --git a/modules/ExpGamingInfo/Rockets/locale/en.cfg b/old/modules/ExpGamingInfo/Rockets/locale/en.cfg similarity index 100% rename from modules/ExpGamingInfo/Rockets/locale/en.cfg rename to old/modules/ExpGamingInfo/Rockets/locale/en.cfg diff --git a/modules/ExpGamingInfo/Rockets/locale/fr.cfg b/old/modules/ExpGamingInfo/Rockets/locale/fr.cfg similarity index 100% rename from modules/ExpGamingInfo/Rockets/locale/fr.cfg rename to old/modules/ExpGamingInfo/Rockets/locale/fr.cfg diff --git a/modules/ExpGamingInfo/Rockets/locale/nl.cfg b/old/modules/ExpGamingInfo/Rockets/locale/nl.cfg similarity index 100% rename from modules/ExpGamingInfo/Rockets/locale/nl.cfg rename to old/modules/ExpGamingInfo/Rockets/locale/nl.cfg diff --git a/modules/ExpGamingInfo/Rockets/locale/sv-SE.cfg b/old/modules/ExpGamingInfo/Rockets/locale/sv-SE.cfg similarity index 100% rename from modules/ExpGamingInfo/Rockets/locale/sv-SE.cfg rename to old/modules/ExpGamingInfo/Rockets/locale/sv-SE.cfg diff --git a/modules/ExpGamingInfo/Rockets/softmod.json b/old/modules/ExpGamingInfo/Rockets/softmod.json similarity index 100% rename from modules/ExpGamingInfo/Rockets/softmod.json rename to old/modules/ExpGamingInfo/Rockets/softmod.json diff --git a/modules/ExpGamingInfo/Rockets/src/sync.lua b/old/modules/ExpGamingInfo/Rockets/src/sync.lua similarity index 100% rename from modules/ExpGamingInfo/Rockets/src/sync.lua rename to old/modules/ExpGamingInfo/Rockets/src/sync.lua diff --git a/modules/ExpGamingInfo/Science/control.lua b/old/modules/ExpGamingInfo/Science/control.lua similarity index 100% rename from modules/ExpGamingInfo/Science/control.lua rename to old/modules/ExpGamingInfo/Science/control.lua diff --git a/modules/ExpGamingInfo/Science/locale/de.cfg b/old/modules/ExpGamingInfo/Science/locale/de.cfg similarity index 100% rename from modules/ExpGamingInfo/Science/locale/de.cfg rename to old/modules/ExpGamingInfo/Science/locale/de.cfg diff --git a/modules/ExpGamingInfo/Science/locale/en.cfg b/old/modules/ExpGamingInfo/Science/locale/en.cfg similarity index 100% rename from modules/ExpGamingInfo/Science/locale/en.cfg rename to old/modules/ExpGamingInfo/Science/locale/en.cfg diff --git a/modules/ExpGamingInfo/Science/locale/fr.cfg b/old/modules/ExpGamingInfo/Science/locale/fr.cfg similarity index 100% rename from modules/ExpGamingInfo/Science/locale/fr.cfg rename to old/modules/ExpGamingInfo/Science/locale/fr.cfg diff --git a/modules/ExpGamingInfo/Science/locale/nl.cfg b/old/modules/ExpGamingInfo/Science/locale/nl.cfg similarity index 100% rename from modules/ExpGamingInfo/Science/locale/nl.cfg rename to old/modules/ExpGamingInfo/Science/locale/nl.cfg diff --git a/modules/ExpGamingInfo/Science/locale/sv-Se.cfg b/old/modules/ExpGamingInfo/Science/locale/sv-Se.cfg similarity index 100% rename from modules/ExpGamingInfo/Science/locale/sv-Se.cfg rename to old/modules/ExpGamingInfo/Science/locale/sv-Se.cfg diff --git a/modules/ExpGamingInfo/Science/softmod.json b/old/modules/ExpGamingInfo/Science/softmod.json similarity index 100% rename from modules/ExpGamingInfo/Science/softmod.json rename to old/modules/ExpGamingInfo/Science/softmod.json diff --git a/modules/ExpGamingInfo/Science/src/sync.lua b/old/modules/ExpGamingInfo/Science/src/sync.lua similarity index 100% rename from modules/ExpGamingInfo/Science/src/sync.lua rename to old/modules/ExpGamingInfo/Science/src/sync.lua diff --git a/modules/ExpGamingInfo/Tasklist/control.lua b/old/modules/ExpGamingInfo/Tasklist/control.lua similarity index 100% rename from modules/ExpGamingInfo/Tasklist/control.lua rename to old/modules/ExpGamingInfo/Tasklist/control.lua diff --git a/modules/ExpGamingInfo/Tasklist/locale/de.cfg b/old/modules/ExpGamingInfo/Tasklist/locale/de.cfg similarity index 100% rename from modules/ExpGamingInfo/Tasklist/locale/de.cfg rename to old/modules/ExpGamingInfo/Tasklist/locale/de.cfg diff --git a/modules/ExpGamingInfo/Tasklist/locale/en.cfg b/old/modules/ExpGamingInfo/Tasklist/locale/en.cfg similarity index 100% rename from modules/ExpGamingInfo/Tasklist/locale/en.cfg rename to old/modules/ExpGamingInfo/Tasklist/locale/en.cfg diff --git a/modules/ExpGamingInfo/Tasklist/locale/fr.cfg b/old/modules/ExpGamingInfo/Tasklist/locale/fr.cfg similarity index 100% rename from modules/ExpGamingInfo/Tasklist/locale/fr.cfg rename to old/modules/ExpGamingInfo/Tasklist/locale/fr.cfg diff --git a/modules/ExpGamingInfo/Tasklist/locale/nl.cfg b/old/modules/ExpGamingInfo/Tasklist/locale/nl.cfg similarity index 100% rename from modules/ExpGamingInfo/Tasklist/locale/nl.cfg rename to old/modules/ExpGamingInfo/Tasklist/locale/nl.cfg diff --git a/modules/ExpGamingInfo/Tasklist/locale/sv-SE.cfg b/old/modules/ExpGamingInfo/Tasklist/locale/sv-SE.cfg similarity index 100% rename from modules/ExpGamingInfo/Tasklist/locale/sv-SE.cfg rename to old/modules/ExpGamingInfo/Tasklist/locale/sv-SE.cfg diff --git a/modules/ExpGamingInfo/Tasklist/softmod.json b/old/modules/ExpGamingInfo/Tasklist/softmod.json similarity index 100% rename from modules/ExpGamingInfo/Tasklist/softmod.json rename to old/modules/ExpGamingInfo/Tasklist/softmod.json diff --git a/modules/ExpGamingInfo/softmod.json b/old/modules/ExpGamingInfo/softmod.json similarity index 100% rename from modules/ExpGamingInfo/softmod.json rename to old/modules/ExpGamingInfo/softmod.json diff --git a/modules/ExpGamingLib/control.lua b/old/modules/ExpGamingLib/control.lua similarity index 100% rename from modules/ExpGamingLib/control.lua rename to old/modules/ExpGamingLib/control.lua diff --git a/modules/ExpGamingLib/softmod.json b/old/modules/ExpGamingLib/softmod.json similarity index 100% rename from modules/ExpGamingLib/softmod.json rename to old/modules/ExpGamingLib/softmod.json diff --git a/modules/ExpGamingPlayer/afkKick/control.lua b/old/modules/ExpGamingPlayer/afkKick/control.lua similarity index 100% rename from modules/ExpGamingPlayer/afkKick/control.lua rename to old/modules/ExpGamingPlayer/afkKick/control.lua diff --git a/modules/ExpGamingPlayer/afkKick/softmod.json b/old/modules/ExpGamingPlayer/afkKick/softmod.json similarity index 100% rename from modules/ExpGamingPlayer/afkKick/softmod.json rename to old/modules/ExpGamingPlayer/afkKick/softmod.json diff --git a/modules/ExpGamingPlayer/afkKick/src/server.lua b/old/modules/ExpGamingPlayer/afkKick/src/server.lua similarity index 100% rename from modules/ExpGamingPlayer/afkKick/src/server.lua rename to old/modules/ExpGamingPlayer/afkKick/src/server.lua diff --git a/modules/ExpGamingPlayer/inventorySearch/control.lua b/old/modules/ExpGamingPlayer/inventorySearch/control.lua similarity index 100% rename from modules/ExpGamingPlayer/inventorySearch/control.lua rename to old/modules/ExpGamingPlayer/inventorySearch/control.lua diff --git a/modules/ExpGamingPlayer/inventorySearch/locale/de.cfg b/old/modules/ExpGamingPlayer/inventorySearch/locale/de.cfg similarity index 100% rename from modules/ExpGamingPlayer/inventorySearch/locale/de.cfg rename to old/modules/ExpGamingPlayer/inventorySearch/locale/de.cfg diff --git a/modules/ExpGamingPlayer/inventorySearch/locale/en.cfg b/old/modules/ExpGamingPlayer/inventorySearch/locale/en.cfg similarity index 100% rename from modules/ExpGamingPlayer/inventorySearch/locale/en.cfg rename to old/modules/ExpGamingPlayer/inventorySearch/locale/en.cfg diff --git a/modules/ExpGamingPlayer/inventorySearch/locale/fr.cfg b/old/modules/ExpGamingPlayer/inventorySearch/locale/fr.cfg similarity index 100% rename from modules/ExpGamingPlayer/inventorySearch/locale/fr.cfg rename to old/modules/ExpGamingPlayer/inventorySearch/locale/fr.cfg diff --git a/modules/ExpGamingPlayer/inventorySearch/locale/nl.cfg b/old/modules/ExpGamingPlayer/inventorySearch/locale/nl.cfg similarity index 100% rename from modules/ExpGamingPlayer/inventorySearch/locale/nl.cfg rename to old/modules/ExpGamingPlayer/inventorySearch/locale/nl.cfg diff --git a/modules/ExpGamingPlayer/inventorySearch/locale/sv-SE.cfg b/old/modules/ExpGamingPlayer/inventorySearch/locale/sv-SE.cfg similarity index 100% rename from modules/ExpGamingPlayer/inventorySearch/locale/sv-SE.cfg rename to old/modules/ExpGamingPlayer/inventorySearch/locale/sv-SE.cfg diff --git a/modules/ExpGamingPlayer/inventorySearch/softmod.json b/old/modules/ExpGamingPlayer/inventorySearch/softmod.json similarity index 100% rename from modules/ExpGamingPlayer/inventorySearch/softmod.json rename to old/modules/ExpGamingPlayer/inventorySearch/softmod.json diff --git a/modules/ExpGamingPlayer/playerInfo/control.lua b/old/modules/ExpGamingPlayer/playerInfo/control.lua similarity index 100% rename from modules/ExpGamingPlayer/playerInfo/control.lua rename to old/modules/ExpGamingPlayer/playerInfo/control.lua diff --git a/modules/ExpGamingPlayer/playerInfo/locale/de.cfg b/old/modules/ExpGamingPlayer/playerInfo/locale/de.cfg similarity index 100% rename from modules/ExpGamingPlayer/playerInfo/locale/de.cfg rename to old/modules/ExpGamingPlayer/playerInfo/locale/de.cfg diff --git a/modules/ExpGamingPlayer/playerInfo/locale/en.cfg b/old/modules/ExpGamingPlayer/playerInfo/locale/en.cfg similarity index 100% rename from modules/ExpGamingPlayer/playerInfo/locale/en.cfg rename to old/modules/ExpGamingPlayer/playerInfo/locale/en.cfg diff --git a/modules/ExpGamingPlayer/playerInfo/locale/fr.cfg b/old/modules/ExpGamingPlayer/playerInfo/locale/fr.cfg similarity index 100% rename from modules/ExpGamingPlayer/playerInfo/locale/fr.cfg rename to old/modules/ExpGamingPlayer/playerInfo/locale/fr.cfg diff --git a/modules/ExpGamingPlayer/playerInfo/locale/nl.cfg b/old/modules/ExpGamingPlayer/playerInfo/locale/nl.cfg similarity index 100% rename from modules/ExpGamingPlayer/playerInfo/locale/nl.cfg rename to old/modules/ExpGamingPlayer/playerInfo/locale/nl.cfg diff --git a/modules/ExpGamingPlayer/playerInfo/locale/sv-SE.cfg b/old/modules/ExpGamingPlayer/playerInfo/locale/sv-SE.cfg similarity index 100% rename from modules/ExpGamingPlayer/playerInfo/locale/sv-SE.cfg rename to old/modules/ExpGamingPlayer/playerInfo/locale/sv-SE.cfg diff --git a/modules/ExpGamingPlayer/playerInfo/softmod.json b/old/modules/ExpGamingPlayer/playerInfo/softmod.json similarity index 100% rename from modules/ExpGamingPlayer/playerInfo/softmod.json rename to old/modules/ExpGamingPlayer/playerInfo/softmod.json diff --git a/modules/ExpGamingPlayer/playerList/control.lua b/old/modules/ExpGamingPlayer/playerList/control.lua similarity index 100% rename from modules/ExpGamingPlayer/playerList/control.lua rename to old/modules/ExpGamingPlayer/playerList/control.lua diff --git a/modules/ExpGamingPlayer/playerList/locale/de.cfg b/old/modules/ExpGamingPlayer/playerList/locale/de.cfg similarity index 100% rename from modules/ExpGamingPlayer/playerList/locale/de.cfg rename to old/modules/ExpGamingPlayer/playerList/locale/de.cfg diff --git a/modules/ExpGamingPlayer/playerList/locale/en.cfg b/old/modules/ExpGamingPlayer/playerList/locale/en.cfg similarity index 100% rename from modules/ExpGamingPlayer/playerList/locale/en.cfg rename to old/modules/ExpGamingPlayer/playerList/locale/en.cfg diff --git a/modules/ExpGamingPlayer/playerList/locale/fr.cfg b/old/modules/ExpGamingPlayer/playerList/locale/fr.cfg similarity index 100% rename from modules/ExpGamingPlayer/playerList/locale/fr.cfg rename to old/modules/ExpGamingPlayer/playerList/locale/fr.cfg diff --git a/modules/ExpGamingPlayer/playerList/locale/nl.cfg b/old/modules/ExpGamingPlayer/playerList/locale/nl.cfg similarity index 100% rename from modules/ExpGamingPlayer/playerList/locale/nl.cfg rename to old/modules/ExpGamingPlayer/playerList/locale/nl.cfg diff --git a/modules/ExpGamingPlayer/playerList/locale/sv-SE.cfg b/old/modules/ExpGamingPlayer/playerList/locale/sv-SE.cfg similarity index 100% rename from modules/ExpGamingPlayer/playerList/locale/sv-SE.cfg rename to old/modules/ExpGamingPlayer/playerList/locale/sv-SE.cfg diff --git a/modules/ExpGamingPlayer/playerList/softmod.json b/old/modules/ExpGamingPlayer/playerList/softmod.json similarity index 100% rename from modules/ExpGamingPlayer/playerList/softmod.json rename to old/modules/ExpGamingPlayer/playerList/softmod.json diff --git a/modules/ExpGamingPlayer/playerList/src/ranking.lua b/old/modules/ExpGamingPlayer/playerList/src/ranking.lua similarity index 100% rename from modules/ExpGamingPlayer/playerList/src/ranking.lua rename to old/modules/ExpGamingPlayer/playerList/src/ranking.lua diff --git a/modules/ExpGamingPlayer/polls/control.lua b/old/modules/ExpGamingPlayer/polls/control.lua similarity index 100% rename from modules/ExpGamingPlayer/polls/control.lua rename to old/modules/ExpGamingPlayer/polls/control.lua diff --git a/modules/ExpGamingPlayer/polls/locale/de.cfg b/old/modules/ExpGamingPlayer/polls/locale/de.cfg similarity index 100% rename from modules/ExpGamingPlayer/polls/locale/de.cfg rename to old/modules/ExpGamingPlayer/polls/locale/de.cfg diff --git a/modules/ExpGamingPlayer/polls/locale/en.cfg b/old/modules/ExpGamingPlayer/polls/locale/en.cfg similarity index 100% rename from modules/ExpGamingPlayer/polls/locale/en.cfg rename to old/modules/ExpGamingPlayer/polls/locale/en.cfg diff --git a/modules/ExpGamingPlayer/polls/locale/fr.cfg b/old/modules/ExpGamingPlayer/polls/locale/fr.cfg similarity index 100% rename from modules/ExpGamingPlayer/polls/locale/fr.cfg rename to old/modules/ExpGamingPlayer/polls/locale/fr.cfg diff --git a/modules/ExpGamingPlayer/polls/locale/nl.cfg b/old/modules/ExpGamingPlayer/polls/locale/nl.cfg similarity index 100% rename from modules/ExpGamingPlayer/polls/locale/nl.cfg rename to old/modules/ExpGamingPlayer/polls/locale/nl.cfg diff --git a/modules/ExpGamingPlayer/polls/locale/sv-SE.cfg b/old/modules/ExpGamingPlayer/polls/locale/sv-SE.cfg similarity index 100% rename from modules/ExpGamingPlayer/polls/locale/sv-SE.cfg rename to old/modules/ExpGamingPlayer/polls/locale/sv-SE.cfg diff --git a/modules/ExpGamingPlayer/polls/softmod.json b/old/modules/ExpGamingPlayer/polls/softmod.json similarity index 100% rename from modules/ExpGamingPlayer/polls/softmod.json rename to old/modules/ExpGamingPlayer/polls/softmod.json diff --git a/modules/ExpGamingPlayer/softmod.json b/old/modules/ExpGamingPlayer/softmod.json similarity index 100% rename from modules/ExpGamingPlayer/softmod.json rename to old/modules/ExpGamingPlayer/softmod.json diff --git a/modules/FactorioStdLib/Color/control.lua b/old/modules/FactorioStdLib/Color/control.lua similarity index 100% rename from modules/FactorioStdLib/Color/control.lua rename to old/modules/FactorioStdLib/Color/control.lua diff --git a/modules/FactorioStdLib/Color/softmod.json b/old/modules/FactorioStdLib/Color/softmod.json similarity index 100% rename from modules/FactorioStdLib/Color/softmod.json rename to old/modules/FactorioStdLib/Color/softmod.json diff --git a/modules/FactorioStdLib/Game/control.lua b/old/modules/FactorioStdLib/Game/control.lua similarity index 100% rename from modules/FactorioStdLib/Game/control.lua rename to old/modules/FactorioStdLib/Game/control.lua diff --git a/modules/FactorioStdLib/Game/softmod.json b/old/modules/FactorioStdLib/Game/softmod.json similarity index 100% rename from modules/FactorioStdLib/Game/softmod.json rename to old/modules/FactorioStdLib/Game/softmod.json diff --git a/modules/FactorioStdLib/String/control.lua b/old/modules/FactorioStdLib/String/control.lua similarity index 100% rename from modules/FactorioStdLib/String/control.lua rename to old/modules/FactorioStdLib/String/control.lua diff --git a/modules/FactorioStdLib/String/softmod.json b/old/modules/FactorioStdLib/String/softmod.json similarity index 100% rename from modules/FactorioStdLib/String/softmod.json rename to old/modules/FactorioStdLib/String/softmod.json diff --git a/modules/FactorioStdLib/Table/control.lua b/old/modules/FactorioStdLib/Table/control.lua similarity index 100% rename from modules/FactorioStdLib/Table/control.lua rename to old/modules/FactorioStdLib/Table/control.lua diff --git a/modules/FactorioStdLib/Table/softmod.json b/old/modules/FactorioStdLib/Table/softmod.json similarity index 100% rename from modules/FactorioStdLib/Table/softmod.json rename to old/modules/FactorioStdLib/Table/softmod.json diff --git a/modules/FactorioStdLib/softmod.json b/old/modules/FactorioStdLib/softmod.json similarity index 100% rename from modules/FactorioStdLib/softmod.json rename to old/modules/FactorioStdLib/softmod.json diff --git a/modules/GameSettingsGui/control.lua b/old/modules/GameSettingsGui/control.lua similarity index 100% rename from modules/GameSettingsGui/control.lua rename to old/modules/GameSettingsGui/control.lua diff --git a/modules/GameSettingsGui/locale/de.cfg b/old/modules/GameSettingsGui/locale/de.cfg similarity index 100% rename from modules/GameSettingsGui/locale/de.cfg rename to old/modules/GameSettingsGui/locale/de.cfg diff --git a/modules/GameSettingsGui/locale/en.cfg b/old/modules/GameSettingsGui/locale/en.cfg similarity index 100% rename from modules/GameSettingsGui/locale/en.cfg rename to old/modules/GameSettingsGui/locale/en.cfg diff --git a/modules/GameSettingsGui/locale/fr.cfg b/old/modules/GameSettingsGui/locale/fr.cfg similarity index 100% rename from modules/GameSettingsGui/locale/fr.cfg rename to old/modules/GameSettingsGui/locale/fr.cfg diff --git a/modules/GameSettingsGui/locale/nl.cfg b/old/modules/GameSettingsGui/locale/nl.cfg similarity index 100% rename from modules/GameSettingsGui/locale/nl.cfg rename to old/modules/GameSettingsGui/locale/nl.cfg diff --git a/modules/GameSettingsGui/locale/sv-SE.cfg b/old/modules/GameSettingsGui/locale/sv-SE.cfg similarity index 100% rename from modules/GameSettingsGui/locale/sv-SE.cfg rename to old/modules/GameSettingsGui/locale/sv-SE.cfg diff --git a/modules/GameSettingsGui/softmod.json b/old/modules/GameSettingsGui/softmod.json similarity index 100% rename from modules/GameSettingsGui/softmod.json rename to old/modules/GameSettingsGui/softmod.json diff --git a/modules/GuiAnnouncements/control.lua b/old/modules/GuiAnnouncements/control.lua similarity index 100% rename from modules/GuiAnnouncements/control.lua rename to old/modules/GuiAnnouncements/control.lua diff --git a/modules/GuiAnnouncements/locale/de.cfg b/old/modules/GuiAnnouncements/locale/de.cfg similarity index 100% rename from modules/GuiAnnouncements/locale/de.cfg rename to old/modules/GuiAnnouncements/locale/de.cfg diff --git a/modules/GuiAnnouncements/locale/en.cfg b/old/modules/GuiAnnouncements/locale/en.cfg similarity index 100% rename from modules/GuiAnnouncements/locale/en.cfg rename to old/modules/GuiAnnouncements/locale/en.cfg diff --git a/modules/GuiAnnouncements/locale/fr.cfg b/old/modules/GuiAnnouncements/locale/fr.cfg similarity index 100% rename from modules/GuiAnnouncements/locale/fr.cfg rename to old/modules/GuiAnnouncements/locale/fr.cfg diff --git a/modules/GuiAnnouncements/locale/nl.cfg b/old/modules/GuiAnnouncements/locale/nl.cfg similarity index 100% rename from modules/GuiAnnouncements/locale/nl.cfg rename to old/modules/GuiAnnouncements/locale/nl.cfg diff --git a/modules/GuiAnnouncements/locale/sv-SE.cfg b/old/modules/GuiAnnouncements/locale/sv-SE.cfg similarity index 100% rename from modules/GuiAnnouncements/locale/sv-SE.cfg rename to old/modules/GuiAnnouncements/locale/sv-SE.cfg diff --git a/modules/GuiAnnouncements/softmod.json b/old/modules/GuiAnnouncements/softmod.json similarity index 100% rename from modules/GuiAnnouncements/softmod.json rename to old/modules/GuiAnnouncements/softmod.json diff --git a/modules/PlayerAutoColor/control.lua b/old/modules/PlayerAutoColor/control.lua similarity index 100% rename from modules/PlayerAutoColor/control.lua rename to old/modules/PlayerAutoColor/control.lua diff --git a/modules/PlayerAutoColor/softmod.json b/old/modules/PlayerAutoColor/softmod.json similarity index 100% rename from modules/PlayerAutoColor/softmod.json rename to old/modules/PlayerAutoColor/softmod.json diff --git a/modules/SpawnArea/control.lua b/old/modules/SpawnArea/control.lua similarity index 100% rename from modules/SpawnArea/control.lua rename to old/modules/SpawnArea/control.lua diff --git a/modules/SpawnArea/softmod.json b/old/modules/SpawnArea/softmod.json similarity index 100% rename from modules/SpawnArea/softmod.json rename to old/modules/SpawnArea/softmod.json diff --git a/modules/SpawnArea/src/spawn_entities.lua b/old/modules/SpawnArea/src/spawn_entities.lua similarity index 100% rename from modules/SpawnArea/src/spawn_entities.lua rename to old/modules/SpawnArea/src/spawn_entities.lua diff --git a/modules/SpawnArea/src/spawn_tiles.lua b/old/modules/SpawnArea/src/spawn_tiles.lua similarity index 100% rename from modules/SpawnArea/src/spawn_tiles.lua rename to old/modules/SpawnArea/src/spawn_tiles.lua diff --git a/modules/WarpPoints/control.lua b/old/modules/WarpPoints/control.lua similarity index 100% rename from modules/WarpPoints/control.lua rename to old/modules/WarpPoints/control.lua diff --git a/modules/WarpPoints/locale/de.cfg b/old/modules/WarpPoints/locale/de.cfg similarity index 100% rename from modules/WarpPoints/locale/de.cfg rename to old/modules/WarpPoints/locale/de.cfg diff --git a/modules/WarpPoints/locale/en.cfg b/old/modules/WarpPoints/locale/en.cfg similarity index 100% rename from modules/WarpPoints/locale/en.cfg rename to old/modules/WarpPoints/locale/en.cfg diff --git a/modules/WarpPoints/locale/fr.cfg b/old/modules/WarpPoints/locale/fr.cfg similarity index 100% rename from modules/WarpPoints/locale/fr.cfg rename to old/modules/WarpPoints/locale/fr.cfg diff --git a/modules/WarpPoints/locale/nl.cfg b/old/modules/WarpPoints/locale/nl.cfg similarity index 100% rename from modules/WarpPoints/locale/nl.cfg rename to old/modules/WarpPoints/locale/nl.cfg diff --git a/modules/WarpPoints/locale/sv-SE.cfg b/old/modules/WarpPoints/locale/sv-SE.cfg similarity index 100% rename from modules/WarpPoints/locale/sv-SE.cfg rename to old/modules/WarpPoints/locale/sv-SE.cfg diff --git a/modules/WarpPoints/softmod.json b/old/modules/WarpPoints/softmod.json similarity index 100% rename from modules/WarpPoints/softmod.json rename to old/modules/WarpPoints/softmod.json diff --git a/modules/WarpPoints/src/commands.lua b/old/modules/WarpPoints/src/commands.lua similarity index 100% rename from modules/WarpPoints/src/commands.lua rename to old/modules/WarpPoints/src/commands.lua diff --git a/modules/WarpPoints/src/warp_entities.lua b/old/modules/WarpPoints/src/warp_entities.lua similarity index 100% rename from modules/WarpPoints/src/warp_entities.lua rename to old/modules/WarpPoints/src/warp_entities.lua diff --git a/modules/WarpPoints/src/warp_tiles.lua b/old/modules/WarpPoints/src/warp_tiles.lua similarity index 100% rename from modules/WarpPoints/src/warp_tiles.lua rename to old/modules/WarpPoints/src/warp_tiles.lua diff --git a/modules/WornPaths/control.lua b/old/modules/WornPaths/control.lua similarity index 100% rename from modules/WornPaths/control.lua rename to old/modules/WornPaths/control.lua diff --git a/modules/WornPaths/softmod.json b/old/modules/WornPaths/softmod.json similarity index 100% rename from modules/WornPaths/softmod.json rename to old/modules/WornPaths/softmod.json diff --git a/modules/WornPaths/src/entites.lua b/old/modules/WornPaths/src/entites.lua similarity index 100% rename from modules/WornPaths/src/entites.lua rename to old/modules/WornPaths/src/entites.lua diff --git a/modules/WornPaths/src/paths.lua b/old/modules/WornPaths/src/paths.lua similarity index 100% rename from modules/WornPaths/src/paths.lua rename to old/modules/WornPaths/src/paths.lua diff --git a/modules/WornPaths/src/placed.lua b/old/modules/WornPaths/src/placed.lua similarity index 100% rename from modules/WornPaths/src/placed.lua rename to old/modules/WornPaths/src/placed.lua diff --git a/modules/index.lua b/old/modules/index.lua similarity index 100% rename from modules/index.lua rename to old/modules/index.lua diff --git a/modules/softmod.json b/old/modules/softmod.json similarity index 100% rename from modules/softmod.json rename to old/modules/softmod.json diff --git a/resources/color_presets.lua b/resources/color_presets.lua new file mode 100644 index 00000000..0dd965e4 --- /dev/null +++ b/resources/color_presets.lua @@ -0,0 +1,163 @@ +-- source: https://www.rapidtables.com/web/color/RGB_Color.html +return { + maroon = {r = 128, g = 0, b = 0}, + dark_red = {r = 139, g = 0, b = 0}, + brown = {r = 165, g = 42, b = 42}, + firebrick = {r = 178, g = 34, b = 34}, + crimson = {r = 220, g = 20, b = 60}, + red = {r = 255, g = 0, b = 0}, + tomato = {r = 255, g = 99, b = 71}, + coral = {r = 255, g = 127, b = 80}, + indian_red = {r = 205, g = 92, b = 92}, + light_coral = {r = 240, g = 128, b = 128}, + dark_salmon = {r = 233, g = 150, b = 122}, + salmon = {r = 250, g = 128, b = 114}, + light_salmon = {r = 255, g = 160, b = 122}, + orange_red = {r = 255, g = 69, b = 0}, + dark_orange = {r = 255, g = 140, b = 0}, + orange = {r = 255, g = 165, b = 0}, + gold = {r = 255, g = 215, b = 0}, + dark_golden_rod = {r = 184, g = 134, b = 11}, + golden_rod = {r = 218, g = 165, b = 32}, + pale_golden_rod = {r = 238, g = 232, b = 170}, + dark_khaki = {r = 189, g = 183, b = 107}, + khaki = {r = 240, g = 230, b = 140}, + olive = {r = 128, g = 128, b = 0}, + yellow = {r = 255, g = 255, b = 0}, + yellow_green = {r = 154, g = 205, b = 50}, + dark_olive_green = {r = 85, g = 107, b = 47}, + olive_drab = {r = 107, g = 142, b = 35}, + lawn_green = {r = 124, g = 252, b = 0}, + chart_reuse = {r = 127, g = 255, b = 0}, + green_yellow = {r = 173, g = 255, b = 47}, + dark_green = {r = 0, g = 100, b = 0}, + green = {r = 0, g = 128, b = 0}, + forest_green = {r = 34, g = 139, b = 34}, + lime = {r = 0, g = 255, b = 0}, + lime_green = {r = 50, g = 205, b = 50}, + light_green = {r = 144, g = 238, b = 144}, + pale_green = {r = 152, g = 251, b = 152}, + dark_sea_green = {r = 143, g = 188, b = 143}, + medium_spring_green = {r = 0, g = 250, b = 154}, + spring_green = {r = 0, g = 255, b = 127}, + sea_green = {r = 46, g = 139, b = 87}, + medium_aqua_marine = {r = 102, g = 205, b = 170}, + medium_sea_green = {r = 60, g = 179, b = 113}, + light_sea_green = {r = 32, g = 178, b = 170}, + dark_slate_gray = {r = 47, g = 79, b = 79}, + teal = {r = 0, g = 128, b = 128}, + dark_cyan = {r = 0, g = 139, b = 139}, + aqua = {r = 0, g = 255, b = 255}, + cyan = {r = 0, g = 255, b = 255}, + light_cyan = {r = 224, g = 255, b = 255}, + dark_turquoise = {r = 0, g = 206, b = 209}, + turquoise = {r = 64, g = 224, b = 208}, + medium_turquoise = {r = 72, g = 209, b = 204}, + pale_turquoise = {r = 175, g = 238, b = 238}, + aqua_marine = {r = 127, g = 255, b = 212}, + powder_blue = {r = 176, g = 224, b = 230}, + cadet_blue = {r = 95, g = 158, b = 160}, + steel_blue = {r = 70, g = 130, b = 180}, + corn_flower_blue = {r = 100, g = 149, b = 237}, + deep_sky_blue = {r = 0, g = 191, b = 255}, + dodger_blue = {r = 30, g = 144, b = 255}, + light_blue = {r = 173, g = 216, b = 230}, + sky_blue = {r = 135, g = 206, b = 235}, + light_sky_blue = {r = 135, g = 206, b = 250}, + midnight_blue = {r = 25, g = 25, b = 112}, + navy = {r = 0, g = 0, b = 128}, + dark_blue = {r = 0, g = 0, b = 139}, + medium_blue = {r = 0, g = 0, b = 205}, + blue = {r = 0, g = 0, b = 255}, + royal_blue = {r = 65, g = 105, b = 225}, + blue_violet = {r = 138, g = 43, b = 226}, + indigo = {r = 75, g = 0, b = 130}, + dark_slate_blue = {r = 72, g = 61, b = 139}, + slate_blue = {r = 106, g = 90, b = 205}, + medium_slate_blue = {r = 123, g = 104, b = 238}, + medium_purple = {r = 147, g = 112, b = 219}, + dark_magenta = {r = 139, g = 0, b = 139}, + dark_violet = {r = 148, g = 0, b = 211}, + dark_orchid = {r = 153, g = 50, b = 204}, + medium_orchid = {r = 186, g = 85, b = 211}, + purple = {r = 128, g = 0, b = 128}, + thistle = {r = 216, g = 191, b = 216}, + plum = {r = 221, g = 160, b = 221}, + violet = {r = 238, g = 130, b = 238}, + magenta = {r = 255, g = 0, b = 255}, + fuchsia = {r = 255, g = 0, b = 255}, + orchid = {r = 218, g = 112, b = 214}, + medium_violet_red = {r = 199, g = 21, b = 133}, + pale_violet_red = {r = 219, g = 112, b = 147}, + deep_pink = {r = 255, g = 20, b = 147}, + hot_pink = {r = 255, g = 105, b = 180}, + light_pink = {r = 255, g = 182, b = 193}, + pink = {r = 255, g = 192, b = 203}, + antique_white = {r = 250, g = 235, b = 215}, + beige = {r = 245, g = 245, b = 220}, + bisque = {r = 255, g = 228, b = 196}, + blanched_almond = {r = 255, g = 235, b = 205}, + wheat = {r = 245, g = 222, b = 179}, + corn_silk = {r = 255, g = 248, b = 220}, + lemon_chiffon = {r = 255, g = 250, b = 205}, + light_golden_rod_yellow = {r = 250, g = 250, b = 210}, + light_yellow = {r = 255, g = 255, b = 224}, + saddle_brown = {r = 139, g = 69, b = 19}, + sienna = {r = 160, g = 82, b = 45}, + chocolate = {r = 210, g = 105, b = 30}, + peru = {r = 205, g = 133, b = 63}, + sandy_brown = {r = 244, g = 164, b = 96}, + burly_wood = {r = 222, g = 184, b = 135}, + tan = {r = 210, g = 180, b = 140}, + rosy_brown = {r = 188, g = 143, b = 143}, + moccasin = {r = 255, g = 228, b = 181}, + navajo_white = {r = 255, g = 222, b = 173}, + peach_puff = {r = 255, g = 218, b = 185}, + misty_rose = {r = 255, g = 228, b = 225}, + lavender_blush = {r = 255, g = 240, b = 245}, + linen = {r = 250, g = 240, b = 230}, + old_lace = {r = 253, g = 245, b = 230}, + papaya_whip = {r = 255, g = 239, b = 213}, + sea_shell = {r = 255, g = 245, b = 238}, + mint_cream = {r = 245, g = 255, b = 250}, + slate_gray = {r = 112, g = 128, b = 144}, + light_slate_gray = {r = 119, g = 136, b = 153}, + light_steel_blue = {r = 176, g = 196, b = 222}, + lavender = {r = 230, g = 230, b = 250}, + floral_white = {r = 255, g = 250, b = 240}, + alice_blue = {r = 240, g = 248, b = 255}, + ghost_white = {r = 248, g = 248, b = 255}, + honeydew = {r = 240, g = 255, b = 240}, + ivory = {r = 255, g = 255, b = 240}, + azure = {r = 240, g = 255, b = 255}, + snow = {r = 255, g = 250, b = 250}, + black = {r = 0, g = 0, b = 0}, + silver = {r = 192, g = 192, b = 192}, + dim_grey = {r = 105, g = 105, b = 105}, + dim_gray = {r = 105, g = 105, b = 105}, + grey = {r = 128, g = 128, b = 128}, + gray = {r = 128, g = 128, b = 128}, + dark_grey = {r = 169, g = 169, b = 169}, + dark_gray = {r = 169, g = 169, b = 169}, + light_grey = {r = 211, g = 211, b = 211}, + light_gray = {r = 211, g = 211, b = 211}, + gainsboro = {r = 220, g = 220, b = 220}, + white_smoke = {r = 245, g = 245, b = 245}, + white = {r = 255, g = 255, b = 255}, + jailed = {r = 255, g = 255, b = 255}, + probation = {r = 255, g = 255, b = 255}, + guest = {r = 255, g = 255, b = 255}, + auto_trusted = {r = 192, g = 192, b = 192}, + regular = {r = 0.155, g = 0.540, b = 0.898}, + admin = {r = 0.093, g = 0.768, b = 0.172}, + donator = {r = 172.6, g = 70.2, b = 215.8}, + [-10] = {r = 255, g = 255, b = 255}, -- probation + [0] = {r = 255, g = 255, b = 255}, -- guest + [10] = {r = 192, g = 192, b = 192}, -- auto_trusted + [20] = {r = 0.155, g = 0.540, b = 0.898}, -- regular + [30] = {r = 0.093, g = 0.768, b = 0.172}, -- admin + success = {r = 0, g = 255, b = 0}, + warning = {r = 255, g = 255, b = 0}, + fail = {r = 255, g = 0, b = 0}, + info = {r = 255, g = 255, b = 255} +} diff --git a/utils/data_stages.lua b/resources/data_stages.lua similarity index 91% rename from utils/data_stages.lua rename to resources/data_stages.lua index 406cd4df..516fb321 100644 --- a/utils/data_stages.lua +++ b/resources/data_stages.lua @@ -10,4 +10,3 @@ _STAGE = { --config_change = 7, runtime = 8 } -_LIFECYCLE = _STAGE.control \ No newline at end of file diff --git a/resources/day_night_cycles.lua b/resources/day_night_cycles.lua new file mode 100644 index 00000000..dd4062bd --- /dev/null +++ b/resources/day_night_cycles.lua @@ -0,0 +1,59 @@ +return { + -- ~7 minute cycle, 3m28s of full light, 1m23s light to dark, 42s full dark, 1m23s dark to light + vanilla = { + ticks_per_day = 25000, + dusk = 0.25, + evening = 0.45, + morning = 0.55, + dawn = 0.75 + }, + -- 10 minute cycle, 4m of full light, 4m light to dark, 6s full dark, 2m dark to light + bright = { + ticks_per_day = 36000, + dusk = 0.2, + evening = 0.59, + morning = 0.6, + dawn = 0.8 + }, + -- ~14 minute cycle, 6m56s of full light, 2m46s light to dark, 1m24s full dark, 2m46s dark to light + double_length = { + ticks_per_day = 50000, + dusk = 0.25, + evening = 0.45, + morning = 0.55, + dawn = 0.75 + }, + -- 10 minute cycle, 6s of full light, 2m light to dark, 4m full dark, 4m dark to light + gloomy = { + ticks_per_day = 36000, + dusk = 0, + evening = 0.2, + morning = 0.6, + dawn = 0.99 + }, + -- ~3.5 minute cycle, 1m44s of full light, 42s light to dark, 21s full dark, 42s dark to light + half_length = { + ticks_per_day = 12500, + dusk = 0.25, + evening = 0.45, + morning = 0.55, + dawn = 0.75 + }, + -- 20 minute cycle, 9m of full light, 1m light to dark, 9m full dark, 1m dark to light + long_days_long_nights_fast_transitions = { + ticks_per_day = 72000, + dusk = 0.225, + evening = 0.275, + morning = 0.725, + dawn = 0.775 + }, + -- 6 hour cycle based on Feb 3 London, England for the day/night/twilight times: + -- Day: 2h15m Night: 2h45m Day to night and night to day: 30m each Map starts mid-day + feb3 = { + ticks_per_day = 1296000, + dusk = 4.5 / 24, + evening = 15.5 / 24, + morning = 17.5 / 24, + dawn = 19.5 / 24 + } +} diff --git a/resources/difficulty_settings.lua b/resources/difficulty_settings.lua new file mode 100644 index 00000000..6a1db0bf --- /dev/null +++ b/resources/difficulty_settings.lua @@ -0,0 +1,72 @@ +-- technology_difficulty has no effect in vanilla +return { + -- the default table is included as a reference but also to give the option of overwriting all user settings + default = { + recipe_difficulty = defines.difficulty_settings.recipe_difficulty.normal, + technology_difficulty = defines.difficulty_settings.technology_difficulty.normal, + technology_price_multiplier = 1 + }, + -- turns on expensive recipes + expensive_recipe = { + recipe_difficulty = defines.difficulty_settings.recipe_difficulty.expensive + }, + -- the following are tech cost reducers + ['tech_x0.25'] = { + technology_price_multiplier = 0.25 + }, + ['tech_x0.5'] = { + technology_price_multiplier = 0.5 + }, + ['tech_x0.75'] = { + technology_price_multiplier = 0.75 + }, + -- the following are all tech cost multipliers + tech_x2 = { + technology_price_multiplier = 2 + }, + tech_x3 = { + technology_price_multiplier = 3 + }, + tech_x4 = { + technology_price_multiplier = 4 + }, + tech_x5 = { + technology_price_multiplier = 5 + }, + tech_x6 = { + technology_price_multiplier = 6 + }, + tech_x8 = { + technology_price_multiplier = 8 + }, + tech_x10 = { + technology_price_multiplier = 10 + }, + tech_x12 = { + technology_price_multiplier = 12 + }, + tech_x14 = { + technology_price_multiplier = 14 + }, + tech_x16 = { + technology_price_multiplier = 16 + }, + tech_x20 = { + technology_price_multiplier = 20 + }, + tech_x50 = { + technology_price_multiplier = 50 + }, + tech_x100 = { + technology_price_multiplier = 100 + }, + tech_x250 = { + technology_price_multiplier = 250 + }, + tech_x500 = { + technology_price_multiplier = 500 + }, + tech_x1000 = { + technology_price_multiplier = 1000 + } +} diff --git a/resources/donator_perks.lua b/resources/donator_perks.lua new file mode 100644 index 00000000..1e68bc6d --- /dev/null +++ b/resources/donator_perks.lua @@ -0,0 +1,4 @@ +return { + rank = 0x1, + train = 0x2 +} diff --git a/resources/fish_messages.lua b/resources/fish_messages.lua new file mode 100644 index 00000000..7f584da4 --- /dev/null +++ b/resources/fish_messages.lua @@ -0,0 +1,56 @@ +return { + 'Why don’t fish like basketball? Cause they’re afraid of the net', + 'What do you get when you cross a banker with a fish? A Loan shark!', + 'How do you make an Octupus laugh? With ten-tickles', + 'What do you call a fish that needs help with his or her vocals? Autotuna', + 'What is the difference between a piano and a fish? You can tune a piano but you cannot tuna fish.', + 'What did the blind man say when he passed the fish market? Good morning ladies.', + 'Did you hear about the goldfish who went bankrupt? Now he’s a bronze fish.', + 'What happens when you put nutella on salmon? You get salmonella', + 'What do you call a fish with no eyes?…Fsh', + 'What kind of money do fishermen make?…Net profits', + 'What do you get if you cross a salmon, a bird’s leg and a hand?…Birdsthigh fish fingers', + 'Why is a fish easy to weigh?…Because it has its own scales', + 'Why are gold fish orange?…The water makes them rusty', + 'What was the Tsar of Russia’s favorite fish?…Tsardines', + 'Why did the dog jump into the sea?…He wanted to chase the catfish', + 'Which fish dresses the best?…The Swordfish – It always looks sharp', + 'What do you get if you cross an abbot with a trout?…Monkfish', + 'What do you call a big fish who makes you an offer you can’t refuse?…The Codfather', + 'Why is a swordfish’s nose 11 inches long?…If it were 12 inches long it would be a foot', + 'What do you get if you cross a trout with an apartment?…A flat fish', + 'Why are fish no good at tennis?…They don’t like to get too close to the net', + 'How do the fish get to school?…By octobus', + 'What fish make the best sandwich?…A peanut butter and jellyfish', + 'Man: Can I have a fly rod and reel for my son?…Fishing Shop Owner: Sorry sir we don’t do trades', + 'Where do fish keep their money?…In the river bank', + 'Why do fish like arcade games?…Because they are finball wizards', + 'What is a mermaid?…A deep-she fish', + 'What do you get if you cross a whale with rotten fish?…Moby Sick', + 'Why didn’t the lobster share his toys?…He was too shellfish', + 'What do fish use to make telephone calls?…a Shell phone', + 'Why don’t fish make very good tennis balls?…They keep getting caught in the net', + 'Electric eels and electric rays have enough electricity to kill a horse.', + 'Most fish have taste buds all over their body.', + 'Most brands of lipstick contain fish scales.', + 'A fish does not add new scales as it grows, but the scales it has increase in size. In this way, growth rings are formed and the rings reveal the age of a fish.', + 'An inflated porcupine fish can reach a diameter of up to 35 inches. It puffs up by swallowing water and then storing it in its stomach.', + 'Most fish cannot swim backwards. Those that can are mainly members of one of the eel families.', + 'The fastest fish is the sailfish. It can swim as fast as a car travels on the highway.', + 'Some desert pupfish can live in hot springs that reach temperatures greater than 113° F.', + 'Anableps, four-eyed fish, can see above and below water at the same time.', + 'Catfish have over 27,000 taste buds. Humans have around 7,000.', + 'One hagfish can make enough slime in one minute to fill a bucket.', + 'A female sunfish can lay 300 million eggs each year.', + 'Fish feel pain and suffer stress just like mammals and birds.', + 'The Dwarf Seahorse is so slow you can’t see it move', + 'Some fish are as small as a grain of rice', + "There's a species of fish called 'Slippery Dick'", + 'Herrings communicate through farts.', + 'One Puffer Fish contains enough poison to kill 30 medium-biters.', + 'When Anglerfish mate, they melt into each other and share their bodies forever.', + "A koi fish named 'Hanako' lived for 225 years.", + "What did the fish say when he posted bail? I'm off the hook!", + 'There was a sale at the fish market today. I went to see what was the catch.', + 'There are over 25,000 identified species of fish on the earth.' +} diff --git a/resources/hodor_messages.lua b/resources/hodor_messages.lua new file mode 100644 index 00000000..45d021ff --- /dev/null +++ b/resources/hodor_messages.lua @@ -0,0 +1,12 @@ +-- List of hodor's responses and their weights +return { + {'Hodor.', 16}, + {'Hodor?', 16}, + {'Hodor!', 16}, + {'Hodor! Hodor! Hodor! Hodor!', 4}, + {'Hodor :(', 4}, + {'Hodor :)', 4}, + {'HOOOODOOOR!', 4}, + {'( ͡° ͜ʖ ͡°)', 1}, + {'☉ ‿ ⚆', 1} +} diff --git a/resources/join_messages.lua b/resources/join_messages.lua new file mode 100644 index 00000000..4caceef3 --- /dev/null +++ b/resources/join_messages.lua @@ -0,0 +1,9 @@ +-- List of join messages and their weights. Default is 10. +return { + {'And remember.. Keep Calm And Spaghetti!', 50}, + {'Press F to pay respects to the "Press F to pay respect" message', 3}, + {'Not sure what to do? Ask Newcott.', 1}, + {'Stick and move, stick and move', 10}, + {'Fly like a butterfly, sting like a bee', 10}, + {"These are not the bots you're looking for", 10} +} diff --git a/resources/map_gen_settings.lua b/resources/map_gen_settings.lua new file mode 100644 index 00000000..400ceefd --- /dev/null +++ b/resources/map_gen_settings.lua @@ -0,0 +1,330 @@ +--[[ The easiest way to create a preset to add to this file is to use factorio itself. Create a new world + (vanilla, not scenario) and configure the settings you want. When you launch the game, you can run the following: + /c + local str = serpent.block(game.surfaces.nauvis.map_gen_settings) + game.write_file('map_gen_settings.lua', str) + + This will output a file with a table that you can add to this resources file or into your specific map. + In either case, make sure to set seed to nil unless you want your map to be *exactly* the same each time. + The expectation is that all changes that deviate from default generation are noted. + Water size and frequency is not denoted as such. Instead water size = water and water frequency = terrain_segmentation +]] +return { + -- the default table is included as a reference but also to give the option of overwriting all user settings + default = { + autoplace_controls = { + coal = { + frequency = 'normal', + richness = 'normal', + size = 'normal' + }, + ['copper-ore'] = { + frequency = 'normal', + richness = 'normal', + size = 'normal' + }, + ['crude-oil'] = { + frequency = 'normal', + richness = 'normal', + size = 'normal' + }, + desert = { + frequency = 'normal', + richness = 'normal', + size = 'normal' + }, + dirt = { + frequency = 'normal', + richness = 'normal', + size = 'normal' + }, + ['enemy-base'] = { + frequency = 'normal', + richness = 'normal', + size = 'normal' + }, + grass = { + frequency = 'normal', + richness = 'normal', + size = 'normal' + }, + ['iron-ore'] = { + frequency = 'normal', + richness = 'normal', + size = 'normal' + }, + sand = { + frequency = 'normal', + richness = 'normal', + size = 'normal' + }, + stone = { + frequency = 'normal', + richness = 'normal', + size = 'normal' + }, + trees = { + frequency = 'normal', + richness = 'normal', + size = 'normal' + }, + ['uranium-ore'] = { + frequency = 'normal', + richness = 'normal', + size = 'normal' + } + }, + cliff_settings = { + cliff_elevation_0 = 10, + cliff_elevation_interval = 10, + name = 'cliff' + }, + height = 2000000, + peaceful_mode = false, + starting_area = 'normal', + terrain_segmentation = 'normal', + water = 'normal', + width = 2000000 + }, + -- no enemies + enemy_none = { + autoplace_controls = { + ['enemy-base'] = { + frequency = 'normal', + richness = 'normal', + size = 'none' + } + } + }, + -- high frequency and big size enemies + enemy_high = { + autoplace_controls = { + ['enemy-base'] = { + frequency = 'high', + richness = 'normal', + size = 'high' + } + } + }, + -- very high frequency and very big size enemies + enemy_very_high = { + autoplace_controls = { + ['enemy-base'] = { + frequency = 'very-high', + richness = 'normal', + size = 'very-high' + } + } + }, + -- no ores + ore_none = { + autoplace_controls = { + coal = { + frequency = 'normal', + richness = 'normal', + size = 'none' + }, + ['copper-ore'] = { + frequency = 'normal', + richness = 'normal', + size = 'none' + }, + ['iron-ore'] = { + frequency = 'normal', + richness = 'normal', + size = 'none' + }, + stone = { + frequency = 'normal', + richness = 'normal', + size = 'none' + }, + ['uranium-ore'] = { + frequency = 'normal', + richness = 'normal', + size = 'none' + } + } + }, + -- no oil + oil_none = { + autoplace_controls = { + ['crude-oil'] = { + frequency = 'normal', + richness = 'normal', + size = 'none' + } + } + }, + -- no ores, no oil + ore_oil_none = { + autoplace_controls = { + coal = { + frequency = 'normal', + richness = 'normal', + size = 'none' + }, + ['copper-ore'] = { + frequency = 'normal', + richness = 'normal', + size = 'none' + }, + ['crude-oil'] = { + frequency = 'normal', + richness = 'normal', + size = 'none' + }, + ['iron-ore'] = { + frequency = 'normal', + richness = 'normal', + size = 'none' + }, + stone = { + frequency = 'normal', + richness = 'normal', + size = 'none' + }, + ['uranium-ore'] = { + frequency = 'normal', + richness = 'normal', + size = 'none' + } + } + }, + -- no water + water_none = { + terrain_segmentation = 'very-low', + water = 'none' + }, + -- very low water + water_very_low = { + terrain_segmentation = 'very-low', + water = 'very-low' + }, + -- no cliffs + cliff_none = { + cliff_settings = { + cliff_elevation_0 = 1024, + cliff_elevation_interval = 10, + name = 'cliff' + } + }, + -- normal cliffs + cliff_normal = { + name = 'cliff', + cliff_elevation_0 = 10, + cliff_elevation_interval = 10 + }, + -- cliffs to very high frequency, very big size + cliff_very_high = { + cliff_settings = { + cliff_elevation_0 = 2.5000572204589844, + cliff_elevation_interval = 2.5000572204589844, + name = 'cliff' + } + }, + -- cliffs to very high frequency, very big size + tree_none = { + autoplace_controls = { + trees = { + frequency = 'normal', + richness = 'normal', + size = 'none' + } + } + }, + -- cliffs to very high frequency, very big size + tree_very_high = { + autoplace_controls = { + trees = { + frequency = 'very-high', + richness = 'very-high', + size = 'very-high' + } + } + }, + -- starting area to very low + starting_area_very_low = { + starting_area = 'very-low' + }, + -- peaceful mode on + peaceful_mode_on = { + peaceful_mode = true + }, + -- random seed, in case you need/want the seed to be unique from nauvis + unique_seed = { + seed = nil + }, + -- grass only + grass_only = { + autoplace_controls = { + grass = {frequency = 'normal', size = 'normal', richness = 'normal'}, + desert = {frequency = 'normal', size = 'none', richness = 'normal'}, + dirt = {frequency = 'normal', size = 'none', richness = 'normal'}, + sand = {frequency = 'normal', size = 'none', richness = 'normal'} + } + }, + -- desert only + desert_only = { + autoplace_controls = { + grass = {frequency = 'normal', size = 'none', richness = 'normal'}, + desert = {frequency = 'normal', size = 'normal', richness = 'normal'}, + dirt = {frequency = 'normal', size = 'none', richness = 'normal'}, + sand = {frequency = 'normal', size = 'none', richness = 'normal'} + } + }, + -- dirt only + dirt_only = { + autoplace_controls = { + grass = {frequency = 'normal', size = 'none', richness = 'normal'}, + desert = {frequency = 'normal', size = 'none', richness = 'normal'}, + dirt = {frequency = 'normal', size = 'normal', richness = 'normal'}, + sand = {frequency = 'normal', size = 'none', richness = 'normal'} + } + }, + -- sand only + sand_only = { + autoplace_controls = { + grass = {frequency = 'normal', size = 'none', richness = 'normal'}, + desert = {frequency = 'normal', size = 'none', richness = 'normal'}, + dirt = {frequency = 'normal', size = 'none', richness = 'normal'}, + sand = {frequency = 'normal', size = 'normal', richness = 'normal'} + } + }, + -- will generate a world with only water (useful for maps that want full terrain control and no entities on the surface) + waterworld = { + autoplace_controls = { + desert = { + frequency = 'normal', + richness = 'normal', + size = 'none' + }, + dirt = { + frequency = 'normal', + richness = 'normal', + size = 'none' + }, + grass = { + frequency = 'normal', + richness = 'normal', + size = 'none' + }, + sand = { + frequency = 'normal', + richness = 'normal', + size = 'none' + } + }, + starting_points = { + { + x = 0, + y = 0 + } + } + }, + -- creates a 1x1 world border, this will prevent chunks from being generated + void = { + height = 1, + width = 1 + } +} diff --git a/resources/map_settings.lua b/resources/map_settings.lua new file mode 100644 index 00000000..c18de210 --- /dev/null +++ b/resources/map_settings.lua @@ -0,0 +1,186 @@ +return { + -- the default table is included as a reference but also to give the option of overwriting all user settings + default = { + pollution = { + enabled = true, + diffusion_ratio = 0.02, + min_to_diffuse = 15, + ageing = 1, + expected_max_per_chunk = 7000, + min_to_show_per_chunk = 700, + min_pollution_to_damage_trees = 3500, + pollution_with_max_forest_damage = 10000, + pollution_per_tree_damage = 2000, + pollution_restored_per_tree_damage = 500, + max_pollution_to_restore_trees = 1000 + }, + enemy_evolution = { + enabled = true, + time_factor = 0.000004, + destroy_factor = 0.002, + pollution_factor = 0.000015 + }, + enemy_expansion = { + enabled = true, + max_expansion_distance = 7, + friendly_base_influence_radius = 2, + enemy_building_influence_radius = 2, + building_coefficient = 0.1, + other_base_coefficient = 2.0, + neighbouring_chunk_coefficient = 0.5, + neighbouring_base_chunk_coefficient = 0.4, + max_colliding_tiles_coefficient = 0.9, + settler_group_min_size = 5, + settler_group_max_size = 20, + min_expansion_cooldown = 4 * 3600, + max_expansion_cooldown = 60 * 3600 + }, + unit_group = { + min_group_gathering_time = 3600, + max_group_gathering_time = 10 * 3600, + max_wait_time_for_late_members = 2 * 3600, + max_group_radius = 30.0, + min_group_radius = 5.0, + max_member_speedup_when_behind = 1.4, + max_member_slowdown_when_ahead = 0.6, + max_group_slowdown_factor = 0.3, + max_group_member_fallback_factor = 3, + member_disown_distance = 10, + tick_tolerance_when_member_arrives = 60, + max_gathering_unit_groups = 30, + max_unit_group_size = 200 + }, + steering = { + default = { + radius = 1.2, + separation_force = 0.005, + separation_factor = 1.2, + force_unit_fuzzy_goto_behavior = false + }, + moving = { + radius = 3, + separation_force = 0.01, + separation_factor = 3, + force_unit_fuzzy_goto_behavior = false + } + }, + path_finder = { + fwd2bwd_ratio = 5, + goal_pressure_ratio = 2, + max_steps_worked_per_tick = 100, + use_path_cache = true, + short_cache_size = 5, + long_cache_size = 25, + short_cache_min_cacheable_distance = 10, + short_cache_min_algo_steps_to_cache = 50, + long_cache_min_cacheable_distance = 30, + cache_max_connect_to_cache_steps_multiplier = 100, + cache_accept_path_start_distance_ratio = 0.2, + cache_accept_path_end_distance_ratio = 0.15, + negative_cache_accept_path_start_distance_ratio = 0.3, + negative_cache_accept_path_end_distance_ratio = 0.3, + cache_path_start_distance_rating_multiplier = 10, + cache_path_end_distance_rating_multiplier = 20, + stale_enemy_with_same_destination_collision_penalty = 30, + ignore_moving_enemy_collision_distance = 5, + enemy_with_different_destination_collision_penalty = 30, + general_entity_collision_penalty = 10, + general_entity_subsequent_collision_penalty = 3, + max_clients_to_accept_any_new_request = 10, + max_clients_to_accept_short_new_request = 100, + direct_distance_to_consider_short_request = 100, + short_request_max_steps = 1000, + short_request_ratio = 0.5, + min_steps_to_check_path_find_termination = 2000, + start_to_goal_cost_multiplier_to_terminate_path_find = 500.0 + }, + max_failed_behavior_count = 3 + }, + -- no pollution + pollution_off = { + pollution = { + enabled = false + } + }, + -- decreases the spread of pollution and increases the absorption per chunk of land + pollution_decreased_per_chunk = { + pollution = { + diffusion_ratio = 0.01, + min_to_diffuse = 30, + ageing = 2 + } + }, + -- tough to spread pollution, pollution rapidly decayse: for venus + pollution_hard_to_spread = { + enabled = true, + diffusion_ratio = 0.01, + min_to_diffuse = 200, + ageing = 5 + }, + -- increases the ability of trees to suck up pollution + pollution_decreased_per_tree = { + pollution = { + pollution_with_max_forest_damage = 20000, + pollution_per_tree_damage = 4000, + max_pollution_to_restore_trees = 2000 + } + }, + -- no enemy evolution + enemy_evolution_off = { + enemy_evolution = { + enabled = false + } + }, + -- evolution from all factors x2 + enemy_evolution_x2 = { + enemy_evolution = { + enabled = true, + time_factor = 0.000008, + destroy_factor = 0.004, + pollution_factor = 0.000030 + } + }, + -- 3x cost for pollution, all else 1x + enemy_evolution_punishes_pollution = { + enemy_evolution = { + enabled = true, + time_factor = 0.000004, + destroy_factor = 0.002, + pollution_factor = 0.000045 + } + }, + -- 3x cost for destroying spawners, all else 1x + enemy_evolution_punishes_destruction = { + enemy_evolution = { + enabled = true, + time_factor = 0.000004, + destroy_factor = 0.006, + pollution_factor = 0.000015 + } + }, + -- no enemy expansion + enemy_expansion_off = { + enemy_expansion = { + enabled = false + } + }, + -- should increase the fequency with which enemies expand + enemy_expansion_frequency_x4 = { + enemy_expansion = { + enabled = true, + min_expansion_cooldown = 1 * 3600, + max_expansion_cooldown = 15 * 3600 + } + }, + -- biters will expand to more chunks and will be more densely packed + enemy_expansion_aggressive = { + enemy_expansion = { + enabled = true, + max_expansion_distance = 21, + friendly_base_influence_radius = 1, + enemy_building_influence_radius = 1, + settler_group_min_size = 1, + settler_group_max_size = 10 + } + } +} diff --git a/resources/market_items.lua b/resources/market_items.lua new file mode 100644 index 00000000..e63da3b4 --- /dev/null +++ b/resources/market_items.lua @@ -0,0 +1,81 @@ +return { + { + name = 'temporary-running-speed-bonus', + name_label = 'Temporary running speed bonus', + type = 'temporary-buff', + description = 'Increases running speed by one level for a short period', + sprite = 'technology/exoskeleton-equipment', + stack_limit = 1, + price = 10, + }, + { + name = 'temporary-mining-speed-bonus', + name_label = 'Temporary mining speed bonus', + type = 'temporary-buff', + description = 'Increases manual mining speed by one level for a short period', + sprite = 'technology/mining-productivity-1', + stack_limit = 1, + price = 10, + }, + {price = 2, name = 'raw-fish'}, + {price = 0.95, name = 'rail'}, + {price = 2, name = 'rail-signal'}, + {price = 2, name = 'rail-chain-signal'}, + {price = 15, name = 'train-stop'}, + {price = 75, name = 'locomotive'}, + {price = 30, name = 'cargo-wagon'}, + {price = 0.95, name = 'red-wire'}, + {price = 0.95, name = 'green-wire'}, + {price = 3, name = 'decider-combinator'}, + {price = 3, name = 'arithmetic-combinator'}, + {price = 3, name = 'constant-combinator'}, + {price = 7, name = 'programmable-speaker'}, + {price = 15, name = 'steel-axe'}, + {price = 15, name = 'submachine-gun'}, + {price = 15, name = 'shotgun'}, + {price = 250, name = 'combat-shotgun'}, + {price = 25, name = 'railgun'}, + {price = 250, name = 'flamethrower'}, + {price = 175, name = 'rocket-launcher'}, + {price = 250, name = 'tank-cannon'}, + {price = 750, name = 'tank-machine-gun'}, + {price = 75, name = 'tank-flamethrower'}, + {price = 2500, name = 'artillery-wagon-cannon'}, + {price = 1, name = 'firearm-magazine'}, + {price = 5, name = 'piercing-rounds-magazine'}, + {price = 20, name = 'uranium-rounds-magazine'}, + {price = 2, name = 'shotgun-shell'}, + {price = 10, name = 'piercing-shotgun-shell'}, + {price = 5, name = 'railgun-dart'}, + {price = 25, name = 'flamethrower-ammo'}, + {price = 15, name = 'rocket'}, + {price = 25, name = 'explosive-rocket'}, + {price = 2500, name = 'atomic-bomb'}, + {price = 20, name = 'cannon-shell'}, + {price = 30, name = 'explosive-cannon-shell'}, + {price = 75, name = 'explosive-uranium-cannon-shell'}, + {price = 100, name = 'artillery-shell'}, + {price = 3, name = 'land-mine'}, + {price = 5, name = 'grenade'}, + {price = 35, name = 'cluster-grenade'}, + {price = 5, name = 'defender-capsule'}, + {price = 75, name = 'destroyer-capsule'}, + {price = 35, name = 'poison-capsule'}, + {price = 35, name = 'slowdown-capsule'}, + {price = 50, name = 'artillery-targeting-remote'}, + {price = 1000, name = 'artillery-turret'}, + {price = 350, name = 'modular-armor'}, + {price = 875, name = 'power-armor'}, + {price = 40, name = 'solar-panel-equipment'}, + {price = 875, name = 'fusion-reactor-equipment'}, + {price = 100, name = 'battery-equipment'}, + {price = 625, name = 'battery-mk2-equipment'}, + {price = 250, name = 'belt-immunity-equipment'}, + {price = 100, name = 'night-vision-equipment'}, + {price = 150, name = 'exoskeleton-equipment'}, + {price = 250, name = 'personal-roboport-equipment'}, + {price = 25, name = 'construction-robot'}, + {price = 350, name = 'energy-shield-equipment'}, + {price = 750, name = 'personal-laser-defense-equipment'}, + {price = 1, name = 'refined-hazard-concrete'}, +} diff --git a/resources/naming_words.lua b/resources/naming_words.lua new file mode 100644 index 00000000..e36c45d6 --- /dev/null +++ b/resources/naming_words.lua @@ -0,0 +1,253 @@ +-- Currently geared towards the winter holidays but we can always revise if needed. +-- (Ex. a table of standard/common words and tables for season-specific words, then combined for specific purposes/use-cases) + +local Module = {} + +Module.adverbs = { + 'Abnormally', + 'Accidentally', + 'Adventurously', + 'Anxiously', + 'Arrogantly', + 'Awkwardly', + 'Bashfully', + 'Beautifully', + 'Blindly', + 'Blissfully', + 'Boastfully', + 'Boldly', + 'Bravely', + 'Calmly', + 'Carefully', + 'Carelessly', + 'Cautiously', + 'Certainly', + 'Cheerfully', + 'Clearly', + 'Closely', + 'Continually', + 'Courageously', + 'Cruelly', + 'Curiously', + 'Dearly', + 'Deeply', + 'Defiantly', + 'Deliberately', + 'Delightfully', + 'Dimly', + 'Doubtfully', + 'Dreamily', + 'Easily', + 'Elegantly', + 'Energetically', + 'Enormously', + 'Enthusiastically', + 'Especially', + 'Eventually', + 'Excitedly', + 'Extremely', + 'Fairly', + 'Faithfully', + 'Famously', + 'Fatally', + 'Ferociously', + 'Fervently', + 'Fiercely', + 'Fondly', + 'Foolishly', + 'Frantically', + 'Frightfully', + 'Furiously', + 'Generally', + 'Generously', + 'Gently', + 'Gladly', + 'Gleefully', + 'Gracefully', + 'Greatly', + 'Happily', + 'Heavily', + 'Helpfully', + 'Helplessly', + 'Honestly', + 'Hopelessly', + 'Hungrily', + 'Innocently', + 'Inquisitively', + 'Intensely', + 'Interestingly', + 'Irritably', + 'Jovially', + 'Joyfully', + 'Kindheartedly', + 'Kindly', + 'Knowingly', + 'Lazily', + 'Lively', + 'Longingly', + 'Loosely', + 'Loudly', + 'Lovingly', + 'Loyally', + 'Madly', + 'Majestically', + 'Merrily', + 'Miserably', + 'Mockingly', + 'Mostly', + 'Mysteriously', + 'Naturally', + 'Nearly', + 'Nicely', + 'Noisily', + 'Obnoxiously', + 'Offensively', + 'Optimistically', + 'Painfully', + 'Patiently', + 'Perfectly', + 'Playfully', + 'Politely', + 'Positively', + 'Powerfully', + 'Questionably', + 'Quickly', + 'Quietly', + 'Randomly', + 'Rapidly', + 'Readily', + 'Reluctantly', + 'Righteously', + 'Safely', + 'Seemingly', + 'Selfishly', + 'Silently', + 'Sleepily', + 'Stealthily', + 'Successfully', + 'Suddenly', + 'Surprisingly', + 'Suspiciously', + 'Sweetly', + 'Tenderly', + 'Thankfully', + 'Unbearably', + 'Unexpectedly', + 'Unfortunately', + 'Unimpressively', + 'Unnecessarily', + 'Urgently', + 'Uselessly', + 'Vaguely', + 'Warmly', + 'Wildly', + 'Wisely', + 'Wrongly', + 'Youthfully', +} + +Module.adjectives = { + 'Amazing', + 'Attractive', + 'Awesome', + 'Beautiful', + 'Big', + 'Blissful', + 'Brave', + 'Breathtaking', + 'Careful', + 'Caroling', + 'Chubby', + 'Clever', + 'Clumsy', + 'Dazzling', + 'Delightful', + 'Eager', + 'Excitable', + 'Fabulous', + 'Faithful', + 'Fancy', + 'Fantastic', + 'Fantastical', + 'Fierce', + 'Gentle', + 'Glamorous', + 'Gorgeous', + 'Great', + 'Grumpy', + 'Happy', + 'Helpful', + 'Interesting', + 'Jolly', + 'Kind', + 'Lazy', + 'Magical', + 'Magnificent', + 'Merry', + 'Muscular', + 'Mystical', + 'Naughty', + 'Nice', + 'Obedient', + 'Peaceful', + 'Plump', + 'Polite', + 'Quaint', + 'Remarkable', + 'Roasting', + 'Scary', + 'Silly', + 'Small', + 'Tender', + 'Thoughtful', + 'White', + 'Witty', + 'Wonderful', +} + +Module.nouns = { + 'Advent', + 'Angel', + 'Bear', + 'Bell', + 'Candy_Cane', + 'Caroler', + 'Chestnut', + 'Christmas_Stocking', + 'Decoration', + 'Elf', + 'Feast', + 'Fruit_Cake', + 'Garland', + 'Gift', + 'Grinch', + 'Ham', + 'Helper', + 'Holiday_Dinner', + 'Holiday_Sweater', + 'Holly', + 'Ice_Skate', + 'Ivy', + 'Menora', + 'Miracle', + 'Mistletoe', + 'Ornament', + 'Package', + 'Party', + 'Present', + 'Reindeer', + 'Ribbon', + 'Scarf', + 'Scrooge', + 'Sleigh', + 'Snowball', + 'Snowflake', + 'Snowman', + 'Sugarplum', + 'Toy', + 'Tree', + 'Turkey', + 'Wreath', +} + +return Module diff --git a/resources/naughty_words.lua b/resources/naughty_words.lua new file mode 100644 index 00000000..b5890bd3 --- /dev/null +++ b/resources/naughty_words.lua @@ -0,0 +1,41 @@ +return { + ['ass'] = true, + ['bugger'] = true, + ['butt'] = true, + ['bum'] = true, + ['bummer'] = true, + ['christ'] = true, + ['crikey'] = true, + ['darn'] = true, + ['dam'] = true, + ['damn'] = true, + ['dang'] = true, + ['dagnabit'] = true, + ['dagnabbit'] = true, + ['drat'] = true, + ['fart'] = true, + ['feck'] = true, + ['frack'] = true, + ['freaking'] = true, + ['frick'] = true, + ['gay'] = true, + ['gee'] = true, + ['geez'] = true, + ['git'] = true, + ['god'] = true, + ['golly'] = true, + ['gosh'] = true, + ['heavens'] = true, + ['heck'] = true, + ['hell'] = true, + ['holy'] = true, + ['jerk'] = true, + ['jesus'] = true, + ['petes'] = true, + ["pete's"] = true, + ['poo'] = true, + ['satan'] = true, + ['willy'] = true, + ['wee'] = true, + ['yikes'] = true +} diff --git a/resources/player_colors.lua b/resources/player_colors.lua new file mode 100644 index 00000000..9b56f480 --- /dev/null +++ b/resources/player_colors.lua @@ -0,0 +1,50 @@ +return { + ['grilledham'] = { + color = {r = 0.9290000202716064, g = 0.3860000739097595, b = 0.51399999856948853, a = 0.5}, + chat_color = {r = 1, g = 0.51999998092651367, b = 0.63300001621246338, a = 0.5} + }, + ['plague006'] = { + color = {r = 64, g = 224, b = 208, a = 0.5}, + chat_color = {r = 175, g = 238, b = 238, a = 0.5} + }, + ['Linaori'] = { + color = {r = 255, g = 255, b = 0, a = 0.5}, + chat_color = {r = 255, g = 255, b = 0, a = 0.5} + }, + ['Jayefuu'] = { + color = {r = 0.559, g = 0.761, b = 0.157, a = 0.5}, + chat_color = {r = 0.708, g = 0.996, b = 0.134, a = 0.5} + }, + ['robertkruijt'] = { + color = {r = 0.275, g = 0.755, b = 0.712, a = 0.5}, + chat_color = {r = 0.335, g = 0.918, b = 0.866, a = 0.5} + }, + ['der-dave.com'] = { + color = {r = 255, g = 162, b = 0, a = 0.5}, + chat_color = {r = 255, g = 162, b = 0, a = 0.5} + }, + ['chromaddict'] = { + color = {r = 0, g = 1, b = 1, a = 0.5}, + chat_color = {r = 0, g = 1, b = 1, a = 0.5} + }, + ['shoghicp'] = { + color = {a = 0, b = 0.50980395078659058, g = 0, r = 0.29411765933036804}, + chat_color = {a = 0, b = 0.50980395078659058, g = 0, r = 0.29411765933036804} + }, + ['aldldl'] = { + color = {r = 0, g = 0, b = 0, a = 0.5}, + chat_color = {r = 0, g = 127, b = 0, a = 0.5} + }, + ['Raiguard'] = { + color = {a = 1, b = 0.50980395078659058, g = 0.54901963472366333, r = 0.13725490868091583}, + chat_color = {a = 1, b = 0.75490200519561768, g = 0.77450978755950928, r = 0.56862747669219971} + }, + ['ferefang'] = { + color = {a = 1, b = 0, g = 0.024000000208616, r = 0.81499999761581}, + chat_color = {a = 1, b = 0.1410000026226, g = 016599999368191, r = 1} + }, + ['Gerkis'] = { + color = {a = 0.5, b = 0.15700000524520874, g = 0.76099997758865356, r = 0.55900001525878906}, + chat_color = {a = 0.5, b = 0.13400000333786011, g = 0.99599999189376831, r = 0.70800000429153442} + }, +} diff --git a/resources/player_sprites.lua b/resources/player_sprites.lua new file mode 100644 index 00000000..4977d9fb --- /dev/null +++ b/resources/player_sprites.lua @@ -0,0 +1,54 @@ +return { + 'item/iron-axe', + 'item/burner-mining-drill', + 'item/burner-inserter', + 'item/stone-furnace', + 'item/light-armor', + 'item/steam-engine', + 'item/inserter', + 'item/transport-belt', + 'item/underground-belt', + 'item/splitter', + 'item/assembling-machine-1', + 'item/long-handed-inserter', + 'item/electronic-circuit', + 'item/electric-mining-drill', + 'item/heavy-armor', + 'item/steel-furnace', + 'item/steel-axe', + 'item/gun-turret', + 'item/fast-transport-belt', + 'item/fast-underground-belt', + 'item/fast-splitter', + 'item/assembling-machine-2', + 'item/fast-inserter', + 'item/radar', + 'item/filter-inserter', + 'item/defender-capsule', + 'item/pumpjack', + 'item/chemical-plant', + 'item/solar-panel', + 'item/advanced-circuit', + 'item/modular-armor', + 'item/accumulator', + 'item/construction-robot', + 'item/distractor-capsule', + 'item/stack-inserter', + 'item/electric-furnace', + 'item/express-transport-belt', + 'item/express-underground-belt', + 'item/express-splitter', + 'item/assembling-machine-3', + 'item/processing-unit', + 'item/power-armor', + 'item/logistic-robot', + 'item/laser-turret', + 'item/stack-filter-inserter', + 'item/destroyer-capsule', + 'item/power-armor-mk2', + 'item/flamethrower-turret', + 'item/beacon', + 'item/steam-turbine', + 'item/centrifuge', + 'item/nuclear-reactor' +} diff --git a/resources/poke_messages.lua b/resources/poke_messages.lua new file mode 100644 index 00000000..4b7b835c --- /dev/null +++ b/resources/poke_messages.lua @@ -0,0 +1,402 @@ +return { + "a stick", + "a leaf", + "a moldy carrot", + "a crispy slice of bacon", + "a french fry", + "a realistic toygun", + "a broomstick", + "a thirteen inch iron stick", + "a mechanical keyboard", + "a fly fishing cane", + "a selfie stick", + "an oversized fidget spinner", + "a thumb extender", + "a dirty straw", + "a green bean", + "a banana", + "an umbrella", + "grandpa's walking stick", + "live firework", + "a toilet brush", + "a fake hand", + "an undercooked hotdog", + "a slice of yesterday's microwaved pizza", + "bubblegum", + "a biter leg", + "grandma's toothbrush", + "charred octopus", + "a dollhouse bathtub", + "a length of copper wire", + "a decommissioned nuke", + "a smelly trout", + "an unopened can of deodorant", + "a stone brick", + "a half full barrel of lube", + "a half empty barrel of lube", + "an unexploded cannon shell", + "a blasting programmable speaker", + "a not so straight rail", + "a mismatched pipe to ground", + "a surplus box of landmines", + "decommissioned yellow rounds", + "an oily pumpjack shaft", + "a melted plastic bar in the shape of the virgin mary", + "a bottle of watermelon vitamin water", + "a slice of watermelon", + "a stegosaurus tibia", + "a basking musician's clarinet", + "a twig", + "an undisclosed pokey item", + "a childhood trophy everyone else got", + "a dead starfish", + "a titanium toothpick", + "a nail file", + "a stamp collection", + "a bucket of lego", + "a rolled up carpet", + "a rolled up WELCOME doormat", + "Bobby's favorite bone", + "an empty bottle of cheap vodka", + "a tattooing needle", + "a peeled cucumber", + "a stack of cotton candy", + "a signed baseball bat", + "that 5 dollar bill grandma sent for christmas", + "a stack of overdue phone bills", + "the 'relax' section of the white pages", + "a bag of gym clothes which never made it to the washing machine", + "a handful of peanut butter", + "a pheasant's feather", + "a rusty pickaxe", + "a diamond sword", + "the bill of rights of a banana republic", + "one of those giant airport Toblerone's", + "a long handed inserter", + "a wiimote", + "an easter chocolate rabbit", + "a ball of yarn the cat threw up", + "a slightly expired but perfectly edible cheese sandwich", + "conclusive proof of lizard people existence", + "a pen drive full of high res wallpapers", + "a pet hamster", + "an oversized goldfish", + "a one foot extension cord", + "a CD from Walmart's 1 dollar bucket", + "a magic wand", + "a list of disappointed people who believed in you", + "murder exhibit no. 3", + "a paperback copy of 'Great Expectations'", + "a baby biter", + "a little biter fang", + "the latest diet fad", + "a belt that no longer fits you", + "an abandoned pet rock", + "a lava lamp", + "some spirit herbs", + "a box of fish sticks found at the back of the freezer", + "a bowl of tofu rice", + "a bowl of ramen noodles", + "a live lobster!", + "a miniature golf cart", + "dunce cap", + "a fully furnished x-mas tree", + "an orphaned power pole", + "an horphaned power pole", + "an box of overpriced girl scout cookies", + "the cheapest item from the yard sale", + "a Sharpie", + "a glowstick", + "a thick unibrow hair", + "a very detailed map of Kazakhstan", + "the official Factorio installation DVD", + "a Liberal Arts degree", + "a pitcher of Kool-Aid", + "a 1/4 pound vegan burrito", + "a bottle of expensive wine", + "a hamster sized gravestone", + "a counterfeit Cuban cigar", + "an old Nokia phone", + "a huge inferiority complex", + "a dead real state agent", + "a deck of tarot cards", + "unreleased Wikileaks documents", + "a mean-looking garden dwarf", + "the actual mythological OBESE cat", + "a telescope used to spy on the MILF next door", + "a fancy candelabra", + "the comic version of the Kama Sutra", + "an inflatable 'Netflix & chill' doll", + "whatever it is redlabel gets high on", + "Obama's birth certificate", + "a deck of Cards Against Humanity", + "a copy of META MEME HUMOR for Dummies", + "an abandoned not-so-young-anymore puppy", + "one of those useless items advertised on TV", + "a genetic blueprint of a Japanese teen idol", + "yesterday’s jam", + "non-stick honey", + "gluten-free gluten", + "a canister of organic petrol", + "the world's tallest midget", + "an inflammable fire extinguisher", + "a chocolate teapot", + "a dvd rewinder", + "an inflatable dart board", + "a copy of ‘How to read for dummies’", + "an English to Spanish dictionary written entirely in Chinese", + "a camouflage high vis jacket", + "soap that has been dropped in the communal showers", + "a portal to the other side", + "", + "a limited offer, one time only poke", + "the poke-o-matic 4000", + "their finger where the sun doesn’t shine", + "a flogged dead horse", + "a chicken that came before the egg", + "a calculator that can divide by zero", + "a dial up internet connection", + "some stuff they found on the underside of the map", + "a proof that the world is flat", + "a radioactive cockroach", + "the second season of Firefly", + "a non-random random number generator", + "last weeks crash report", + "a pair of two left shoes", + "the meaning of life", + "a map of the London underground", + "an oversized foam hand", + "weight loss pills", + "a deconstruction planner that new players can use", + "a fish that is the same size as a grain of rice", + "a videotape of what they did last Summer", + "a love note from the cute girl sitting behind them", + "a cup of dihydrogen monoxide", + "an external combustion engine", + "a profound sense of optimism", + "a yoghurt past its use by date", + "steel beams that can’t be melted with jet fuel", + "a picture of who really shot JFK", + "footage of the faked Apollo landing", + "some of their holiday photos", + "the password for the server mainframe", + "ʇxǝʇ uʍop ǝpısdn ǝɯos", + "their lost jumper", + "a melted ice cube", + "a box where they keep all their secrets", + "a piece of paper that says ‘You are reading this’", + "a book with the answer to every lateral thinking puzzle that hasn’t been made yet", + "a list of cheat codes for Factorio", + "a lemon that looks and taste like a lime", + "a bible for atheists", + "99 red balloons", + "a biter egg", + "what the cat dragged in", + "a straight banana", + "a clock where all the hours say ‘Party time’", + "some overcooked bacon", + "a copy of a copy of a copy of a cloning machine", + "trademark infringement", + "their undying, unquestionable love", + "an old Nintendo cartridge that only works if you blow on it", + "all their hopes and ambitions condensed into a single action", + "their existential crisis", + "a call for their attention", + "an antimatter version of themselves", + "a left handed tin opener", + "a fireplace poker", + "a parallel universe in which the poking happens the other way round", + "a message in a bottle", + "spaghetti bolognese", + "an active proximity mine", + "their fear of rejection", + "all the effort it takes to press a button in a video game", + "a genuine fake Rolex watch", + "the last digit of Pi", + "the 0.17 patch notes", + "a chest full of landfill", + "a Comcast call centre", + "a 3-10-3 train", + "a train deadlock", + "an incorrectly placed rail signal", + "a desync log", + "'error executing command'", + "a stack overflow", + "Trump’s tax return", + "an ego deflating machine", + "the keys to Redlabel’s gentleman's club", + "a used voodoo doll", + "a cheese grater that has seen better days", + "a bowl of swinger’s car keys", + "a student’s cookbook where all the pages are damp", + "a copy of ‘How to start a cult’ covered in Redlabel’s fingerprints", + "a censored copy of 1984", + "the local orphanage’s ice cream delivery", + "a map to the treasure", + "the clothes they use for cross dressing at the weekend", + "their student loans", + "a cattle prod", + "forbidden love", + "an innocent looking poke, but deep down their heart longs for so much more", + "a rough draft of their new rom-com script", + "a finger covered in whipped cream and an expression that says there is plenty more where that came from", + "an empty toilet roll tube and a look of urgent desperation", + "a kitchen sink that does everything except work as a sink", + "teenage angst", + "sexually repressive parents", + "an overdue pregnant women", + "biter pheromones", + "a mail order bride", + "an unoriginal message", + "a subscription to the magazine ‘Identifying wood’", + "their shopping list", + "the monster that lives under their bed", + "a creaky floorboard", + "an ask for forgiveness", + "suspicious intentions", + "all of their childhood nightmares", + "an expired winning lottery ticket", + "all the trash that’s piling up round back", + "The location where they bury the bodies", + "a fear of trains", + "a piece of blu tack that is covered in hairs", + "a life filled with regret", + "a lifejacket for a child", + "a neglected tamagotchi", + "an obese cat", + "a randomly selected predetermined message", + "a copy of the Kamasutra with some of the pages missing", + "the american dream", + "a fluffy pillow", + "the Spanish Inquisition", + "an addiction to Cracktorio", + "a broken mirror", + "a dismembered hooker", + "a popped balloon", + "a hipster breakfast", + "a nintendo cartridge", + "a generic greething card", + "an empty bottle of barbiturics", + "a cleanup on aisle 4", + "the Eiffel Tower", + "an idea for a poke message", + "Hodor's brain", + "a barn full of mice", + "thousands of wooden chests", + "a deconstruction planner for environment only", + "a blueprint that makes a lag machine", + "a poorly setup Miditorio blueprint", + "their chores", + "a random adjective followed by a random noun", + "the Communist Manifesto for capitalists", + "an uncomputable number", + "a decimal in factorio", + "an old redmew fun fact", + "a poke", + "a cheeto that looks like Abraham Lincoln", + "the lack of hope these messages give", + "recursives with no end condition", + "a player poking a player with a player poking a player with...", + "Ebenezer Scrooge's wallet", + "a hundred dollars paid in pennies", + "a game of paintball in a room filled with mirrors", + "a hunted house running away from a rifle shot", + "a breakfast of bleach and nails", + "the time of day", + "the square root of two", + "two plus two minus one", + "a textbook on elementary calculus in group theory", + "a younger version of themself", + "a grabbing claw", + "a missing comma in code", + "a calculator dividing by zero", + "ultrared ultraviolet light", + "the originality in half of these", + "a collection of Sim's patch notes", + "a game of Dwarf Fortress", + "immense eye strain", + "Ctrl + Alt + F4", + "a match box in a boxing glove", + "a goat with a pen inside", + "the amount of iterations before bogosort sorts 100 objects", + "reddit.com/r/redmew", + "a recording of Seeburg background music", + "an ever-expanding trumpet", + "the current number of poke messages: 556", + "the twelve pokes of pokemas", + "a pokey pokemon", + "a spot in the Boston Marathon", + "an eleven-seven", + "an engineer in a car in a rocket", + "the fact that you can landfill over a pump", + "a fish that swims in land", + "an everything bagel without the bagel", + "the three hundred thirty third poke message", + "a fake holiday", + "poke messages that are useful", + "a recorder sonata", + "a real cannon", + "lorem ipsum dolor sit amet consectetuer adipiscing elit sed diam nonummy nibh euismod tincidunt", + "a handle taped on a handlebar mustache", + "a Rick and Morty copypasta", + "a skip button that skips to the next ad", + "a loading screen that doesn't tell you progress", + "the acceleration of earth's gravity", + "a poke repeated twice", + "a poke repeated twice", + "a sentence intentionnaly mispeeled", + "an overly proud committee", + "a narcissitic history book", + "a fruit more intelligent than cows", + "a pig the size of your pinky", + "a schizophrenic sorting algorithm", + "a dye of rhea", + "a stick the dog brought in", + "the yellow paint on a pencil", + "a flag of surrender", + "their private airstrip", + "a well with a hundred frogs", + "syntax errors", + "a ten foot chin", + "a goldfish that plays jazz", + "a free lettuce", + "a hilarious sock", + "jean skirts", + "a fire hydrant that shoots fire", + "a teethbrush", + "a toothsbrush", + "a belief that caboose plural is cabeese", + "an unidentified flying object", + "a bigfoot sighting", + "their determination", + "the steadily declining quality of these poke messages as I run out of ideas", + "a random generator for these messages", + "a hat made for whales", + "an ice cube on fire", + "a ring that wears you", + "an everyday noun", + "a fun fact", + "the fun fact: there are three other fun fact messages", + "the fun fact: most calico cats are female", + "the fun fact: sloths move about 2.564 times faster than snails", + "the fun fact: there are only two actual fun facts", + "a broken clock in prison", + "a wishing well with more money than you", + "a snake in a hole", + "a colossal toad", + "a pair of shoes for a colossal toad", + "an alarm that sounds like the fire alarm", + "a sponge made out of ice", + "a rude key", + "Hodor", + "a moonwalking horse", + "a horn made out of jello", + "a fine wine negative one year old", + "a six running away from seven", + "an island of cannibals that's sinking", + "a respectful insult", + "a highfalutin retort", + "a rabbit in the briar patch", + "a petition to respell poker as poke her", + "the four hundredth poke message", +} diff --git a/resources/ranks.lua b/resources/ranks.lua new file mode 100644 index 00000000..977bc9e5 --- /dev/null +++ b/resources/ranks.lua @@ -0,0 +1,8 @@ +-- When adding/removing/changing ranks, rank_system has a migrate_data() function you can use to adjust the existing data. +return { + probation = -10, + guest = 0, + auto_trusted = 10, + regular = 20, + admin = 30, +} diff --git a/resources/tag_groups.lua b/resources/tag_groups.lua new file mode 100644 index 00000000..527a9bf0 --- /dev/null +++ b/resources/tag_groups.lua @@ -0,0 +1,53 @@ +-- A part of band.lua +-- Feel free to edit. + +return { + ['Trooper'] = { + path = 'item/tank', + verb = 'strengthened' + }, + ['Mining'] = { + path = 'item/electric-mining-drill', + verb = 'enriched' + }, + ['Smelting'] = { + path = 'item/stone-furnace', + verb = 'fused' + }, + ['Production'] = { + path = 'item/assembling-machine-2', + verb = 'enhanced' + }, + ['Science'] = { + path = 'item/science-pack-3', + verb = 'advanced' + }, + ['Wizard'] = { + path = 'item/green-wire', + verb = 'combinated' + }, + ['Trains'] = { + path = 'item/locomotive', + verb = 'derailed' + }, + ['Oil'] = { + path = 'fluid/crude-oil', + verb = 'lubricated' + }, + ['Powah!'] = { + path = 'item/steam-engine', + verb = 'electrified' + }, + ['Spaceman'] = { + path = 'item/rocket-silo', + verb = 'warped' + }, + ['Cat'] = { + path = 'item/raw-fish', + verb = 'mewed' + }, + ['Dog'] = { + path = 'entity/medium-biter', + verb = 'woofed' + } +} diff --git a/resources/turkey_messages.lua b/resources/turkey_messages.lua new file mode 100644 index 00000000..86876ec0 --- /dev/null +++ b/resources/turkey_messages.lua @@ -0,0 +1,57 @@ +return { + 'Benjamin Franklin wanted the turkey to be the national bird, not the eagle.', + 'There was no turkey on the menu at the first Thanksgiving.', + 'Thanksgiving is the reason for TV dinners!', + 'Wild turkeys can run 20 miles per hour when they are scared.', + 'Female turkeys (called hens) do not gobble.', + 'The real first Thanksgiving was held in Texas in 1541.', + 'Baby turkeys are called poults.', + 'The best way to tell if a cranberry is ripe it to see if it bounces.', + 'Benjamin Franklin wanted the turkey to be the national bird, not the eagle.', + 'There was no turkey on the menu at the first Thanksgiving.', + 'Thanksgiving is the reason for TV dinners!', + 'Wild turkeys can run 20 miles per hour when they are scared.', + 'Female turkeys (called hens) do not gobble.', + 'The real first Thanksgiving was held in Texas in 1541.', + 'Baby turkeys are called poults.', + 'The best way to tell if a cranberry is ripe it to see if it bounces.', + 'There were no forks at the first Thanksgiving. ', + 'Thomas Jefferson refused to declare Thanksgiving as a holiday.', + 'About 46 million turkeys are cooked for Thanksgiving each year.', + 'The Butterball Turkey Talk Line answers almost 100,000 calls each season.', + 'There are four places in the US named Turkey.', + 'Black Friday is the busiest day of the year for plumbers.', + 'Jingle Bells was originally a Thanksgiving song.', + 'Turkey-like creatures roamed the Americas 75 million years ago.', + 'Canadian Thanksgiving predates American Thanksgiving by 43 years.', + 'On average, it takes about 7 hours to cook a Thanksgiving dinner. People spend about 16 minutes eating it.', + 'The first Thanksgiving was held in the autumn of 1621', + 'Why did the turkey cross the road? It was Thanksgiving and he wanted to convince people he was a chicken.', + 'What did the turkey say to the computer? "Google, google, google."', + 'Why did the farmer separate the turkey and the chicken? He sensed fowl play.', + 'What music did the Pilgrims listen to? Plymouth rock.', + 'If Pilgrims were alive today what would they be known for? Their age!', + 'What does Miley Cyrus eat for Thanksgiving? Twerky.', + 'If your great-grandmother saw you making boxed mashed potatoes shed turn over in her gravy.', + 'What does a turkey drink from? A gobble-t.', + 'What smells best at Thanksgiving dinner? Your nose.', + 'Thanksgiving is the only holiday where you eat the mascot.', + 'How do you keep a turkey in suspense? Ill tell you later.', + 'My family told me to stop telling bad Thanksgiving jokes, but I couldnt just quit cold turkey.', + 'What kind of music did the Pilgrims like? Plymouth Rock ', + 'If April showers bring May flowers, what do May flowers bring? Pilgrims ', + 'Why cant you take a turkey to church? They use FOWL language. ', + 'Why was the Thanksgiving soup so expensive? It had 24 carrots. ', + 'What happened when the turkey got into a fight? He got the stuffing knocked out of him! ', + 'What do you get when you cross a turkey with a banjo? A turkey that can pluck itself! ', + 'When do you serve tofu turkey? Pranksgiving. ', + 'What did the turkey say to the man who tried to shoot it? Liberty, Equality and Bad aim for all. ', + 'Who doesnt eat on Thanksgiving? A turkey because it is always stuffed. ', + 'Why did the Pilgrims want to sail to America in the spring? Because April showers bring Mayflowers! ', + 'What did baby corn say to mama corn? Wheres popcorn? ', + 'If the Pilgrims were alive today, what would they be most famous for? Their AGE! ', + 'Why do the pants of pilgrims keep falling down? Because their belt buckles are on their hats! ', + 'Why did they let the turkey join the band? Because he had the drumsticks ', + 'What does Miley Cyrus eat for Thanksgiving? Twerk-ey! ', + 'What did the mother turkey say to her disobedient children? "If your father could see you now, hed turn over in his gravy!" ' +} diff --git a/resources/version.lua b/resources/version.lua new file mode 100644 index 00000000..98d7ac58 --- /dev/null +++ b/resources/version.lua @@ -0,0 +1,2 @@ +global.redmew_version = nil +global.expgaming_version = '5.0.0' \ No newline at end of file diff --git a/utils/alien_evolution_progress.lua b/utils/alien_evolution_progress.lua new file mode 100644 index 00000000..343aff82 --- /dev/null +++ b/utils/alien_evolution_progress.lua @@ -0,0 +1,179 @@ +--[[-- info + Original (javascript) version: https://hastebin.com/udakacavap.js + Can be tested against: https://wiki.factorio.com/Enemies#Spawn_chances_by_evolution_factor +]] + +-- dependencies +local Global = require 'utils.global' +local Debug = require 'utils.debug' +local table = require 'utils.table' + +-- localized functions +local get_random_weighted = table.get_random_weighted +local round = math.round +local ceil = math.ceil +local floor = math.floor +local random = math.random +local pairs = pairs +local format = string.format + +-- this +local AlienEvolutionProgress = {} + +local memory = { + spawner_specifications = {}, + spawner_specifications_count = 0, + evolution_cache = { + ['biter-spawner'] = { + evolution = -1, + weight_table = {}, + }, + ['spitters-spawner'] = { + evolution = -1, + weight_table = {}, + }, + }, +} + +Global.register_init({ + memory = memory, +}, function(tbl) + for name, prototype in pairs(game.entity_prototypes) do + if prototype.type == 'unit-spawner' and prototype.subgroup.name == 'enemies' then + tbl.memory.spawner_specifications[name] = prototype.result_units + memory.spawner_specifications_count = memory.spawner_specifications_count + 1 + end + end +end, function(tbl) + memory = tbl.memory +end) + +local function lerp(low, high, pos) + local s = high.evolution_factor - low.evolution_factor; + local l = (pos - low.evolution_factor) / s; + return (low.weight * (1 - l)) + (high.weight * l) +end + +local function get_values(map, evolution_factor) + local result = {} + local sum = 0 + + for _, spawner_data in pairs(map) do + local list = spawner_data.spawn_points; + local low = list[1]; + local high = list[#list]; + + for _, val in pairs(list) do + local val_evolution = val.evolution_factor + if val_evolution <= evolution_factor and val_evolution > low.evolution_factor then + low = val; + end + if val_evolution >= evolution_factor and val_evolution < high.evolution_factor then + high = val + end + end + + local val + if evolution_factor <= low.evolution_factor then + val = low.weight + elseif evolution_factor >= high.evolution_factor then + val = high.weight; + else + val = lerp(low, high, evolution_factor) + end + sum = sum + val; + + result[spawner_data.unit] = val; + end + + local weighted_table = {} + local count = 0 + for index, _ in pairs(result) do + count = count + 1 + weighted_table[count] = {index, result[index] / sum} + end + + return weighted_table; +end + +local function get_spawner_values(spawner, evolution) + local spawner_specification = memory.spawner_specifications[spawner] + if not spawner_specification then + Debug.print(format('Spawner "%s" does not exist in the prototype data', spawner)) + return + end + + local cache = memory.evolution_cache[spawner] + + if not cache then + cache = { + evolution = -1, + weight_table = {}, + } + memory.evolution_cache[spawner] = cache + end + + local evolution_value = round(evolution * 100) + if (cache.evolution < evolution_value) then + cache.evolution = evolution_value + cache.weight_table = get_values(spawner_specification, evolution) + end + + return cache.weight_table +end + +local function calculate_total(count, spawner, evolution) + if count == 0 then + return {} + end + + local spawner_values = get_spawner_values(spawner, evolution) + if not spawner_values then + return {} + end + + local aliens = {} + for _ = 1, count do + local name = get_random_weighted(spawner_values) + aliens[name] = (aliens[name] or 0) + 1 + end + + return aliens +end + +---Creates the spawner_request structure required for AlienEvolutionProgress.get_aliens for all +---available spawners. If dividing the total spawners by the total aliens causes a fraction, the +---fraction will decide a chance to spawn. 1 alien for 2 spawners will have 50% on both. +---@param total_aliens table +function AlienEvolutionProgress.create_spawner_request(total_aliens) + local per_spawner = total_aliens / memory.spawner_specifications_count + local fraction = per_spawner % 1 + + local spawner_request = {} + for spawner, _ in pairs(memory.spawner_specifications) do + local count = per_spawner + if fraction > 0 then + if random() > fraction then + count = ceil(count) + else + count = floor(count) + end + end + spawner_request[spawner] = count + end + + return spawner_request +end + +function AlienEvolutionProgress.get_aliens(spawner_requests, evolution) + local aliens = {} + for spawner, count in pairs(spawner_requests) do + for name, amount in pairs(calculate_total(count, spawner, evolution)) do + aliens[name] = (aliens[name] or 0) + amount + end + end + + return aliens +end + +return AlienEvolutionProgress diff --git a/utils/command.lua b/utils/command.lua new file mode 100644 index 00000000..5f57110a --- /dev/null +++ b/utils/command.lua @@ -0,0 +1,326 @@ +local Event = require 'utils.event' +local Game = require 'utils.game' +local Utils = require 'utils.core' +local Timestamp = require 'utils.timestamp' +local Rank = require 'features.rank_system' +local Donator = require 'features.donator' +local Server = require 'features.server' +local Ranks = require 'resources.ranks' + +local insert = table.insert +local format = string.format +local next = next +local serialize = serpent.line +local match = string.match +local gmatch = string.gmatch +local get_rank_name = Rank.get_rank_name + +local Command = {} + +local deprecated_command_alternatives = { + ['silent-command'] = 'sc', + ['tpplayer'] = 'tp ', + ['tppos'] = 'tp', + ['tpmode'] = 'tp mode', + ['color-redmew'] = 'redmew-color' +} + +local notify_on_commands = { + ['version'] = 'RedMew has a version as well, accessible via /redmew-version', + ['color'] = 'RedMew allows color saving and a color randomizer: check out /redmew-color', + ['ban'] = 'In case your forgot: please remember to include a message on how to appeal a ban' +} + +local option_names = { + ['description'] = 'A description of the command', + ['arguments'] = 'A table of arguments, example: {"foo", "bar"} would map the first 2 arguments to foo and bar', + ['default_values'] = 'A default value for a given argument when omitted, example: {bar = false}', + ['required_rank'] = 'Set this to determins what rank is required to execute a command', + ['donator_only'] = 'Set this to true if only donators may execute this command', + ['debug_only'] = 'Set this to true if it should be registered when _DEBUG is true', + ['cheat_only'] = 'Set this to true if it should be registered when _CHEATS is true', + ['allowed_by_server'] = 'Set to true if the server (host) may execute this command', + ['allowed_by_player'] = 'Set to false to disable players from executing this command', + ['log_command'] = 'Set to true to log commands. Always true when admin is required', + ['capture_excess_arguments'] = 'Allows the last argument to be the remaining text in the command', + ['custom_help_text'] = 'Sets a custom help text to override the auto-generated help', +} + +---Validates if there aren't any wrong fields in the options. +---@param command_name string +---@param options table +local function assert_existing_options(command_name, options) + local invalid = {} + for name, _ in pairs(options) do + if not option_names[name] then + insert(invalid, name) + end + end + + if next(invalid) then + error(format("The following options were given to the command '%s' but are invalid: %s", command_name, serialize(invalid))) + end +end + +---Adds a command to be executed. +--- +---Options table accepts the following structure: { +--- description = 'A description of the command', +--- arguments = {'foo', 'bar'}, -- maps arguments to these names in the given sequence +--- default_values = {bar = false}, -- gives a default value to 'bar' when omitted +--- required_rank = Ranks.regular, -- defaults to Ranks.guest +--- donator_only = true, -- defaults to false +--- debug_only = true, -- registers the command if _DEBUG is set to true, defaults to false +--- cheat_only = true, -- registers the command if _CHEATS is set to true, defaults to false +--- allowed_by_server = true, -- lets the server execute this, defaults to false +--- allowed_by_player = false, -- lets players execute this, defaults to true +--- log_command = true, -- defaults to false unless admin only, then always true +--- capture_excess_arguments = true, -- defaults to false, captures excess arguments in the last argument, useful for sentences +---} +--- +---The callback receives the following arguments: +--- - arguments (indexed by name, value is extracted from the parameters) +--- - the LuaPlayer or nil if it doesn't exist (such as the server player) +--- - the game tick in which the command was executed +--- +---@param command_name string +---@param options table +---@param callback function +function Command.add(command_name, options, callback) + local description = options.description or '[Undocumented command]' + local arguments = options.arguments or {} + local default_values = options.default_values or {} + local required_rank = options.required_rank or Ranks.guest + local donator_only = options.donator_only or false + local debug_only = options.debug_only or false + local cheat_only = options.cheat_only or false + local capture_excess_arguments = options.capture_excess_arguments or false + local custom_help_text = options.custom_help_text or false + local allowed_by_server = options.allowed_by_server or false + local allowed_by_player = options.allowed_by_player + local log_command = options.log_command or (required_rank >= Ranks.admin) or false + local argument_list_size = table_size(arguments) + local argument_list = '' + + assert_existing_options(command_name, options) + + if nil == options.allowed_by_player then + allowed_by_player = true + end + + if (not _DEBUG and debug_only) and (not _CHEATS and cheat_only) then + return + end + + if not allowed_by_player and not allowed_by_server then + error(format("The command '%s' is not allowed by the server nor player, please enable at least one of them.", command_name)) + end + + for index, argument_name in pairs(arguments) do + local argument_display = argument_name + for default_value_name, _ in pairs(default_values) do + if default_value_name == argument_name then + argument_display = argument_display .. ':optional' + break + end + end + + if argument_list_size == index and capture_excess_arguments then + argument_display = argument_display .. ':sentence' + end + + argument_list = format('%s<%s> ', argument_list, argument_display) + end + + local extra = '' + + if allowed_by_server and not allowed_by_player then + extra = ' (Server only)' + elseif allowed_by_player and (required_rank > Ranks.guest) then + extra = {'command.required_rank', get_rank_name(required_rank)} + elseif allowed_by_player and donator_only then + extra = ' (Donator only)' + end + + local help_text = {'command.help_text_format',(custom_help_text or argument_list), description, extra} + + commands.add_command(command_name, help_text, function (command) + local print -- custom print reference in case no player is present + local player = game.player + local player_name = player and player.valid and player.name or '' + if not player or not player.valid then + print = log + + if not allowed_by_server then + print(format("The command '%s' is not allowed to be executed by the server.", command_name)) + return + end + else + print = player.print + + if not allowed_by_player then + print(format("The command '%s' is not allowed to be executed by players.", command_name)) + return + end + + if Rank.less_than(player_name, required_rank) then + print({'command.higher_rank_needed', command_name, get_rank_name(required_rank)}) + return + end + + if donator_only and not Donator.is_donator(player_name) then + print(format("The command '%s' is only allowed for donators.", command_name)) + return + end + end + + local named_arguments = {} + local from_command = {} + local raw_parameter_index = 1 + for param in gmatch(command.parameter or '', '%S+') do + if capture_excess_arguments and raw_parameter_index == argument_list_size then + if not from_command[raw_parameter_index] then + from_command[raw_parameter_index] = param + else + from_command[raw_parameter_index] = from_command[raw_parameter_index] .. ' ' .. param + end + else + from_command[raw_parameter_index] = param + raw_parameter_index = raw_parameter_index + 1 + end + end + + local errors = {} + + for index, argument in pairs(arguments) do + local parameter = from_command[index] + + if not parameter then + for default_value_name, default_value in pairs(default_values) do + if default_value_name == argument then + parameter = default_value + break + end + end + end + + if parameter == nil then + insert(errors, format('Argument "%s" from command %s is missing.', argument, command_name)) + else + named_arguments[argument] = parameter + end + end + + local return_early = false + + for _, error in pairs(errors) do + return_early = true + print(error) + end + + if return_early then + return + end + + if log_command then + local tick = 'pre-game' + if game then + tick = Utils.format_time(game.tick) + end + local server_time = Server.get_current_time() + if server_time then + server_time = format('(Server time: %s)', Timestamp.to_string(server_time)) + else + server_time = '' + end + + log(format('%s(Map time: %s) [%s Command] %s, used: %s %s', server_time, tick, (options.required_rank >= Ranks.admin) and 'Admin' or 'Player', player_name, command_name, serialize(named_arguments))) + end + + local success, error = pcall(function () + callback(named_arguments, player, command.tick) + end) + + if not success then + local serialized_arguments = serialize(named_arguments) + if _DEBUG then + print(format("%s triggered an error running a command and has been logged: '%s' with arguments %s", player_name, command_name, serialized_arguments)) + print(error) + return + end + + print(format('There was an error running %s, it has been logged.', command_name)) + log(format("Error while running '%s' with arguments %s: %s", command_name, serialized_arguments, error)) + end + end) +end + +function Command.search(keyword) + local matches = {} + local count = 0 + keyword = keyword:lower() + for name, description in pairs(commands.commands) do + local command = format('%s %s', name, description) + if match(command:lower(), keyword) then + count = count + 1 + matches[count] = command + end + end + + -- built-in commands use LocalisedString, which cannot be translated until player.print is called + for name in pairs(commands.game_commands) do + name = name + if match(name:lower(), keyword) then + count = count + 1 + matches[count] = name + end + end + + return matches +end + +--- Trigger messages on deprecated or defined commands, ignores the server +local function on_command(event) + if not event.player_index then + return + end + + local alternative = deprecated_command_alternatives[event.command] + if alternative then + local player = Game.get_player_by_index(event.player_index) + if player then + player.print(format('Warning! Usage of the command "/%s" is deprecated. Please use "/%s" instead.', event.command, alternative)) + end + end + + local notification = notify_on_commands[event.command] + if notification and event.player_index then + local player = Game.get_player_by_index(event.player_index) + if player then + player.print(notification) + end + end +end + +--- Traps command errors if not in DEBUG. +if not _DEBUG then + local old_add_command = commands.add_command + commands.add_command = + function(name, desc, func) + old_add_command( + name, + desc, + function(cmd) + local success, error = pcall(func, cmd) + if not success then + log(error) + Game.player_print('Sorry there was an error running ' .. cmd.name) + end + end + ) + end +end + +Event.add(defines.events.on_console_command, on_command) + +return Command diff --git a/utils/core.lua b/utils/core.lua new file mode 100644 index 00000000..d8bdc6b3 --- /dev/null +++ b/utils/core.lua @@ -0,0 +1,246 @@ +-- This file contains core utilities used by the redmew scenario. + +-- Dependencies +local Game = require 'utils.game' +local Color = require 'resources.color_presets' +local Server = require 'features.server' + +-- localized functions +local random = math.random +local sqrt = math.sqrt +local floor = math.floor +local format = string.format +local match = string.match +local insert = table.insert +local concat = table.concat + +-- local constants +local prefix = '## - ' +local minutes_to_ticks = 60 * 60 +local hours_to_ticks = 60 * 60 * 60 +local ticks_to_minutes = 1 / minutes_to_ticks +local ticks_to_hours = 1 / hours_to_ticks + +-- local vars +local Module = {} + +--- Measures distance between pos1 and pos2 +function Module.distance(pos1, pos2) + local dx = pos2.x - pos1.x + local dy = pos2.y - pos1.y + return sqrt(dx * dx + dy * dy) +end + +--- Takes msg and prints it to all players except provided player +-- @param msg table if locale is used +-- @param player the player not to send the message to +-- @param color the color to use for the message, defaults to white +function Module.print_except(msg, player, color) + if not color then + color = Color.white + end + + for _, p in pairs(game.connected_players) do + if p ~= player then + p.print(msg, color) + end + end +end + +--- Prints a message to all online admins +-- @param msg table if locale is used +-- @param source string must be the name of a player, nil for server. +function Module.print_admins(msg, source) + local source_name + local chat_color + if source then + if type(source) == 'string' then + source_name = source + chat_color = game.players[source].chat_color + else + source_name = source.name + chat_color = source.chat_color + end + else + source_name = 'Server' + chat_color = Color.yellow + end + local formatted_msg = {'utils_core.print_admins',prefix, source_name, msg} + log(formatted_msg) + for _, p in pairs(game.connected_players) do + if p.admin then + p.print(formatted_msg, chat_color) + end + end +end + +--- Returns a valid string with the name of the actor of a command. +function Module.get_actor() + if game.player then + return game.player.name + end + return '' +end + +function Module.cast_bool(var) + if var then + return true + else + return false + end +end + +function Module.find_entities_by_last_user(player, surface, filters) + if type(player) == 'string' or not player then + error("bad argument #1 to '" .. debug.getinfo(1, 'n').name .. "' (number or LuaPlayer expected, got " .. type(player) .. ')', 1) + return + end + if type(surface) ~= 'table' and type(surface) ~= 'number' then + error("bad argument #2 to '" .. debug.getinfo(1, 'n').name .. "' (number or LuaSurface expected, got " .. type(surface) .. ')', 1) + return + end + local entities = {} + local filter = filters or {} + if type(surface) == 'number' then + surface = game.surfaces[surface] + end + if type(player) == 'number' then + player = Game.get_player_by_index(player) + end + filter.force = player.force.name + for _, e in pairs(surface.find_entities_filtered(filter)) do + if e.last_user == player then + insert(entities, e) + end + end + return entities +end + +function Module.ternary(c, t, f) + if c then + return t + else + return f + end +end + +--- Takes a time in ticks and returns a string with the time in format "x hour(s) x minute(s)" +function Module.format_time(ticks) + local result = {} + + local hours = floor(ticks * ticks_to_hours) + if hours > 0 then + ticks = ticks - hours * hours_to_ticks + insert(result, hours) + if hours == 1 then + insert(result, 'hour') + else + insert(result, 'hours') + end + end + + local minutes = floor(ticks * ticks_to_minutes) + insert(result, minutes) + if minutes == 1 then + insert(result, 'minute') + else + insert(result, 'minutes') + end + + return concat(result, ' ') +end + +--- Prints a message letting the player know they cannot run a command +-- @param name string name of the command +function Module.cant_run(name) + Game.player_print("Can't run command (" .. name .. ') - insufficient permission.') +end + +--- Logs the use of a command and its user +-- @param actor string with the actor's name (usually acquired by calling get_actor) +-- @param command the command's name as table element +-- @param parameters the command's parameters as a table (optional) +function Module.log_command(actor, command, parameters) + local action = concat {'[Admin-Command] ', actor, ' used: ', command} + if parameters then + action = concat {action, ' ', parameters} + end + log(action) +end + +function Module.comma_value(n) -- credit http://richard.warburton.it + local left, num, right = match(n, '^([^%d]*%d)(%d*)(.-)$') + return left .. (num:reverse():gsub('(%d%d%d)', '%1,'):reverse()) .. right +end + +--- Asserts the argument is one of type arg_types +-- @param arg the variable to check +-- @param arg_types the type as a table of sings +-- @return boolean +function Module.verify_mult_types(arg, arg_types) + for _, arg_type in pairs(arg_types) do + if type(arg) == arg_type then + return true + end + end + return false +end + +--- Returns a random RGB color as a table +function Module.random_RGB() + return {r = random(0, 255), g = random(0, 255), b = random(0, 255)} +end + +--- Sets a table element to value while also returning value. +-- @param tbl table to change the element of +-- @param key string +-- @param value nil|boolean|number|string|table to set the element to +-- @return value +function Module.set_and_return(tbl, key, value) + tbl[key] = value + return value +end + +--- Takes msg and prints it to all players. Also prints to the log and discord +-- @param msg The message to print +-- @param warning_prefix The name of the module/warning +function Module.action_warning(warning_prefix, msg) + game.print(prefix .. msg, Color.yellow) + msg = format('%s %s', warning_prefix, msg) + log(msg) + Server.to_discord_bold(msg) +end + +--- Takes msg and prints it to all players except provided player. Also prints to the log and discord +-- @param msg The message to print +-- @param warning_prefix The name of the module/warning +-- @param player the player not to send the message to +function Module.silent_action_warning(warning_prefix, msg, player) + Module.print_except(prefix .. msg, player, Color.yellow) + msg = format('%s %s', warning_prefix, msg) + log(msg) + Server.to_discord_bold(msg) +end + +-- add utility functions that exist in base factorio/util +require 'util' + +--- Moves a position according to the parameters given +-- Notice: only accepts cardinal directions as direction +-- @param position
table containing a map position +-- @param direction north, east, south, west +-- @param distance +-- @return
modified position +Module.move_position = util.moveposition + +--- Takes a direction and gives you the opposite +-- @param direction north, east, south, west, northeast, northwest, southeast, southwest +-- @return representing the direction +Module.opposite_direction = util.oppositedirection + +--- Takes the string of a module and returns whether is it available or not +-- @param name the name of the module (ex. 'utils.core') +-- @return +Module.is_module_available = util.ismoduleavailable + +return Module diff --git a/utils/dump_env.lua b/utils/dump_env.lua new file mode 100644 index 00000000..b5cbccac --- /dev/null +++ b/utils/dump_env.lua @@ -0,0 +1,32 @@ +-- A small debugging tool that writes the contents of _ENV to a file when the game loads. +-- Useful for ensuring you get the same information when loading +-- the reference and desync levels in desync reports. +-- dependencies +local table = require 'utils.table' +local Event = require 'utils.event' + +-- localized functions +local inspect = table.inspect + +-- local constants +local filename = 'env_dump.lua' + +-- Removes metatables and the package table +local filter = function(item, path) + if path[#path] ~= inspect.METATABLE and item ~= 'package' then + return item + end +end + +local function player_joined(event) + local dump_string = inspect(_ENV, {process = filter}) + if dump_string then + local s = string.format('tick on join: %s\n%s', event.tick, dump_string) + game.write_file(filename, s) + game.print('_ENV dumped into ' .. filename) + else + game.print('_ENV not dumped, dump_string was nil') + end +end + +Event.add(defines.events.on_player_joined_game, player_joined) diff --git a/utils/game.lua b/utils/game.lua new file mode 100644 index 00000000..7a151c17 --- /dev/null +++ b/utils/game.lua @@ -0,0 +1,120 @@ +local Global = require 'utils.global' +local Color = require 'resources.color_presets' +local pairs = pairs + +local Game = {} + +local bad_name_players = {} +Global.register( + bad_name_players, + function(tbl) + bad_name_players = tbl + end +) + +--[[ + Due to a bug in the Factorio api the following expression isn't guaranteed to be true. + game.players[player.index] == player + get_player_by_index(index) will always return the correct player. + When looking up players by name or iterating through all players use game.players instead. +]] +function Game.get_player_by_index(index) + local p = game.players[index] + + if not p then + return nil + end + if p.index == index then + return p + end + + p = bad_name_players[index] + if p then + if p.valid then + return p + else + return nil + end + end + + for k, v in pairs(game.players) do + if k == index then + bad_name_players[index] = v + return v + end + end +end + +--- Returns a valid LuaPlayer if given a number, string, or LuaPlayer. Returns nil otherwise. +-- obj +function Game.get_player_from_any(obj) + local o_type = type(obj) + local p + if type == 'number' then + p = Game.get_player_by_index(obj) + elseif o_type == 'string' then + p = game.players[obj] + elseif o_type == 'table' and obj.valid and obj.is_player() then + return obj + end + + if p and p.valid then + return p + end +end + +--- Prints to player or console. +-- @param str table if locale is used +-- @param color
defaults to white +function Game.player_print(str, color) + color = color or Color.white + if game.player then + game.player.print(str, color) + else + print(str) + end +end + +--[[ + @param Position String to display at + @param text String to display + @param color table in {r = 0~1, g = 0~1, b = 0~1}, defaults to white. + @param surface LuaSurface + + @return the created entity +]] +function Game.print_floating_text(surface, position, text, color) + color = color or Color.white + + return surface.create_entity { + name = 'tutorial-flying-text', + color = color, + text = text, + position = position + } +end + +--[[ + Creates a floating text entity at the player location with the specified color in {r, g, b} format. + Example: "+10 iron" or "-10 coins" + + @param text String to display + @param color table in {r = 0~1, g = 0~1, b = 0~1}, defaults to white. + + @return the created entity +]] +function Game.print_player_floating_text_position(player_index, text, color, x_offset, y_offset) + local player = Game.get_player_by_index(player_index) + if not player or not player.valid then + return + end + + local position = player.position + return Game.print_floating_text(player.surface, {x = position.x + x_offset, y = position.y + y_offset}, text, color) +end + +function Game.print_player_floating_text(player_index, text, color) + Game.print_player_floating_text_position(player_index, text, color, 0, -1.5) +end + +return Game diff --git a/utils/gui.lua b/utils/gui.lua new file mode 100644 index 00000000..7ff901aa --- /dev/null +++ b/utils/gui.lua @@ -0,0 +1,287 @@ +local Token = require 'utils.token' +local Event = require 'utils.event' +local Game = require 'utils.game' +local Global = require 'utils.global' + +local Gui = {} + +local data = {} + +Global.register( + data, + function(tbl) + data = tbl + end +) + +local top_elements = {} +local on_visible_handlers = {} +local on_pre_hidden_handlers = {} + +function Gui.uid_name() + return tostring(Token.uid()) +end + +-- Associates data with the LuaGuiElement. If data is nil then removes the data +function Gui.set_data(element, value) + data[element.player_index * 0x100000000 + element.index] = value +end + +-- Gets the Associated data with this LuaGuiElement if any. +function Gui.get_data(element) + return data[element.player_index * 0x100000000 + element.index] +end + +-- Removes data associated with LuaGuiElement and its children recursively. +function Gui.remove_data_recursively(element) + Gui.set_data(element, nil) + + local children = element.children + + if not children then + return + end + + for _, child in ipairs(children) do + if child.valid then + Gui.remove_data_recursively(child) + end + end +end + +function Gui.remove_children_data(element) + local children = element.children + + if not children then + return + end + + for _, child in ipairs(children) do + if child.valid then + Gui.set_data(child, nil) + Gui.remove_children_data(child) + end + end +end + +function Gui.destroy(element) + Gui.remove_data_recursively(element) + element.destroy() +end + +function Gui.clear(element) + Gui.remove_children_data(element) + element.clear() +end + +local function handler_factory(event_id) + local handlers + + local function on_event(event) + local element = event.element + if not element or not element.valid then + return + end + + local handler = handlers[element.name] + if not handler then + return + end + + local player = Game.get_player_by_index(event.player_index) + if not player or not player.valid then + return + end + event.player = player + + handler(event) + end + + return function(element_name, handler) + if not handlers then + handlers = {} + Event.add(event_id, on_event) + end + + handlers[element_name] = handler + end +end + +local function custom_handler_factory(handlers) + return function(element_name, handler) + handlers[element_name] = handler + end +end + +local function custom_raise(handlers, element, player) + local handler = handlers[element.name] + if not handler then + return + end + + handler({element = element, player = player}) +end + +-- Register a handler for the on_gui_checked_state_changed event for LuaGuiElements with element_name. +-- Can only have one handler per element name. +-- Guarantees that the element and the player are valid when calling the handler. +-- Adds a player field to the event table. +Gui.on_checked_state_changed = handler_factory(defines.events.on_gui_checked_state_changed) + +-- Register a handler for the on_gui_click event for LuaGuiElements with element_name. +-- Can only have one handler per element name. +-- Guarantees that the element and the player are valid when calling the handler. +-- Adds a player field to the event table. +Gui.on_click = handler_factory(defines.events.on_gui_click) + +-- Register a handler for the on_gui_closed event for a custom LuaGuiElements with element_name. +-- Can only have one handler per element name. +-- Guarantees that the element and the player are valid when calling the handler. +-- Adds a player field to the event table. +Gui.on_custom_close = handler_factory(defines.events.on_gui_closed) + +-- Register a handler for the on_gui_elem_changed event for LuaGuiElements with element_name. +-- Can only have one handler per element name. +-- Guarantees that the element and the player are valid when calling the handler. +-- Adds a player field to the event table. +Gui.on_elem_changed = handler_factory(defines.events.on_gui_elem_changed) + +-- Register a handler for the on_gui_selection_state_changed event for LuaGuiElements with element_name. +-- Can only have one handler per element name. +-- Guarantees that the element and the player are valid when calling the handler. +-- Adds a player field to the event table. +Gui.on_selection_state_changed = handler_factory(defines.events.on_gui_selection_state_changed) + +-- Register a handler for the on_gui_text_changed event for LuaGuiElements with element_name. +-- Can only have one handler per element name. +-- Guarantees that the element and the player are valid when calling the handler. +-- Adds a player field to the event table. +Gui.on_text_changed = handler_factory(defines.events.on_gui_text_changed) + +-- Register a handler for the on_gui_value_changed event for LuaGuiElements with element_name. +-- Can only have one handler per element name. +-- Guarantees that the element and the player are valid when calling the handler. +-- Adds a player field to the event table. +Gui.on_value_changed = handler_factory(defines.events.on_gui_value_changed) + +-- Register a handler for when the player shows the top LuaGuiElements with element_name. +-- Assuming the element_name has been added with Gui.allow_player_to_toggle_top_element_visibility. +-- Can only have one handler per element name. +-- Guarantees that the element and the player are valid when calling the handler. +-- Adds a player field to the event table. +Gui.on_player_show_top = custom_handler_factory(on_visible_handlers) + +-- Register a handler for when the player hides the top LuaGuiElements with element_name. +-- Assuming the element_name has been added with Gui.allow_player_to_toggle_top_element_visibility. +-- Can only have one handler per element name. +-- Guarantees that the element and the player are valid when calling the handler. +-- Adds a player field to the event table. +Gui.on_pre_player_hide_top = custom_handler_factory(on_pre_hidden_handlers) + +--- Allows the player to show / hide this element. +-- The element must be part in gui.top. +-- This function must be called in the control stage, i.e not inside an event. +-- @param element_name This name must be globally unique. +function Gui.allow_player_to_toggle_top_element_visibility(element_name) + if _LIFECYCLE ~= _STAGE.control then + error('can only be called during the control stage', 2) + end + top_elements[#top_elements + 1] = element_name +end + +local toggle_button_name = Gui.uid_name() + +Event.add( + defines.events.on_player_created, + function(event) + local player = Game.get_player_by_index(event.player_index) + + if not player or not player.valid then + return + end + + local b = + player.gui.top.add { + type = 'button', + name = toggle_button_name, + caption = '<', + tooltip = 'Shows / hides the Redmew Gui buttons.' + } + local style = b.style + style.width = 18 + style.height = 38 + style.left_padding = 0 + style.top_padding = 0 + style.right_padding = 0 + style.bottom_padding = 0 + style.font = 'default-small-bold' + end +) + +Gui.on_click( + toggle_button_name, + function(event) + local button = event.element + local player = event.player + local top = player.gui.top + + if button.caption == '<' then + for i = 1, #top_elements do + local name = top_elements[i] + local ele = top[name] + if ele and ele.valid then + local style = ele.style + + -- if visible is not set it has the value of nil. + -- Hence nil is treated as is visible. + local v = style.visible + if v or v == nil then + custom_raise(on_pre_hidden_handlers, ele, player) + style.visible = false + end + end + end + + button.caption = '>' + button.style.height = 24 + else + for i = 1, #top_elements do + local name = top_elements[i] + local ele = top[name] + if ele and ele.valid then + local style = ele.style + + if not style.visible then + style.visible = true + custom_raise(on_visible_handlers, ele, player) + end + end + end + + button.caption = '<' + button.style.height = 38 + end + end +) + +if _DEBUG then + local concat = table.concat + + local names = {} + Gui.names = names + + function Gui.uid_name() + local info = debug.getinfo(2, 'Sl') + local filepath = info.source:match('^.+/currently%-playing/(.+)$'):sub(1, -5) + local line = info.currentline + + local token = tostring(Token.uid()) + + local name = concat {token, ' - ', filepath, ':line:', line} + names[token] = name + + return token + end +end + +return Gui diff --git a/utils/inspect.lua b/utils/inspect.lua new file mode 100644 index 00000000..c13a8cd4 --- /dev/null +++ b/utils/inspect.lua @@ -0,0 +1,342 @@ +local inspect ={ + _VERSION = 'inspect.lua 3.1.0', + _URL = 'http://github.com/kikito/inspect.lua', + _DESCRIPTION = 'human-readable representations of tables', + _LICENSE = [[ + MIT LICENSE + + Copyright (c) 2013 Enrique García Cota + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ]] +} + +local tostring = tostring + +inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end}) +inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end}) + +-- Apostrophizes the string if it has quotes, but not aphostrophes +-- Otherwise, it returns a regular quoted string +local function smartQuote(str) + if str:match('"') and not str:match("'") then + return "'" .. str .. "'" + end + return '"' .. str:gsub('"', '\\"') .. '"' +end + +-- \a => '\\a', \0 => '\\0', 31 => '\31' +local shortControlCharEscapes = { + ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", + ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v" +} +local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031 +for i=0, 31 do + local ch = string.char(i) + if not shortControlCharEscapes[ch] then + shortControlCharEscapes[ch] = "\\"..i + longControlCharEscapes[ch] = string.format("\\%03d", i) + end +end + +local function escape(str) + return (str:gsub("\\", "\\\\") + :gsub("(%c)%f[0-9]", longControlCharEscapes) + :gsub("%c", shortControlCharEscapes)) +end + +local function isIdentifier(str) + return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" ) +end + +local function isSequenceKey(k, sequenceLength) + return type(k) == 'number' + and 1 <= k + and k <= sequenceLength + and math.floor(k) == k +end + +local defaultTypeOrders = { + ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4, + ['function'] = 5, ['userdata'] = 6, ['thread'] = 7 +} + +local function sortKeys(a, b) + local ta, tb = type(a), type(b) + + -- strings and numbers are sorted numerically/alphabetically + if ta == tb and (ta == 'string' or ta == 'number') then return a < b end + + local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb] + -- Two default types are compared according to the defaultTypeOrders table + if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb] + elseif dta then return true -- default types before custom ones + elseif dtb then return false -- custom types after default ones + end + + -- custom types are sorted out alphabetically + return ta < tb +end + +-- For implementation reasons, the behavior of rawlen & # is "undefined" when +-- tables aren't pure sequences. So we implement our own # operator. +local function getSequenceLength(t) + local len = 1 + local v = rawget(t,len) + while v ~= nil do + len = len + 1 + v = rawget(t,len) + end + return len - 1 +end + +local function getNonSequentialKeys(t) + local keys = {} + local sequenceLength = getSequenceLength(t) + for k,_ in pairs(t) do + if not isSequenceKey(k, sequenceLength) then table.insert(keys, k) end + end + table.sort(keys, sortKeys) + return keys, sequenceLength +end + +local function getToStringResultSafely(t, mt) + local __tostring = type(mt) == 'table' and rawget(mt, '__tostring') + local str, ok + if type(__tostring) == 'function' then + ok, str = pcall(__tostring, t) + str = ok and str or 'error: ' .. tostring(str) + end + if type(str) == 'string' and #str > 0 then return str end +end + +local function countTableAppearances(t, tableAppearances) + tableAppearances = tableAppearances or {} + + if type(t) == 'table' then + if not tableAppearances[t] then + tableAppearances[t] = 1 + for k,v in pairs(t) do + countTableAppearances(k, tableAppearances) + countTableAppearances(v, tableAppearances) + end + countTableAppearances(getmetatable(t), tableAppearances) + else + tableAppearances[t] = tableAppearances[t] + 1 + end + end + + return tableAppearances +end + +local copySequence = function(s) + local copy, len = {}, #s + for i=1, len do copy[i] = s[i] end + return copy, len +end + +local function makePath(path, ...) + local keys = {...} + local newPath, len = copySequence(path) + for i=1, #keys do + newPath[len + i] = keys[i] + end + return newPath +end + +local function processRecursive(process, item, path, visited) + + if item == nil then return nil end + if visited[item] then return visited[item] end + + local processed = process(item, path) + if type(processed) == 'table' then + local processedCopy = {} + visited[item] = processedCopy + local processedKey + + for k,v in pairs(processed) do + processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited) + if processedKey ~= nil then + processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited) + end + end + + local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) + setmetatable(processedCopy, mt) + processed = processedCopy + end + return processed +end + + + +------------------------------------------------------------------- + +local Inspector = {} +local Inspector_mt = {__index = Inspector} + +function Inspector:puts(...) + local args = {...} + local buffer = self.buffer + local len = #buffer + for i=1, #args do + len = len + 1 + buffer[len] = args[i] + end +end + +function Inspector:down(f) + self.level = self.level + 1 + f() + self.level = self.level - 1 +end + +function Inspector:tabify() + self:puts(self.newline, string.rep(self.indent, self.level)) +end + +function Inspector:alreadyVisited(v) + return self.ids[v] ~= nil +end + +function Inspector:getId(v) + local id = self.ids[v] + if not id then + local tv = type(v) + id = (self.maxIds[tv] or 0) + 1 + self.maxIds[tv] = id + self.ids[v] = id + end + return tostring(id) +end + +function Inspector:putKey(k) + if isIdentifier(k) then return self:puts(k) end + self:puts("[") + self:putValue(k) + self:puts("]") +end + +function Inspector:putTable(t) + if t == inspect.KEY or t == inspect.METATABLE then + self:puts(tostring(t)) + elseif self:alreadyVisited(t) then + self:puts('
') + elseif self.level >= self.depth then + self:puts('{...}') + else + if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end + + local nonSequentialKeys, sequenceLength = getNonSequentialKeys(t) + local mt = getmetatable(t) + local toStringResult = getToStringResultSafely(t, mt) + + self:puts('{') + self:down(function() + if toStringResult then + self:puts(' -- ', escape(toStringResult)) + if sequenceLength >= 1 then self:tabify() end + end + + local count = 0 + for i=1, sequenceLength do + if count > 0 then self:puts(',') end + self:puts(' ') + self:putValue(t[i]) + count = count + 1 + end + + for _,k in ipairs(nonSequentialKeys) do + if count > 0 then self:puts(',') end + self:tabify() + self:putKey(k) + self:puts(' = ') + self:putValue(t[k]) + count = count + 1 + end + + if mt then + if count > 0 then self:puts(',') end + self:tabify() + self:puts(' = ') + self:putValue(mt) + end + end) + + if #nonSequentialKeys > 0 or mt then -- result is multi-lined. Justify closing } + self:tabify() + elseif sequenceLength > 0 then -- array tables have one extra space before closing } + self:puts(' ') + end + + self:puts('}') + end +end + +function Inspector:putValue(v) + local tv = type(v) + + if tv == 'string' then + self:puts(smartQuote(escape(v))) + elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or + tv == 'cdata' or tv == 'ctype' then + self:puts(tostring(v)) + elseif tv == 'table' then + self:putTable(v) + else + self:puts('<',tv,' ',self:getId(v),'>') + end +end + +------------------------------------------------------------------- + +function inspect.inspect(root, options) + options = options or {} + + local depth = options.depth or math.huge + local newline = options.newline or '\n' + local indent = options.indent or ' ' + local process = options.process + + if process then + root = processRecursive(process, root, {}, {}) + end + + local inspector = setmetatable({ + depth = depth, + level = 0, + buffer = {}, + ids = {}, + maxIds = {}, + newline = newline, + indent = indent, + tableAppearances = countTableAppearances(root) + }, Inspector_mt) + + inspector:putValue(root) + + return table.concat(inspector.buffer) +end + +setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end }) + +return inspect + diff --git a/utils/math.lua b/utils/math.lua new file mode 100644 index 00000000..3a4e3652 --- /dev/null +++ b/utils/math.lua @@ -0,0 +1,54 @@ +--luacheck:ignore global math +local _sin = math.sin +local _cos = math.cos + +math.sqrt2 = math.sqrt(2) +math.inv_sqrt2 = 1 / math.sqrt2 +math.tau = 2 * math.pi + +math.sin = function(x) + return math.floor(_sin(x) * 10000000 + 0.5) / 10000000 +end + +math.cos = function(x) + return math.floor(_cos(x) * 10000000 + 0.5) / 10000000 +end + +-- rounds number (num) to certain number of decimal places (idp) +math.round = function(num, idp) + local mult = 10 ^ (idp or 0) + return math.floor(num * mult + 0.5) / mult +end + +math.clamp = function(num, min, max) + if num < min then + return min + elseif num > max then + return max + else + return num + end +end + +--- Takes two points and calculates the slope of a line +-- @param x1, y1 numbers - cordinates of a point on a line +-- @param x2, y2 numbers - cordinates of a point on a line +-- @return number - the slope of the line +math.calculate_slope = function(x1, y1, x2, y2) + return math.abs((y2 - y1) / (x2 - x1)) +end + +--- Calculates the y-intercept of a line +-- @param x, y numbers - coordinates of point on line +-- @param slope number - the slope of a line +-- @return number - the y-intercept of a line +math.calculate_y_intercept = function(x, y, slope) + return y - (slope * x) +end + +local deg_to_rad = math.tau / 360 +math.degrees = function(angle) + return angle * deg_to_rad +end + +return math diff --git a/utils/player_rewards.lua b/utils/player_rewards.lua new file mode 100644 index 00000000..69da941c --- /dev/null +++ b/utils/player_rewards.lua @@ -0,0 +1,154 @@ +local Global = require 'utils.global' +local Game = require 'utils.game' +local PlayerStats = require 'features.player_stats' +local Command = require 'utils.command' +local Ranks = require 'resources.ranks' + +local format = string.format +local abs = math.abs +local concat = table.concat + +local Public = {} +local reward_token = {global.config.player_rewards.token} or {global.config.market.currency} or {'coin'} + +Global.register( + { + reward_token = reward_token + }, + function(tbl) + reward_token = tbl.reward_token + end +) + +--- Returns the single or plural form of the token name +local function get_token_plural(quantity) + if quantity and quantity > 1 then + return concat({reward_token[1], 's'}) + else + return reward_token[1] + end +end + +--- Set the item to use for rewards +-- @param reward string - item name to use as reward +-- @return boolean true - indicating success +Public.set_reward = function(reward) + if global.config.player_rewards.enabled == false then + return false + end + + reward_token[1] = reward + return true +end + +--- Returns the name of the reward item +Public.get_reward = function() + return reward_token[1] +end + +--- Gives reward tokens to the player +-- @param player +-- @param amount of reward tokens +-- @param message an optional message to send to the affected player +-- @return indicating how many were inserted or if operation failed +Public.give_reward = function(player, amount, message) + if global.config.player_rewards.enabled == false then + return 0 + end + + local player_index + if type(player) == 'number' then + player_index = player + player = Game.get_player_by_index(player) + else + player_index = player.index + end + local reward = {name = reward_token[1], count = amount} + if not player.can_insert(reward) then + return 0 + end + if message then + player.print(message) + end + local coin_difference = player.insert(reward) + if reward_token[1] == 'coin' then + PlayerStats.change_coin_earned(player_index, coin_difference) + end + return coin_difference +end + +--- Removes reward tokens from the player +-- @param player +-- @param amount of reward tokens +-- @param message an optional message to send to the affected player +-- @return indicating how many were removed or if operation failed +Public.remove_reward = function(player, amount, message) + if global.config.player_rewards.enabled == false then + return 0 + end + + local player_index + if type(player) == 'number' then + player_index = player + player = Game.get_player_by_index(player) + else + player_index = player.index + end + local unreward = {name = reward_token[1], count = amount} + if message then + player.print(message) + end + local coin_difference = player.remove_item(unreward) + if reward_token[1] == 'coin' then + PlayerStats.change_coin_earned(player_index, -coin_difference) + end + return coin_difference +end + +Command.add( + 'reward', + { + description = 'Gives a reward to a target player (removes if quantity is negative)', + arguments = {'target', 'quantity', 'reason'}, + default_values = {reason = false}, + required_rank = Ranks.admin, + capture_excess_arguments = true, + allowed_by_server = true, + allowed_by_player = true + }, + function(args, player) + local player_name = 'server' + if player then + player_name = player.name + end + + local target_name = args.target + local target = game.players[target_name] + if not target then + player.print('Target not found.') + return + end + + local quantity = tonumber(args.quantity) + if quantity > 0 then + Public.give_reward(target, quantity) + local string = format('%s has rewarded %s with %s %s', player_name, target_name, quantity, get_token_plural(quantity)) + if args.reason then + string = format('%s for %s', string, args.reason) + end + game.print(string) + elseif quantity < 0 then + quantity = abs(quantity) + Public.remove_reward(target, quantity) + local string = format('%s has punished %s by taking %s %s', player_name, target_name, quantity, get_token_plural(quantity)) + if args.reason then + string = format('%s for %s', string, args.reason) + end + game.print(string) + else + Game.player_print(" A reward of 0 is neither a reward nor a punishment, it's just dumb. Try harder.") + end + end +) + +return Public diff --git a/utils/print_override.lua b/utils/print_override.lua new file mode 100644 index 00000000..848c91b2 --- /dev/null +++ b/utils/print_override.lua @@ -0,0 +1,13 @@ +local Public = {} + +local locale_string = {'', '[PRINT] ', nil} +local raw_print = print + +function print(str) + locale_string[3] = str + log(locale_string) +end + +Public.raw_print = raw_print + +return Public diff --git a/utils/priority_queue.lua b/utils/priority_queue.lua new file mode 100644 index 00000000..bccd2358 --- /dev/null +++ b/utils/priority_queue.lua @@ -0,0 +1,74 @@ +local PriorityQueue = {} + +function PriorityQueue.new() + return {} +end + +local function default_comp(a, b) + return a < b +end + +local function HeapifyFromEndToStart(queue, comp) + comp = comp or default_comp + local pos = #queue + while pos > 1 do + local parent = bit32.rshift(pos, 1) -- integer division by 2 + if comp(queue[pos], queue[parent]) then + queue[pos], queue[parent] = queue[parent], queue[pos] + pos = parent + else + break + end + end +end + +local function HeapifyFromStartToEnd(queue, comp) + comp = comp or default_comp + local parent = 1 + local smallest = 1 + while true do + local child = parent * 2 + if child > #queue then + break + end + if comp(queue[child], queue[parent]) then + smallest = child + end + child = child + 1 + if child <= #queue and comp(queue[child], queue[smallest]) then + smallest = child + end + + if parent ~= smallest then + queue[parent], queue[smallest] = queue[smallest], queue[parent] + parent = smallest + else + break + end + end +end + +function PriorityQueue.size(queue) + return #queue +end + +function PriorityQueue.push(queue, element, comp) + table.insert(queue, element) + HeapifyFromEndToStart(queue, comp) +end + +function PriorityQueue.pop(queue, comp) + local element = queue[1] + + queue[1] = queue[#queue] + queue[#queue] = nil + HeapifyFromStartToEnd(queue, comp) + + return element +end + +function PriorityQueue.peek(queue) + return queue[1] +end + +return PriorityQueue diff --git a/utils/queue.lua b/utils/queue.lua new file mode 100644 index 00000000..1907c30b --- /dev/null +++ b/utils/queue.lua @@ -0,0 +1,34 @@ +local Queue = {} + +function Queue.new() + local queue = {_head = 0, _tail = 0} + return queue +end + +function Queue.size(queue) + return queue._tail - queue._head +end + +function Queue.push(queue, element) + local index = queue._head + queue[index] = element + queue._head = index - 1 +end + +function Queue.peek(queue) + return queue[queue._tail] +end + +function Queue.pop(queue) + local index = queue._tail + + local element = queue[index] + queue[index] = nil + + if element then + queue._tail = index - 1 + end + return element +end + +return Queue diff --git a/utils/recipe_locker.lua b/utils/recipe_locker.lua new file mode 100644 index 00000000..ce754b5b --- /dev/null +++ b/utils/recipe_locker.lua @@ -0,0 +1,58 @@ +-- A module to prevent recipes from being unlocked by research. Accessed via the public functions. +local Event = require 'utils.event' +local Global = require 'utils.global' + +local Public = {} + +local recipes = {} + +Global.register( + { + recipes = recipes + }, + function(tbl) + recipes = tbl.recipes + end +) + +Event.add( + defines.events.on_research_finished, + function(event) + local p_force = game.forces.player + local r = event.research + for _, effect in pairs(r.effects) do + local recipe = effect.recipe + if recipe and recipes[recipe] then + p_force.recipes[recipe].enabled = false + end + end + end +) + +Event.on_init( + function() + for recipe in pairs(recipes) do + game.forces.player.recipes[recipe].enabled = false + end + end +) + +--- Locks recipes, preventing them from being enabled by research. +-- Does not check if they should be enabled/disabled by existing research. +-- @param tbl
an array of recipe strings +function Public.lock_recipes(tbl) + for i = 1, #tbl do + recipes[tbl[i]] = true + end +end + +--- Unlocks recipes, allowing them to be enabled by research. +-- Does not check if they should be enabled/disabled by existing research. +-- @param tbl
an array of recipe strings +function Public.unlock_recipes(tbl) + for i = 1, #tbl do + recipes[tbl[i]] = nil + end +end + +return Public diff --git a/utils/redmew_settings.lua b/utils/redmew_settings.lua new file mode 100644 index 00000000..9ddc7971 --- /dev/null +++ b/utils/redmew_settings.lua @@ -0,0 +1,178 @@ +local Global = require 'utils.global' +local type = type +local error = error +local tonumber = tonumber +local tostring = tostring +local pairs = pairs +local format = string.format + +--- Contains a set of callables that will attempt to sanitize and transform the input +local settings_type = { + fraction = function (input) + input = tonumber(input) + + if input == nil then + return false, 'fraction setting type requires the input to be a valid number between 0 and 1.' + end + + if input < 0 then + input = 0 + end + + if input > 1 then + input = 1 + end + + return true, input + end, + string = function (input) + if input == nil then + return true, '' + end + + local input_type = type(input) + if input_type == 'string' then + return true, input + end + + if input_type == 'number' or input_type == 'boolean' then + return true, tostring(input) + end + + return false, 'string setting type requires the input to be either a valid string or something that can be converted to a string.' + end, + boolean = function (input) + local input_type = type(input) + + if input_type == 'boolean' then + return true, input + end + + if input_type == 'string' then + if input == '0' or input == '' or input == 'false' or input == 'no' then + return true, false + end + if input == '1' or input == 'true' or input == 'yes' then + return true, true + end + + return true, tonumber(input) ~= nil + end + + if input_type == 'number' then + return true, input ~= 0 + end + + return false, 'boolean setting type requires the input to be either a boolean, number or string that can be transformed to a boolean.' + end, +} + +local settings = {} +local memory = {} + +Global.register(memory, function (tbl) memory = tbl end) + +local Public = {} + +Public.types = {fraction = 'fraction', string = 'string', boolean = 'boolean'} + +---Register a specific setting with a sensitization setting type. +--- +--- Available setting types: +--- - fraction (number between 0 and 1) in either number or string form +--- - string a string or anything that can be cast to a string +--- - boolean, 1, 0, yes, no, true, false or an empty string for false +--- +--- This function must be called in the control stage, i.e. not inside an event. +--- +---@param name string +---@param setting_type string +---@param default mixed +function Public.register(name, setting_type, default) + if _LIFECYCLE ~= _STAGE.control then + error(format('You can only register setting names in the control stage, i.e. not inside events. Tried setting "%s" with type "%s".', name, setting_type), 2) + end + + if settings[name] then + error(format('Trying to register setting for "%s" while it has already been registered.', name), 2) + end + + local callback = settings_type[setting_type] + if not callback then + error(format('Trying to register setting for "%s" with type "%s" while this type does not exist.', name, setting_type), 2) + end + + local setting = { + default = default, + callback = callback, + } + + settings[name] = setting + + return setting +end + +---Sets a setting to a specific value for a player. +--- +---In order to get a setting value, it has to be registered via the "register" function. +--- +---@param player_index number +---@param name string +---@param value mixed +function Public.set(player_index, name, value) + local setting = settings[name] + if not setting then + return error(format('Setting "%s" does not exist.', name), 2) + end + + local success, sanitized_value = setting.callback(value) + + if not success then + error(format('Setting "%s" failed: %s', name, sanitized_value), 2) + end + + local player_settings = memory[player_index] + if not player_settings then + player_settings = {} + memory[player_index] = player_settings + end + + player_settings[name] = sanitized_value + + return sanitized_value +end + +---Returns the value of a setting for this player. +--- +---In order to set a setting value, it has to be registered via the "register" function. +--- +---@param player_index number +---@param name string +function Public.get(player_index, name) + local setting = settings[name] + if not setting then + return error(format('Setting "%s" does not exist.', name), 2) + end + + local player_settings = memory[player_index] + if not player_settings then + return setting.default + end + + local player_setting = player_settings[name] + return player_setting ~= nil and player_setting or setting.default +end + +---Returns a table of all settings for a given player in a key => value setup +---@param player_index number +function Public.all(player_index) + local player_settings = memory[player_index] or {} + local output = {} + for name, data in pairs(settings) do + output[name] = player_settings[name] or data.default + end + + return output +end + +return Public diff --git a/utils/state_machine.lua b/utils/state_machine.lua new file mode 100644 index 00000000..a3295caf --- /dev/null +++ b/utils/state_machine.lua @@ -0,0 +1,119 @@ +--- This module provides a classical mealy/moore state machine. +-- Each machine in constructed by calling new() +-- States and Transitions are lazily added to the machine as transition handlers and state tick handlers are registered. +-- However the state machine must be fully defined after init is done. Dynamic machine changes are currently unsupported +-- An example usage can be found here: map_gen\combined\tetris\control.lua + +local Module = {} + +local Debug = require 'utils.debug' + +local in_state_callbacks = {} +local transaction_callbacks = {} +local max_stack_depth = 20 +local machine_count = 0 +local control_stage = _STAGE.control + +--- Transitions the supplied machine into a given state and executes all transaction_callbacks +-- @param self StateMachine +-- @param new_state number/string The new state to transition to +function Module.transition(self, new_state) + Debug.print(string.format('Transitioning from state %d to state %d.', self.state, new_state)) + local old_state = self.state + + local stack_depth = self.stack_depth + self.stack_depth = stack_depth + 1 + if stack_depth > max_stack_depth then + if _DEBUG then + error('[WARNING] Stack overflow at:' .. debug.traceback()) + else + log('[WARNING] Stack overflow at:' .. debug.traceback()) + end + end + + local exit_callbacks = transaction_callbacks[self.id][old_state] + if exit_callbacks then + local entry_callbacks = exit_callbacks[new_state] + if entry_callbacks then + for i = 1, #entry_callbacks do + local callback = entry_callbacks[i] + if callback then + callback() + end + end + end + end + self.state = new_state +end + +--- Is this machine in this state? +-- @param self StateMachine +-- @param state number/string +-- @return boolean +function Module.in_state(self, state) + return self.state == state +end + +--- Invoke a machine tick. Will execute all in_state_callbacks of the given machine +-- @param self StateMachine the machine, whose handlers will be invoked +function Module.machine_tick(self) + local callbacks = in_state_callbacks[self.id][self.state] + if callbacks then + for i=1, #callbacks do + local callback = callbacks[i] + if callback then + callback() + end + end + end + self.stack_depth = 0 +end + +--- Register a handler that will be invoked by StateMachine.machine_tick +-- You may register multiple handlers for the same transition +-- NOTICE: This function will invoke an error if called after init. Dynamic machine changes are currently unsupported +-- @param self StateMachine the machine +-- @param state number/string The state, that the machine will be in, when callback is invoked +-- @param callback function +function Module.register_state_tick_callback(self, state, callback) + if _LIFECYCLE ~= control_stage then + error('Calling StateMachine.register_state_tick_callback after the control stage is unsupported due to desyncs.', 2) + end + in_state_callbacks[self.id][state] = in_state_callbacks[self.id][state] or {} + table.insert(in_state_callbacks[self.id][state], callback) +end + +--- Register a handler that will be invoked by StateMachine.transition +-- You may register multiple handlers for the same transition +-- NOTICE: This function will invoke an error if called after init. Dynamic machine changes are currently unsupported +-- @param self StateMachine the machine +-- @param state number/string exiting state +-- @param state number/string entering state +-- @param callback function +function Module.register_transition_callback(self, old, new, callback) + if _LIFECYCLE ~= control_stage then + error('Calling StateMachine.register_transition_callback after the control stage is unsupported due to desyncs.', 2) + end + transaction_callbacks[self.id][old] = transaction_callbacks[self.id][old] or {} + transaction_callbacks[self.id][old][new] = transaction_callbacks[self.id][old][new] or {} + table.insert(transaction_callbacks[self.id][old][new], callback) +end + +--- Constructs a new state machine +-- @param init_state number/string The starting state of the machine +-- @return StateMachine The constructed state machine object +function Module.new(init_state) + if _LIFECYCLE ~= control_stage then + error('Calling StateMachine.new after the control stage is unsupported due to desyncs.', 2) + end + machine_count = machine_count + 1 + in_state_callbacks[machine_count] = {} + transaction_callbacks[machine_count] = {} + return { + state = init_state, + stack_depth = 0, + id = machine_count, + } +end + +return Module diff --git a/utils/table.lua b/utils/table.lua new file mode 100644 index 00000000..b828f8a6 --- /dev/null +++ b/utils/table.lua @@ -0,0 +1,265 @@ +--luacheck:ignore global table +local random = math.random +local floor = math.floor +local remove = table.remove +local tonumber = tonumber +local pairs = pairs +local table_size = table_size + +--- Searches a table to remove a specific element without an index +-- @param t
to search +-- @param table element to search for +function table.remove_element(t, element) + for k, v in pairs(t) do + if v == element then + remove(t, k) + break + end + end +end + +--- Removes an item from an array in O(1) time. +-- The catch is that fast_remove doesn't guarantee to maintain the order of items in the array. +-- @param tbl
arrayed table +-- @param index Must be >= 0. The case where index > #tbl is handled. +function table.fast_remove(tbl, index) + local count = #tbl + if index > count then + return + elseif index < count then + tbl[index] = tbl[count] + end + + tbl[count] = nil +end + +--- Adds the contents of table t2 to table t1 +-- @param t1
to insert into +-- @param t2
to insert from +function table.add_all(t1, t2) + for k, v in pairs(t2) do + if tonumber(k) then + t1[#t1 + 1] = v + else + t1[k] = v + end + end +end + +--- Checks if a table contains an element +-- @param t
+-- @param e table element +-- @returns the index of the element or nil +function table.index_of(t, e) + for k, v in pairs(t) do + if v == e then + return k + end + end + return nil +end + +--- Checks if the arrayed portion of a table contains an element +-- @param t
+-- @param e table element +-- @returns the index of the element or nil +function table.index_of_in_array(t, e) + for i = 1, #t do + if t[i] == e then + return i + end + end + return nil +end + +local index_of = table.index_of +--- Checks if a table contains an element +-- @param t
+-- @param e table element +-- @returns indicating success +function table.contains(t, e) + return index_of(t, e) and true or false +end + +local index_of_in_array = table.index_of_in_array +--- Checks if the arrayed portion of a table contains an element +-- @param t
+-- @param e table element +-- @returns indicating success +function table.array_contains(t, e) + return index_of_in_array(t, e) and true or false +end + +--- Adds an element into a specific index position while shuffling the rest down +-- @param t
to add into +-- @param index the position in the table to add to +-- @param element to add to the table +function table.set(t, index, element) + local i = 1 + for k in pairs(t) do + if i == index then + t[k] = element + return nil + end + i = i + 1 + end + error('Index out of bounds', 2) +end + +--- Chooses a random entry from a table +-- because this uses math.random, it cannot be used outside of events +-- @param t
+-- @param key to indicate whether to return the key or value +-- @return a random element of table t +function table.get_random_dictionary_entry(t, key) + local target_index = random(1, table_size(t)) + local count = 1 + for k, v in pairs(t) do + if target_index == count then + if key then + return k + else + return v + end + end + count = count + 1 + end +end + +--- Chooses a random entry from a weighted table +-- because this uses math.random, it cannot be used outside of events +-- @param weight_table
of tables with items and their weights +-- @param item_index of the index of items, defaults to 1 +-- @param weight_index of the index of the weights, defaults to 2 +-- @return table element +-- @see features.chat_triggers::hodor +function table.get_random_weighted(weighted_table, item_index, weight_index) + local total_weight = 0 + item_index = item_index or 1 + weight_index = weight_index or 2 + + for _, w in pairs(weighted_table) do + total_weight = total_weight + w[weight_index] + end + + local index = random() * total_weight + local weight_sum = 0 + for _, w in pairs(weighted_table) do + weight_sum = weight_sum + w[weight_index] + if weight_sum >= index then + return w[item_index] + end + end +end + +--- Creates a fisher-yates shuffle of a sequential number-indexed table +-- because this uses math.random, it cannot be used outside of events if no rng is supplied +-- from: http://www.sdknews.com/cross-platform/corona/tutorial-how-to-shuffle-table-items +-- @param t
to shuffle +function table.shuffle_table(t, rng) + local rand = rng or math.random + local iterations = #t + if iterations == 0 then + error('Not a sequential table') + return + end + local j + + for i = iterations, 2, -1 do + j = rand(i) + t[i], t[j] = t[j], t[i] + end +end + +--- Clears all existing entries in a table +-- @param t
to clear +-- @param array to indicate whether the table is an array or not +function table.clear_table(t, array) + if array then + for i = 1, #t do + t[i] = nil + end + else + for i in pairs(t) do + t[i] = nil + end + end +end + +--[[ + Returns the index where t[index] == target. + If there is no such index, returns a negative value such that bit32.bnot(value) is + the index that the value should be inserted to keep the list ordered. + t must be a list in ascending order for the return value to be valid. + + Usage example: + local t = {1,3,5,7,9} + local x = 5 + local index = table.binary_search(t, x) + if index < 0 then + game.print("value not found, smallest index where t[index] > x is: " .. bit32.bnot(index)) + else + game.print("value found at index: " .. index) + end +]] +function table.binary_search(t, target) + --For some reason bit32.bnot doesn't return negative numbers so I'm using ~x = -1 - x instead. + + local lower = 1 + local upper = #t + + if upper == 0 then + return -2 -- ~1 + end + + repeat + local mid = floor((lower + upper) * 0.5) + local value = t[mid] + if value == target then + return mid + elseif value < target then + lower = mid + 1 + else + upper = mid - 1 + end + until lower > upper + + return -1 - lower -- ~lower +end + +-- add table-related functions that exist in base factorio/util to the 'table' table +require 'util' + +--- Similar to serpent.block, returns a string with a pretty representation of a table. +-- Notice: This method is not appropriate for saving/restoring tables. It is meant to be used by the programmer mainly while debugging a program. +-- @param table
the table to serialize +-- @param options
options are depth, newline, indent, process +-- depth sets the maximum depth that will be printed out. When the max depth is reached, inspect will stop parsing tables and just return {...} +-- process is a function which allow altering the passed object before transforming it into a string. +-- A typical way to use it would be to remove certain values so that they don't appear at all. +-- return the prettied table +table.inspect = require 'utils.inspect' + +--- Takes a table and returns the number of entries in the table. (Slower than #table, faster than iterating via pairs) +table.size = table_size + +--- Creates a deepcopy of a table. Metatables and LuaObjects inside the table are shallow copies. +-- Shallow copies meaning it copies the reference to the object instead of the object itself. +-- @param object
the object to copy +-- @return
the copied object +table.deep_copy = table.deepcopy + +--- Merges multiple tables. Tables later in the list will overwrite entries from tables earlier in the list. +-- Ex. merge({{1, 2, 3}, {[2] = 0}, {[3] = 0}}) will return {1, 0, 0} +-- @param tables
takes a table of tables to merge +-- @return
a merged table +table.merge = util.merge + +--- Determines if two tables are structurally equal. +-- Notice: tables that are LuaObjects or contain LuaObjects won't be compared correctly, use == operator for LuaObjects +-- @param tbl1
+-- @param tbl2
+-- @return +table.equals = table.compare + +return table diff --git a/utils/task.lua b/utils/task.lua new file mode 100644 index 00000000..a43c3174 --- /dev/null +++ b/utils/task.lua @@ -0,0 +1,115 @@ +-- Threading simulation module +-- Task.sleep() +-- @author Valansch and Grilledham +-- github: https://github.com/Refactorio/RedMew +-- ======================================================= -- + +local Queue = require 'utils.queue' +local PriorityQueue = require 'utils.priority_queue' +local Event = require 'utils.event' +local Token = require 'utils.token' + +local Task = {} + +global.callbacks = global.callbacks or PriorityQueue.new() +global.next_async_callback_time = -1 +global.task_queue = global.task_queue or Queue.new() +global.total_task_weight = 0 +global.task_queue_speed = 1 + +local function comp(a, b) + return a.time < b.time +end + +global.tpt = global.task_queue_speed +local function get_task_per_tick() + if game.tick % 300 == 0 then + local size = global.total_task_weight + global.tpt = math.floor(math.log10(size + 1)) * global.task_queue_speed + if global.tpt < 1 then + global.tpt = 1 + end + end + return global.tpt +end + +local function on_tick() + local queue = global.task_queue + for i = 1, get_task_per_tick() do + local task = Queue.peek(queue) + if task ~= nil then + -- result is error if not success else result is a boolean for if the task should stay in the queue. + local success, result = pcall(Token.get(task.func_token), task.params) + if not success then + if _DEBUG then + error(result) + else + log(result) + end + Queue.pop(queue) + global.total_task_weight = global.total_task_weight - task.weight + elseif not result then + Queue.pop(queue) + global.total_task_weight = global.total_task_weight - task.weight + end + end + end + + local callbacks = global.callbacks + local callback = PriorityQueue.peek(callbacks) + while callback ~= nil and game.tick >= callback.time do + local success, result = pcall(Token.get(callback.func_token), callback.params) + if not success then + if _DEBUG then + error(result) + else + log(result) + end + end + PriorityQueue.pop(callbacks, comp) + callback = PriorityQueue.peek(callbacks) + end +end + +--- Allows you to set a timer (in ticks) after which the tokened function will be run with params given as an argument +-- Cannot be called before init +-- @param ticks +-- @param func_token a token for a function store via the token system +-- @param params the argument to send to the tokened function +function Task.set_timeout_in_ticks(ticks, func_token, params) + if not game then + error('cannot call when game is not available', 2) + end + local time = game.tick + ticks + local callback = {time = time, func_token = func_token, params = params} + PriorityQueue.push(global.callbacks, callback, comp) +end + +--- Allows you to set a timer (in seconds) after which the tokened function will be run with params given as an argument +-- Cannot be called before init +-- @param sec +-- @param func_token a token for a function store via the token system +-- @param params the argument to send to the tokened function +function Task.set_timeout(sec, func_token, params) + if not game then + error('cannot call when game is not available', 2) + end + Task.set_timeout_in_ticks(60 * sec, func_token, params) +end + +--- Queueing allows you to split up heavy tasks which don't need to be completed in the same tick. +-- Queued tasks are generally run 1 per tick. If the queue backs up, more tasks will be processed per tick. +-- @param func_token a token for a function stored via the token system +-- If this function returns `true` it will run again the next tick, delaying other queued tasks (see weight) +-- @param params the argument to send to the tokened function +-- @param weight (defaults to 1) weight is the number of ticks a task is expected to take. +-- Ex. if the task is expected to repeat multiple times (ie. the function returns true and loops several ticks) +function Task.queue_task(func_token, params, weight) + weight = weight or 1 + global.total_task_weight = global.total_task_weight + weight + Queue.push(global.task_queue, {func_token = func_token, params = params, weight = weight}) +end + +Event.add(defines.events.on_tick, on_tick) + +return Task diff --git a/utils/timestamp.lua b/utils/timestamp.lua new file mode 100644 index 00000000..9af76e5d --- /dev/null +++ b/utils/timestamp.lua @@ -0,0 +1,152 @@ +--- source https://github.com/daurnimator/luatz/blob/master/luatz/timetable.lua +-- edited down to just what is needed. + +local Public = {} + +local floor = math.floor +local strformat = string.format + +local function borrow(tens, units, base) + local frac = tens % 1 + units = units + frac * base + tens = tens - frac + return tens, units +end + +local function carry(tens, units, base) + if units >= base then + tens = tens + floor(units / base) + units = units % base + elseif units < 0 then + tens = tens + floor(units / base) + units = (base + units) % base + end + return tens, units +end + +local function is_leap(y) + if (y % 4) ~= 0 then + return false + elseif (y % 100) ~= 0 then + return true + else + return (y % 400) == 0 + end +end + +local mon_lengths = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} + +-- Number of days in year until start of month; not corrected for leap years +local months_to_days_cumulative = {0} +for i = 2, 12 do + months_to_days_cumulative[i] = months_to_days_cumulative[i - 1] + mon_lengths[i - 1] +end + +local function month_length(m, y) + if m == 2 then + return is_leap(y) and 29 or 28 + else + return mon_lengths[m] + end +end + +local function day_of_year(day, month, year) + local yday = months_to_days_cumulative[month] + if month > 2 and is_leap(year) then + yday = yday + 1 + end + return yday + day +end + +local function leap_years_since(year) + return floor(year / 4) - floor(year / 100) + floor(year / 400) +end + +local leap_years_since_1970 = leap_years_since(1970) + +local function normalise(year, month, day, hour, min, sec) + -- `month` and `day` start from 1, need -1 and +1 so it works modulo + month, day = month - 1, day - 1 + + -- Convert everything (except seconds) to an integer + -- by propagating fractional components down. + year, month = borrow(year, month, 12) + -- Carry from month to year first, so we get month length correct in next line around leap years + year, month = carry(year, month, 12) + month, day = borrow(month, day, month_length(floor(month + 1), year)) + day, hour = borrow(day, hour, 24) + hour, min = borrow(hour, min, 60) + min, sec = borrow(min, sec, 60) + + -- Propagate out of range values up + -- e.g. if `min` is 70, `hour` increments by 1 and `min` becomes 10 + -- This has to happen for all columns after borrowing, as lower radixes may be pushed out of range + min, sec = carry(min, sec, 60) -- TODO: consider leap seconds? + hour, min = carry(hour, min, 60) + day, hour = carry(day, hour, 24) + -- Ensure `day` is not underflowed + -- Add a whole year of days at a time, this is later resolved by adding months + -- TODO[OPTIMIZE]: This could be slow if `day` is far out of range + while day < 0 do + month = month - 1 + if month < 0 then + year = year - 1 + month = 11 + end + day = day + month_length(month + 1, year) + end + year, month = carry(year, month, 12) + + -- TODO[OPTIMIZE]: This could potentially be slow if `day` is very large + while true do + local i = month_length(month + 1, year) + if day < i then + break + end + day = day - i + month = month + 1 + if month >= 12 then + month = 0 + year = year + 1 + end + end + + -- Now we can place `day` and `month` back in their normal ranges + -- e.g. month as 1-12 instead of 0-11 + month, day = month + 1, day + 1 + + return {year = year, month = month, day = day, hour = hour, min = min, sec = sec} +end + +--- Converts unix epoch timestamp into table {year: number, month: number, day: number, hour: number, min: number, sec: number} +-- @param sec unix epoch timestamp +-- @return {year: number, month: number, day: number, hour: number, min: number, sec: number} +function Public.to_timetable(secs) + return normalise(1970, 1, 1, 0, 0, secs) +end + +--- Converts timetable into unix epoch timestamp +-- @param timetable
{year: number, month: number, day: number, hour: number, min: number, sec: number} +-- @return number +function Public.from_timetable(timetable) + local tt = normalise(timetable.year, timetable.month, timetable.day, timetable.hour, timetable.min, timetable.sec) + + local year, month, day, hour, min, sec = tt.year, tt.month, tt.day, tt.hour, tt.min, tt.sec + + local days_since_epoch = + day_of_year(day, month, year) + 365 * (year - 1970) + -- Each leap year adds one day + (leap_years_since(year - 1) - leap_years_since_1970) - + 1 + + return days_since_epoch * (60 * 60 * 24) + hour * (60 * 60) + min * 60 + sec +end + +--- Converts unix epoch timestamp into human readable string. +-- @param secs unix epoch timestamp +-- @return string +function Public.to_string(secs) + local tt = normalise(1970, 1, 1, 0, 0, secs) + return strformat('%04u-%02u-%02u %02u:%02u:%02d', tt.year, tt.month, tt.day, tt.hour, tt.min, tt.sec) +end + +return Public