package game_logic.runescape import controllers.Automaton import controllers.PointerVarianceBounds import controllers.RobotAutomaton import params.StandingTaskParams import params.TravelTaskParams import java.awt.Point import java.awt.event.KeyEvent /** * Implementation of [RSOrchestrator] using a [RobotAutomaton]. * * This class handles executing RuneScape automation tasks by controlling * the game client via image recognition and input emulation. * * Usage examples: * Travel between bank and crafting station: * ``` * val agent = RSAgent.getInstance() * val travelParams = TravelTaskParams(...) * agent.doTravelTask(agent, travelParams) * ``` * Craft while standing at bank: * ``` * val standingParams = StandingTaskParams(...) * orchestrator.doStandingTask(agent, standingParams) * ``` * * * @param automaton The [Automaton] instance used to control the game. Defaults to [RobotAutomaton]. */ class RSAgent(override val automaton: Automaton = RobotAutomaton()) : RSOrchestrator { companion object { /** * Extra padding in milliseconds added before actions to account for latency. 500ms is entirely arbitrary. It is * simply a value that works well during high-load periods. Better to be conservative than lossy. * * This defines an extra duration in milliseconds that is added to sleeps * and waits. * * It is to account for latency in the system before actions like mouse moves * and clicks actually take effect. */ private const val LATENCY_PADDING_MS: Long = 500L /** * The duration in milliseconds of one "tick". The duration of 600ms matches the tick duration of game servers. * * This defines the concept of a "tick" as a unit of time used for pacing actions. * * It is used in methods like [sleepForNTicks] to calculate sleep durations * based on multiplying a number of ticks by this value. * * For example, 5 ticks with this value would be 5 * 600 = 3000ms sleep duration. */ private const val TICK_DURATION_MS = 600L } /*============================================================================================================== interface implementation ==============================================================================================================*/ /** * Handles the crafting process when standing at the bank. * * This method takes care of the workflow when standing at the bank: * - Opens the bank interface by left-clicking near the provided bank point location. * - Withdraws the desired inventory preset using the provided hotkey. * - Opens the crafting interface using the provided crafting dialogue hotkey. * - Clicks the default "Accept" hotkey to start crafting. * - Waits for the specified crafting duration plus random variance. * * @param taskParams The [StandingTaskParams] configuring the task details like bank location, hotkeys, and durations. */ override fun processAtBank( taskParams: StandingTaskParams ) { //open the bank located by the chest parameter moveMouseLeftClickAndSleep(automaton.getNearbyPoint(taskParams.bankPoint, PointerVarianceBounds(10, 10)), 900, 400) //withdraw the desired inventory preset automaton.keyPress(taskParams.bankPresetHotkey) //sleep for a server tick sleepForNTicks(1) //open the crafting dialog with the correct hotkey automaton.keyPress(taskParams.craftingDialogHotkey) //sleep for a server tick sleepForNTicks(1) //press the "accept" default hotkey automaton.keyPress(KeyEvent.VK_SPACE) //wait for the desired time to finish automaton.sleep(taskParams.craftingWaitDurationMillis, taskParams.craftingWaitDurationVarianceMillis) } /** * Handles the crafting process when at a station near the bank. * * This method orchestrates the workflow when at the crafting station: * * - Travels from the bank to the station by left-clicking near the bank point. * - Waits for the randomized travel duration. * * - Withdraws the desired inventory preset using the provided hotkey. * * - Travels to the crafting station by left-clicking the station point. * - Waits for the randomized travel duration. * * - Opens the crafting interface using the provided hotkey. * * - Waits for the randomized crafting duration. * * @param taskParams The [TravelTaskParams] configuring the task details. */ override fun processAtStationNearBank( taskParams: TravelTaskParams ) { //move to the bank and open the interface moveMouseLeftClickAndSleep( automaton.getNearbyPoint(taskParams.bankPoint, PointerVarianceBounds(10, 10)), taskParams.travelDurationMillis, taskParams.travelDurationVarianceMillis ) //withdraw desired loadout automaton.keyPress(taskParams.bankPresetHotkey) sleepForNTicks(1) //move to station and open the crafting dialog moveMouseLeftClickAndSleep(taskParams.travelPoint, taskParams.travelDurationMillis, taskParams.travelDurationVarianceMillis) //start the crafting task automaton.keyPress(KeyEvent.VK_SPACE) //wait for it to complete automaton.sleep(taskParams.craftingWaitDurationMillis, taskParams.craftingWaitDurationVarianceMillis) } /*============================================================================================================== cheater functions ==============================================================================================================*/ /** * Prompts the user to position the mouse and returns that position. * * This method prints a prompt message, then waits for the user to position * the mouse. * * It then returns the current mouse position as a Point after a slight delay. * * @return The Point position of the mouse after user positions it. */ override fun getBankPoint(): Point { return promptUserForPoint("Hold your mouse over the bank...") } /** * Sleeps for a specified number of game ticks. * * A brief random variance is also added to the sleep duration to add some less-robotic behavior. * * @param n The number of game ticks to sleep for. * @see TICK_DURATION_MS */ override fun sleepForNTicks(n: Long) { val latencyPadding = LATENCY_PADDING_MS val baseWaitTime = n * TICK_DURATION_MS automaton.sleep(latencyPadding + baseWaitTime, 150) } }