Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ; WHAT THE SCRIPT ACTUALLY DOES
- ; It uses AutoHotKey automation scripting language to control computer's keyboard and mouse. It moves player and shoots randomly, it says random messages at random time, it says randomly messages to killed opponents and it even reads the opponents' messages and answers to them by putting their words in random order with postfix. It also changes player's nickname randomly to make ignoring almost impossible and keeps track of used nickanems in file usednicknames.txt file (if you e.g. want to parse the log file later)
- ; INSTRUCTIONS
- ; 1. Download and install AutoHotKey from https://www.autohotkey.com/
- ; 2. Save the script as bot.ahk in Quake 2 root path
- ; 3. Download file tf.ahk from https://github.com/hi5/TF and put it to Quake 2 root path
- ; 4. Put following setting to Quake 2 config: "logfile 3". This should create log file to baseq2\qconsole.log . If the file path is different in your Quake 2 installation, change variable logFilePath
- ; 5. Change you screen resolution to variables xResolution and yResolution
- ; 6. Change Quake 2 executable name to variable quake2ExecutableName (e.g. quake2.exe, kmquake2.exe, q2pro.exe ...)
- ; 7. Create file messages.txt to Quake 2 root path. Put one message per line that you want the bot to say randomly
- ; 8. Create file deaths.txt to Quake 2 root path. Put one message per line that you want the bot to say randomly to the opponent when he/she dies
- ; 9. Right click bot.ahk file and select "Run Script"
- ; 10. Launch Quake 2 and start the game. The script is paused by default. Press Ctrl+Z to toggle the pause. Press Ctrl+X to stop the script
- ; TIPS / KNOWN ISSUES
- ; - You might want to use VPN to avoid banning of your real IP-address!
- ; - Function MapKeys might need adjusting depending on your regional keyboard setup (if you live in US, it's possible that mapping is not needed at all). More info about the mapping process at https://www.autohotkey.com/docs/KeyList.htm#SpecialKeys
- ; - It's possible that the bot sometimes answers to itself, because it doesn't remember nickname it was using previously (would need to scan file usednicknames.txt)
- ; !!! PLEASE FEEL FREE TO MODIFY THE SCRIPT AND HAPPY BOTTING !!!
- ; -------------- VARIABLES --------------
- ; Executable name that the script runs in. If the application is alt+tabbed to something else, the script stops running automatically
- quake2ExecutableName := "kmquake2.exe"
- xResolution := 2560
- yResolution := 1440
- logFilePath := "baseq2\qconsole.log"
- messagesFilePath := "messages.txt"
- deathsFilePath := "deaths.txt"
- usedNickNamesFilePath := "usednicknames.txt"
- ; -------------- SCRIPT STARTS --------------
- #If WinActive("ahk_exe " . quake2ExecutableName)
- nickName := ""
- Pause On
- xShootCoordinate := Ceil((xResolution/2)*1.06)
- ; for 2560x1400 center is 1279, 720
- yShootCoordinate := (yResolution/2)-1
- Loop
- {
- if WinActive("ahk_exe " . quake2ExecutableName)
- {
- Random, rand, 1, 60
- if (rand = 4)
- {
- nickName := RandomString(15, "lI()!i1 []{}|ll11")
- ConsoleCommand("set name """ . nickName . """")
- FileAppend, %nickName%`r`n, %usedNickNamesFilePath%
- }
- else if (rand > 5 && rand < 10)
- {
- SayMessage(RandomRowFromFile(messagesFilePath))
- }
- else if (rand > 10 && rand < 20)
- {
- ; Second last line, to give some delay for the message
- logRow := LastRowFromFile(logFilePath, -2)
- if (IsChatMessageRow(logRow))
- {
- rowNickName := GetNicknameFromChatMessageRow(logRow)
- ; Only answer to real players and public chat messages
- if (!InStr(rowNickName, nickName) && !InStr(rowNickName, "console") && !InStr(rowNickName, "WallFly") && !InStr(rowNickName, "(private message)") && !InStr(rowNickName, "(~"))
- {
- SayMessage(rowNickName . ", " . WordsToRandomOrder(GetMessageFromChatMessageRow(logRow)) . " LOL :D")
- }
- }
- else if (IsDeathRow(logRow))
- {
- rowNickName := GetNicknameFromDeathRow(logRow)
- if (!InStr(rowNickName, nickName))
- {
- SayMessage(rowNickName . ", " . RandomRowFromFile(deathsFilePath))
- }
- }
- }
- sleepTime := rand * 40
- if (rand < 45)
- {
- Send, {w Down}
- Click, Right
- Sleep %sleepTime%
- Send, {w Up}
- }
- else
- {
- Send, {s Down}
- Click, Right
- Sleep %sleepTime%
- Send, {s Up}
- }
- MouseMove, xShootCoordinate, yShootCoordinate
- if (rand > 20)
- {
- Click
- }
- if (rand > 30)
- {
- Send, {d Down}
- Send, {c Down}
- Sleep 300
- Send, {d Up}
- Send, {c Up}
- }
- }
- }
- return
- ; Ctrl + z to toggle pause
- ^z::Pause
- ; Ctrl + x to exit the script
- ^x::ExitApp
- ; -------------- FUNCTIONS STARTS --------------
- #Include tf.ahk
- MapKeys(input)
- {
- mappedInput := ""
- Loop, Parse, input
- {
- char := ""
- ; Normal characters that does not need a mapping
- if (RegExMatch(A_LoopField, "[0-9a-zA-Z,.\s]"))
- {
- char := A_LoopField
- }
- else
- {
- ; Special characters that needs special mapping
- switch A_LoopField
- {
- case "!": char := "+{SC002}"
- case "@": char := "+{SC003}"
- case "#": char := "+{SC004}"
- case "$": char := "+{SC005}"
- case "%": char := "+{SC006}"
- case "^": char := "+{SC007}"
- case "&": char := "+{SC008}"
- case "*": char := "+{SC009}"
- case "(": char := "+{SC00A}"
- case ")": char := "+{SC00B}"
- case "-": char := "{SC00C}"
- case "_": char := "+{SC00C}"
- case "=": char := "{SC00D}"
- case "+": char := "+{SC00D}"
- case "[": char := "{SC01A}"
- case "{": char := "+{SC01A}"
- case "]": char := "{SC01B}"
- case "}": char := "+{SC01B}"
- case ";": char := "{SC027}"
- case ":": char := "+{SC027}"
- case "'": char := "{SC028}"
- case """": char := "+{SC028}"
- case "\": char := "{SC02B}"
- case "|": char := "+{SC02B}"
- case "<": char := "+{SC033}"
- case ">": char := "+{SC034}"
- case "/": char := "{SC035}"
- case "?": char := "+{SC035}"
- }
- }
- mappedInput .= char
- }
- return mappedInput
- }
- SayMessage(message)
- {
- ; Remove/convert characters that cannot be used in chat message
- message := StrReplace(message, """", "'")
- message := MapKeys(message)
- Send, t
- Sleep 20
- Send, %message%
- Send, {Enter}
- }
- ConsoleCommand(command)
- {
- command := MapKeys(command)
- Send, {SC029}
- Sleep 20
- Send, %command%
- Send, {Enter}
- Send, {SC029}
- }
- RandomString(length, characters)
- {
- returnValue := ""
- Loop % length
- {
- Random, rand, 1, StrLen(characters)
- returnValue := returnValue . SubStr(characters, rand, 1)
- }
- return returnValue
- }
- WordsToRandomOrder(words)
- {
- randomWords := words
- Sort, randomWords, D Random
- return randomWords
- }
- GetWord(word, number)
- {
- StringSplit, wordArray, word, % A_Space
- return wordArray%number%
- }
- RemoveNewLines(input)
- {
- returnValue := input
- StringReplace,returnValue,returnValue,`n,,A
- StringReplace,returnValue,returnValue,`r,,A
- return returnValue
- }
- IsChatMessageRow(row)
- {
- if (SubStr(row, 1, 2) = "^a")
- {
- return true
- }
- return false
- }
- GetNicknameFromChatMessageRow(row)
- {
- firstWord := GetWord(row, 1)
- return SubStr(firstWord, 3, StrLen(firstWord)-3)
- }
- GetMessageFromChatMessageRow(row)
- {
- parts := StrSplit(row, ": ")
- if (parts[2])
- {
- return parts[2]
- }
- return ""
- }
- IsDeathRow(row)
- {
- if (InStr(row,"was railed by")
- || InStr(row,"was popped by")
- || InStr(row,"was blown away")
- || InStr(row,"almost dodged"))
- {
- return true
- }
- return false
- }
- GetNicknameFromDeathRow(row)
- {
- firstWord := GetWord(row, 1)
- return firstWord
- }
- RandomRowFromFile(filePath)
- {
- linesCount := TF_CountLines(filePath)
- Random, lineRand, 1, linesCount
- row := TF_ReadLines(filePath, lineRand, lineRand)
- row := RemoveNewLines(row)
- return row
- }
- ; Retrieves the last row by default
- LastRowFromFile(filePath, rowNumber := 0)
- {
- row := TF_Tail(filePath, rowNumber, 0, 0)
- row := RemoveNewLines(row)
- return row
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement