local VERSION = "3.6b1" current_client_lnk_in_use = "rom" -- Default client link name. It uses this link if no 'client' link is specified. -- Load server link list. if fileExists(getExecutionPath() .. "/ServerLinkList.lua") then include("ServerLinkList.lua") end -- Updates the character selection address. local characterSelectionAddress local function updateCharacterSelectionAddress() local pattern = string.char( 0xA1, 0xFF, 0xFF, 0xFF, 0xFF, 0x3B, 0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x28, 0x56, 0x8B, 0xFF) local mask = "x????xx????xxxxx" local offset = 1 local startloc = 0x560000 local found = findPatternInProcess(getProc(), pattern, mask, startloc, 0xA0000); if( found == 0 ) then error("Unable to find characterSelectionAddress. Pattern needs updating.", 0); end characterSelectionAddress = memoryReadUInt(getProc(), found + offset); end -- Restores game window and makes it active. local function restoreWindow() local foreground = foregroundWindow() local client = getWin() while foreground ~= client or getWindowPos(client) == -32000 do if foreground == 0 then yrest(500) elseif foreground == getHwnd() or getWindowPos(client) == -32000 then showWindow(client, sw.restore) local timer = getTime() if getWindowPos(client) == -32000 then repeat yrest(500) until getWindowPos(client) ~= -32000 or deltaTime(getTime(),timer) > 5000 else repeat yrest(500) until foregroundWindow() == client or deltaTime(getTime(),timer) > 2000 end elseif foreground ~= client then showWindow(client, sw.minimize) local timer = getTime() repeat yrest(500) until getWindowPos(client) == -32000 or deltaTime(getTime(),timer) > 2000 end foreground = foregroundWindow() end end -- Attempts to lock the 'Lock' dir. local function lockdir() local success = os.execute("mkdir \""..getExecutionPath().."/../Lock\" 2> nul") if (success == true) or (success == 0) then local file, err = io.open(getExecutionPath().."/../lock.pid", "w"); if( not file ) then error(err, 0); end file:write("pid:"..getHwnd().." time:"..os.time()) file:close() return true end end -- Unlocks the 'Lock' dir. local function unlockdir() os.execute("rmdir \""..getExecutionPath().."/../Lock\"") end -- Returns true if the lock is stale(old). local function lockStale() local file = io.open(getExecutionPath().."/../Lock.pid", "r"); if file then local raw = file:read() or "" file:close() local apid, atime = string.match(raw,"pid:(.*) time:(.*)") if apid and atime then return (os.time()-tonumber(atime)) > 60 end end end -- Waits until it can reserve the active window. function reserveActiveWindow() -- Wait for if other bot needs active window repeat if lockdir() then -- Lock successful break else -- Check if lock stale if lockStale() then unlockdir() rest(100) else rest(3000) end end until false end -- Releases the active window when no longer needed. function releaseActiveWindow() unlockdir() end -- Starts the game client. local function startClient(client) print("Starting client "..client.." ...") -- Get shortcut if not string.match(client, "%.lnk$") then client = client .. ".lnk" end if not fileExists(getExecutionPath().."/"..client) then error("Game shortcut does not exist: ".. client) end client = string.lower(client) local path = getExecutionPath().."/"..client -- fix spaces path = string.gsub(path," ","\" \"") reserveActiveWindow() -- So you don't accidentally attach to another consoles client. -- Get a list of current clients local beforeWindows = findWindowList("*", "Radiant Arcana"); -- Start client local a,b,c = os.execute("START "..path.." NoCheckVersion") if not a then error("Trouble executing shortcut. Values returned were: "..(a or "nil")..", "..(b or "nil")..", "..(c or "nil")) end -- Wait for new client to start __WIN = nil repeat rest(1000) local nowWindows = findWindowList("*", "Radiant Arcana"); for i = 1, #nowWindows do local found = false for j = 1, #beforeWindows do if nowWindows[i] == beforeWindows[j] then found = true break end end if not found then -- This is the one we just started __WIN = nowWindows[i] break end end until __WIN releaseActiveWindow() current_client_lnk_in_use = client end -- Returns which screen the game is up to. local function gameState(win) local proc if win then proc = openProcess( findProcessByWindow(win) ) else proc = getProc() end if proc == nil then error("No valid process found.") end local atLogin = memoryReadUInt(proc, addresses.editBoxHasFocus_address) == 0 -- Only when not in game local atCharacterSelection = memoryReadUInt(proc, characterSelectionAddress) == 10 local atLoadingScreen = memoryReadBytePtr(proc,addresses.loadingScreenPtr, addresses.loadingScreen_offset) ~= 0 local inGame = memoryReadInt(proc, addresses.isInGame) == 1 if win then closeProcess(proc) end if not atLoadingScreen then if inGame then return 3 elseif atCharacterSelection then return 2 elseif atLogin then return 1 end end return 0 -- Means between states end -- Clicks an account login button on the login screen. local function clickLoginButton(account) -- Login button array (ba) local ba = {} local a = 0 a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+38]={row=a,col=3} ba[a+49]={row=a,col=4} ba[a+59]={row=a,col=5} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+38]={row=a,col=3} ba[a+49]={row=a,col=4} ba[a+59]={row=a,col=5} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+38]={row=a,col=3} ba[a+49]={row=a,col=4} ba[a+59]={row=a,col=5} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+38]={row=a,col=3} ba[a+49]={row=a,col=4} ba[a+59]={row=a,col=5} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+38]={row=a,col=3} ba[a+49]={row=a,col=4} ba[a+59]={row=a,col=5} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+30]={row=a,col=3} ba[a+51]={row=a,col=5} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+30]={row=a,col=3} ba[a+40]={row=a,col=4} ba[a+51]={row=a,col=5} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+30]={row=a,col=3} ba[a+40]={row=a,col=4} ba[a+51]={row=a,col=5} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+30]={row=a,col=3} ba[a+40]={row=a,col=4} ba[a+51]={row=a,col=5} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+30]={row=a,col=3} ba[a+40]={row=a,col=4} ba[a+51]={row=a,col=5} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} a=a+1 ba[a]={row=a,col=1} ba[a+19]={row=a,col=2} ba[a+30]={row=a,col=3} ba[a+40]={row=a,col=4} ba[a+51]={row=a,col=5} ba[a+70]={row=a,col=6} ba[a+89]={row=a,col=7} local function buttonXPer(butnum) return -62 + 15.5*ba[butnum].col end local function buttonYPer(butnum) return -73 + 3.5*ba[butnum].row end restoreWindow() -- Get dimensions of window local x,y,w,h = windowRect(getWin()) -- Calculate coords local xPer,yPer = buttonXPer(account), buttonYPer(account) moveX,moveY = x+w/2+xPer*h/100, y+h+yPer*h/100 -- Move and click mouse detach() mouseSet(moveX,moveY) rest(100) mouseLClick() attach(getWin()) end -- Clicks a character on the character selection screen. local function clickCharacter(num) -- Character click array local charXPer = -18 local charYPer = { [1] = -26, [2] = -19, [3] = -12, [4] = -5, [5] = 2, [6] = 9, [7] = 16, [8] = 23, } restoreWindow() -- Get dimensions of window local x,y,w,h = windowRect(getWin()) -- Calculate coords local xPer,yPer = charXPer, charYPer[num] moveX,moveY = x+w+xPer*h/100, y+h/2+yPer*h/100 -- Move and click mouse detach() mouseSet(moveX,moveY) rest(100) mouseLClick() attach(getWin()) end -- Main login function. function login(character, account, client, clientx, clienty, clientw, clienth, consolex, consoley, consolew, consoleh) if consolex or consoley or consolew or consoleh then setWindowPos(getHwnd(), consolex, consoley, consolew, consoleh) rest(500) end client = client or current_client_lnk_in_use if not client then error("No client specified.") end if not account then error("No account specified.") end if not character then error("No character specified.") end startClient(client) if clientx or clienty or clientw or clienth then setWindowPos(__WIN, clientx, clienty, clientw, clienth) end rest(5000) __PROC = openProcess( findProcessByWindow(getWin()) ) -- Need to update the character selection address before any use of gameState() updateCharacterSelectionAddress() -- Wait until it's at the login screen. ('atLogin' returns true early so I'm not sure this is necessary) repeat rest(1000) until gameState(__WIN) == 1 rest(3000) -- Click login button until it leave login reserveActiveWindow() print("Clicking account "..account.." ...") repeat clickLoginButton(account) rest(2000) until gameState() ~= 1 releaseActiveWindow() -- Wait until at character selection repeat rest(2000) until gameState() == 2 rest(3000) -- Click character reserveActiveWindow() print("Selecting character "..character.." ... ") clickCharacter(character) rest(500) keyboardPress(key.VK_ENTER) releaseActiveWindow() -- Wait until in game print("Waiting until in-game ...") repeat rest(2000) until gameState() == 3 rest(3000) --== Things that need updating IdAddressTables = {} if CCamera then local cameraAddress = memoryReadUIntPtr(getProc(), addresses.staticbase_char, addresses.camPtr_offset) or 0; camera = CCamera(cameraAddress); end if CGuildbank then guildbank = CGuildbank() end -- Remember login details if player then player:update() -- Makes sure the macro is set up for RoMScripts. CurrentLoginAcc = RoMScript("LogID") CurrentLoginChar = RoMScript("CHARACTER_SELECT.selectedIndex") CurrentLoginServer = RoMScript ("GetCurrentRealm") end end -- Kills the client and waits for it to end. function killClient() if __WIN and windowValid(__WIN) then local pid = findProcessByWindow(__WIN); os.execute("TASKKILL /PID " .. pid .. " /F") end local crashed, pid, killed repeat rest(500) if not killed then crashed, pid = isClientCrashed() if crashed then os.execute("TASKKILL /PID " .. pid .. " /F"); killed = true end end until not windowValid(__WIN) end