import java.awt.Point import java.awt.event.KeyEvent /** * Interface for a RuneScape orchestrator that can execute bot actions. * * This defines the capabilities that a RuneScape orchestrator needs to * coordinate and perform bot actions like banking, traveling, and crafting. * * Implementations will contain the game-specific logic to interact with the * RuneScape client and APIs to carry out the actions. * * Usage example: * ``` * val orch = RSOrchestrator.getInstance() * val params = StandingTaskParams(...) * RSOrchestrator.doStandingTask(orch, params) * ``` */ interface RSOrchestrator : Orchestrator { companion object { /** * Performs a standing crafting task loop at the bank. * * This handles a simple crafting loop where the player stands at the bank and crafts. * * It withdraws items from the bank, crafts a batch of items, deposits crafted items, * and repeats. * * Usage example: * ``` * val orch = RSOrchestrator.getInstance() * val params = StandingTaskParams(...) * RSOrchestrator.doStandingTask(orch, params) * ``` * * @param orchestrator The [RSOrchestrator] that will execute the actions. * @param params The [StandingTaskParams] configuring the task details. * @return Unit. */ fun doStandingTask(orchestrator: RSOrchestrator, params: StandingTaskParams) { orchestrator.doLoop(params.totalVolume, params.volumePerStep) { orchestrator.processAtBank(params) } } /** * Performs a crafting workflow loop that involves traveling between a bank and crafting station. * * This handles the overall workflow orchestration of: * 1. Withdrawing items from the bank. * 2. Traveling to the crafting station. * 3. Crafting items. * 4. Traveling back to the bank when inventory is empty. * * It will repeat this loop for the specified total volume of items to craft, doing the given volume per loop iteration. * * Usage example: * * ``` * val orch = RSOrchestrator.getInstance() * val params = TravelTaskParams(...) * RSOrchestrator.doTravelTask(orch, params) * ``` * * @param orchestrator The [RSOrchestrator] instance that will execute the actual actions. * @param params The [TravelTaskParams] configuring the crafting loop details. * @return Unit. */ fun doTravelTask(orchestrator: RSOrchestrator, params: TravelTaskParams) { orchestrator.doLoop(params.totalVolume, params.volumePerStep) { orchestrator.processAtStationNearBank(params) } } /** * Gets an instance of the [RSOrchestrator]. * * This provides access to the orchestrator instance that can be used to * coordinate bot actions. * * @return The [RSOrchestrator] instance. */ fun getInstance(): RSOrchestrator { return RSAgent() } } //end of companion object /** * Handles the crafting process when standing at the bank. * * This method orchestrates 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 "Make" hotkey to start crafting. * - Waits for the specified crafting duration plus random variance. * * Usage example: * ``` * val params = StandingTaskParams(...) * orchestrator.processAtBank(params) * ``` * * @param taskParams The StandingTaskParams configuring the task details like bank location, hotkeys, durations etc. */ fun processAtBank(taskParams: StandingTaskParams) /** * Handles the crafting workflow when at a station near the bank. * * This orchestrates the steps to craft items at a station near the bank: * * - Travels from the bank to the station by left-clicking near the bank point. * - Waits for the randomized travel duration. * * - Withdraws the preset inventory from the bank using the hotkey. * - Waits for the randomized bank interaction duration. * * - 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. * - Clicks the "Make" button using the hotkey to craft items. * - Waits for the randomized crafting duration. * * Usage example: * ``` * val params = TravelTaskParams(...) * orchestrator.processAtStationNearBank(params) * ``` * * @param taskParams The TravelTaskParams configuring the task details like locations, durations, hotkeys etc. */ fun processAtStationNearBank(taskParams: TravelTaskParams) /** * Gets the screen point location of the bank. * * This returns the x,y screen coordinates where the bank is located, which can be used to interact with the bank. * * Usage: * * ``` * val bankPoint = orchestrator.getBankLocation() * * // Left click the bank location to open the interface * orchestrator.moveMouseLeftClickAndSleep(bankPoint, 100) * * // Withdraw preset inventory at bank * orchestrator.automaton.keyPress(KeyEvent.VK_F1) * ``` * * @return The Point representing the x,y screen coordinates of the bank location. */ fun getBankPoint(): Point } /** * Implementation of [RSOrchestrator] using a [RobotController]. * * 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 [RobotController]. */ private class RSAgent(override val automaton: Automaton = RobotController()) : 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. * * Usage example: * * ``` * val params = StandingTaskParams(...) * orchestrator.processAtBank(params) * ``` * * @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.getAlmostPoint(taskParams.bankPoint, WiggleParams()), 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.sleepWithVariance(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. * * Usage example: * ``` * val params = TravelTaskParams(...) * orchestrator.processAtStationNearBank(params) * ``` * * @param taskParams The [TravelTaskParams] configuring the task details. */ override fun processAtStationNearBank( taskParams: TravelTaskParams ) { //move to the bank and open the interface moveMouseLeftClickAndSleep( automaton.getAlmostPoint(taskParams.bankPoint, WiggleParams()), 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.sleepWithVariance(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. * * The delay allows the mouse to settle before sampling its position. * * Usage example: * ``` * // Get bank location from user mouse position * val bankPoint = orchestrator.getBankPoint() * // Left click the bank to open the interface * orchestrator.automaton.mouseMove(bankPoint) * orchestrator.automaton.mouseClick(InputEvent.BUTTON1_DOWN_MASK) * ``` * * @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. * * This method calculates the total sleep duration based on the number of ticks * and the tick duration constant. It then sleeps for that amount plus a small * latency padding. * * A random variance is also added to the sleep duration to add some less-robotic behavior. * * Usage example: * ``` * val ticks = 10 // Sleep for 10 game ticks * orchestrator.sleepTicks(ticks) * ``` * * @param n The number of game ticks to sleep for. */ fun sleepForNTicks(n: Long) { val latencyPadding = LATENCY_PADDING_MS val baseWaitTime = n * TICK_DURATION_MS automaton.sleepWithVariance(latencyPadding + baseWaitTime, 150) } }