-- lib.lua -- This is a pseudo library designed for MicroMaco. -- Functions, variables, etc. declared here will -- be accessable in all scripts made for MicroMacro. -- -- Note: You should not modify this script unless -- you are fixing something. Please report fixes -- to the author (seph@mchsi.com) or post on the -- forums at http://solarimpact.servegame.com -- -- Any changes you make may be overwritten if -- you chose to update. Consider making a module -- instead. cocoAvailable = (coroutine.coco == true); ------------------------------------------------------ -- Import configurations and libraries ------------------------------------------------------ -- Set module path package.path = ".\\?.lua;" .. getPath() .. "/lib/mods/?.lua"; require("xml"); require("sw"); require("priority"); require("classes"); require("cli"); ------------------------------------------------------ -- Keyboard & Language setup ------------------------------------------------------ key = nil; if( keyboard ~= nil and type(keyboard) == "string" ) then key = require("keyboard/" .. keyboard); end if( key == true or key == nil ) then setTextColor(cli.yellow); error("Error loading keyboard module."); key = { }; end ------------------------------------------------------ -- VARIABLE DECLARATION ------------------------------------------------------ -- Our start/stop keys. -- Only useful when using startMacro() startKeyDefault = key.VK_F5; stopKeyDefault = key.VK_F6; startKey = startKeyDefault; stopKey = stopKeyDefault; -- Timed function list. -- These are automatically "timed" when registered timerList = {} -- Priority Defines -- DEPRECATED! Use priority.high, priority.low, or priority.normal instead PRIORITY_HIGH = 1; PRIORITY_NORMAL = 0; PRIORITY_LOW = -1; ------------------------------------------------------ -- FUNCTION DECLARATION ------------------------------------------------------ -- Dumps a traceback into MicroMacro's log -- for internal use function __log_traceback() logRaw(debug.traceback("", 2)); logRaw("\n\n----------TRACEBACK END----------\n\n"); end -- Runs a coroutine in a protected state, if available function safeYield() -- make sure we're not trying to yield in the main thread -- do nothing. local co = coroutine.running(); if( co == nil ) then return; end if( cocoAvailable ) then local status, err = pcall(coroutine.yield); if( status ~= true ) then __log_traceback(); setTextColor(cli.yellow); error(err, 3); end else coroutine.yield(); end end -- unpacks varargs and returns them as a table. -- also returns 'n', the true size of the table. function unpack2(...) local n = select('#', ...); local t = {}; for i = 1,n do local v = select(i, ...); t[i] = v; end return t, n; end -- Formatted output -- C printf-like function function printf(format, ...) local t, n = unpack2(...); for i = 1,n do local v = t[i]; if( type(v) == "nil" ) then local err = sprintf("bad argument #%d to 'printf' (got %s)", i, type(v)); error(err, 2); end if( type(v) == "table" or type(v) == "boolean" or type(v) == "function" or type(v) == "thread" or type(v) == "userdata" ) then t[i] = tostring(t[i]); end end local status, err = pcall(string.format, format, unpack(t)); if( status == false ) then error(err, 2); end io.write(err); end -- Colored printf-like function function cprintf(color, format, ...) setTextColor(color); io.write(string.format(format, ...)); setTextColor(cli.lightgray); end -- Formatted output -- C sprintf-like function --sprintf = string.format; function sprintf(format, ...) local t, n = unpack2(...); for i = 1,n do local v = t[i]; if( type(v) == "nil" ) then local err = sprintf("bad argument #%d to 'printf' (got %s)", i, type(v)); error(err, 2); end if( type(v) == "table" or type(v) == "boolean" or type(v) == "function" or type(v) == "thread" or type(v) == "userdata" ) then t[i] = tostring(t[i]); end end local status, err = pcall(string.format, format, unpack(t)); if( status == false ) then error(err, 2); end return err; end function __printf(...) -- pcall() wrapper to trap format errors to the source local function WrapperFunc() io.write(string.format(unpack(arg))) end local status, err = pcall(WrapperFunc); if not status then error(err, 2); end end -- Include another file -- Directly calling dofile() may lead to issues with -- relative paths. It is recommended to use include(). function include(file) if( file == nil or string.len(file) < 1 ) then error("Cannot include \'nil\'.", 2); end local startExecutionPath = getExecutionPath(); local test = string.find(file, "%a:[/\\]"); local isRelative = ( test == nil ); local status, err; if( isRelative ) then local fullpath = startExecutionPath .. "/" .. file; setExecutionPath(getFilePath(fullpath)); status, err = pcall(dofile, fullpath); else setExecutionPath(getFilePath(file)); status, err = pcall(dofile, file); end if( not status ) then error(err, 2); end setExecutionPath(startExecutionPath); end -- Time conversion for timers -- Convert hours to timer value function hoursToTimer(hours) return math.floor( hours * 3600000 ); end -- Convert minutes to timer value function minutesToTimer(minutes) return math.floor( minutes * 60000 ); end -- Converts seconds to timer value function secondsToTimer(seconds) return math.floor( seconds * 1000 ); end -- Prepares an at-exit callback function that is -- called when the script terminates __EXIT_CALLBACK = nil; function atExit(func) if( type(func) ~= "function" and type(func) ~= "nil" ) then local err = "Error: Non-function type passed to atExit() where a function is expected."; setTextColor(cli.yellow); error(err, 2); return; end __EXIT_CALLBACK = func; end -- Prepare an at-pause callback function -- called when the script is paused. function defaultPauseCallback() printf("Paused.\n"); end __PAUSE_CALLBACK = defaultPauseCallback; function atPause(func) if( type(func) ~= "function" and type(func) ~= "nil" ) then local err = "Error: Non-function type passed to atPause() where a function is expected."; setTextColor(cli.yellow); error(err, 2); return; end __PAUSE_CALLBACK = func; if( func == nil ) then __PAUSE_CALLBACK = defaultPauseCallback; end end -- Prepare an at-resume callback function -- called when the script is resumed. function defaultResumeCallback() printf("Started.\n"); end __RESUME_CALLBACK = defaultResumeCallback; function atResume(func) if( type(func) ~= "function" and type(func) ~= "nil" ) then local err = "Error: Non-function type passed to atResume() where a function is expected."; setTextColor(cli.yellow); error(err, 2); return; end __RESUME_CALLBACK = func; if( func == nil ) then __RESUME_CALLBACK = defaultResumeCallback; end end -- Register a function to be called automatically. -- Should be used in combination with startMacro -- as opposed to manually. function registerTimer(name, time, func, ...) if( type(func) ~= "function" ) then local err = "Error: Non-function type passed to registerTimer() where a function is expected."; setTextColor(cli.yellow); error(err, 2); return; end if( type(time) ~= "number" ) then local err = "Error: Non-numerical type passed to registerTimer() where a time value is expected."; setTextColor(cli.yellow); error(err, 2); return; end local tmp = {}; tmp.time = time; tmp.func = func; tmp.args = unpack2(...); newTimer(name); startTimer(name, time); timerList[name] = tmp; end -- Unregisters a function from being called automatically. -- Only works if the function has been registered function unregisterTimer(name) timerList[name] = nil; removeTimer(name); end -- Toggles __PErunning to 0 in order to stop -- the protected environment from continuing. function stopPE() __PErunning = 0; safeYield(); end -- Protected environment for startMacro to run. -- Runs the function as a coroutine. -- It allows us to easily run certain tasks -- regularly without having to program them -- directly into the macro. function __ProtectedEnvironment(foo, defaultstate) if( defaultstate == false ) then __PErunning = 0; printf("The macro is currently not running. Press the start key (%s) to begin.\n", getKeyName(startKey)); printf("You may use (%s) key to stop/pause the script.\n", getKeyName(stopKey)); else __PErunning = 1; printf("Press the (%s) key to stop/pause the script.\nYou can resume with the (%s) key.\n", getKeyName(stopKey), getKeyName(startKey)); end local co = coroutine.create(foo); while( 1 ) do if( keyPressed(startKey) ) and ( __PErunning == 0 ) then for i,v in pairs(timerList) do if( isTriggered(i) ) then -- restart timers startTimer(i, v.time); end end if( __RESUME_CALLBACK ~= nil ) then __RESUME_CALLBACK(); end __PErunning = 1; end; if( keyPressed(stopKey) and ( __PErunning == 1 ) ) then if( __PAUSE_CALLBACK ~= nil ) then __PAUSE_CALLBACK(); end __PErunning = 0; end; if( keyPressedLocal(key.VK_CONTROL) and keyPressedLocal(key.VK_L) ) then break; end -- automatic timer functions if( __PErunning == 1 ) then for i,v in pairs(timerList) do if( isTriggered(i) ) then startTimer(i, v.time); -- restart timer. if( v.func == nil ) then -- throw an error local err = sprintf("Timer \'%s\' error: invalid function", i); setTextColor(cli.yellow); error(err, 2); else v.func(unpack(v.args)); end end end end if( coroutine.status(co) ~= 'dead' ) then -- continue macro if( __PErunning == 1) then local runstatus,message = coroutine.resume(co); if( runstatus == false ) then -- an error has occured --__log_traceback(); setTextColor(cli.yellow); error(message, 2); end else rest(10); -- minimize CPU usage end else -- macro is "dead" break; end end -- cleanup timers for i,v in pairs(timerList) do timerList[i] = nil; end print("Stopping execution."); if( __PAUSE_CALLBACK ~= defaultPauseCallback and __PAUSE_CALLBACK ~= nil ) then __PAUSE_CALLBACK(); end if( __EXIT_CALLBACK ~= nil ) then __EXIT_CALLBACK(); end -- restore defaults startKey = startKeyDefault; stopKey = stopKeyDefault; atResume(nil); atPause(nil); atExit(nil); end -- startMacro passes the call off to -- __ProtectedEnvironment, and handles -- any errors. function startMacro(foo, defaultstate) if( defaultstate ~= true ) then defaultstate = false; end; if( type(foo) ~= "function" ) then local err = "Error: Non-function type passed to startMacro(). Value is of type " .. type(foo) .. "."; __log_traceback(); setTextColor(cli.yellow); error(err, 2); return; end local status, err = pcall( __ProtectedEnvironment, foo, defaultstate); if( status ) then -- no errors else -- errors occured...log them __log_traceback(); setTextColor(cli.yellow); error(err, 3); end end -- A pretty standard rest/sleep command. -- It automatically will yeild, though. function yrest(msec) if( msec == nil ) then error("yrest() cannot rest for \'nil\'.\n", 2); end; safeYield(); -- if Coco is available, use 10ms sections. this is the highest -- resolution available on Windows. -- if Coco is not available, fall back on 100ms sections to -- avoid yielding across C-boundaries local resttime; local sections; local ext; if( cocoAvailable ) then resttime = 10; else resttime = 100; end if( msec < resttime ) then rest(msec); return; else sections = math.floor(msec / resttime); -- split into 10/100ms sections ext = math.mod(msec, resttime); -- any left overs... for b = 1,sections do rest(resttime); safeYield(); end; if( ext > 0 ) then rest(ext); end end end -- Checks if a color is within 'accuracy' of another color. -- Each channel is checked individually. -- 'accuracy' should be between 0 and 255. function colorMatch(color1, color2, accuracy) local r1, g1, b1, r2, g2, b2; if( accuracy == nil ) then accuracy = 0; end; if( accuracy < 0 ) then accuracy = 0; end; if( accuracy > 255 ) then accuracy = 255; end; r1 = getR(color1); g1 = getG(color1); b1 = getB(color1); r2 = getR(color2); g2 = getG(color2); b2 = getB(color2); if( math.abs(r2 - r1) <= accuracy and math.abs(g2 - g1) <= accuracy and math.abs(b2 - b1) <= accuracy ) then return true; -- they match end; return false; -- they don't match end -- Checks and returns true if a file exists. function fileExists(fullpath) local handle = io.open(fullpath, "r"); local success = handle ~= nil; if( success ) then handle:close(); end return success; end -- "explode" a string into a table, splitting at 'token' -- i.e. "Hello World, how are you?" explodes using the space -- character would return {"Hello", "World,", "how", "are", "you?"} -- if 'token' is not given, space is assumed. function explode(instr, token) local findpos; local holder = {}; if( token == nil ) then token = " "; end findpos = string.find(instr, token); if( findpos == nil ) then return instr; end while( findpos ) do table.insert(holder, string.sub(instr, 0, findpos -1)); instr = string.sub(instr, findpos + string.len(token)); findpos = string.find(instr, token); end if( string.len(instr) ) then table.insert(holder, instr); end return holder; end