package com.openosrs.client.api; import com.openosrs.client.core.ClientCore; import com.openosrs.client.engine.GameEngine; import com.openosrs.client.api.types.Position; import com.openosrs.client.api.types.NPC; import com.openosrs.client.api.types.GameObject; import com.openosrs.client.api.types.Item; import com.openosrs.client.api.types.GroundItem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.CompletableFuture; /** * InteractionAPI - Handles player interactions with the game world. * * This API module provides methods for: * - Movement and pathfinding * - NPC interactions * - Object interactions * - Item usage and manipulation * - Chat and communication */ public class InteractionAPI { private static final Logger logger = LoggerFactory.getLogger(InteractionAPI.class); private final ClientCore clientCore; private final GameEngine gameEngine; public InteractionAPI(ClientCore clientCore, GameEngine gameEngine) { this.clientCore = clientCore; this.gameEngine = gameEngine; } /** * Walk to a specific position. */ public CompletableFuture walkTo(Position position) { if (position == null) { return CompletableFuture.completedFuture(false); } logger.debug("Walking to position: {}", position); return CompletableFuture.supplyAsync(() -> { try { // TODO: Integrate with actual game engine walking logic // For now, this is a stub that simulates walking Position currentPos = clientCore.getPlayerState().getPosition(); if (currentPos == null) { logger.warn("Cannot walk - player position unknown"); return false; } double distance = currentPos.distanceTo(position); if (distance == 0) { logger.debug("Already at target position"); return true; } // Simulate walking time based on distance int walkTime = (int) (distance * 100); // 100ms per tile roughly Thread.sleep(Math.min(walkTime, 5000)); // Cap at 5 seconds // TODO: Actually send walk packet to server and update player position logger.debug("Walk completed to {}", position); return true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.warn("Walk interrupted"); return false; } catch (Exception e) { logger.error("Error during walk", e); return false; } }); } /** * Run to a specific position. */ public CompletableFuture runTo(Position position) { if (position == null) { return CompletableFuture.completedFuture(false); } logger.debug("Running to position: {}", position); return CompletableFuture.supplyAsync(() -> { try { // TODO: Enable run mode if not already enabled // Then use walking logic but faster Position currentPos = clientCore.getPlayerState().getPosition(); if (currentPos == null) { logger.warn("Cannot run - player position unknown"); return false; } double distance = currentPos.distanceTo(position); if (distance == 0) { logger.debug("Already at target position"); return true; } // Simulate running time (faster than walking) int runTime = (int) (distance * 60); // 60ms per tile roughly Thread.sleep(Math.min(runTime, 3000)); // Cap at 3 seconds // TODO: Actually send run packet to server and update player position logger.debug("Run completed to {}", position); return true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.warn("Run interrupted"); return false; } catch (Exception e) { logger.error("Error during run", e); return false; } }); } /** * Interact with an NPC. */ public CompletableFuture interactWithNPC(NPC npc, String action) { if (npc == null || action == null) { return CompletableFuture.completedFuture(false); } logger.debug("Interacting with NPC {} using action: {}", npc.getName(), action); return CompletableFuture.supplyAsync(() -> { try { // TODO: Check if NPC is in range // TODO: Send interaction packet to server // TODO: Handle interaction response Position playerPos = clientCore.getPlayerState().getPosition(); if (playerPos == null) { logger.warn("Cannot interact - player position unknown"); return false; } double distance = npc.distanceToPlayer(playerPos); if (distance > 5) { logger.warn("NPC too far away for interaction: {} tiles", distance); return false; } // Simulate interaction time Thread.sleep(600); // 1 game tick logger.debug("Interaction completed with NPC {}", npc.getName()); return true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.warn("NPC interaction interrupted"); return false; } catch (Exception e) { logger.error("Error during NPC interaction", e); return false; } }); } /** * Interact with a game object. */ public CompletableFuture interactWithObject(GameObject object, String action) { if (object == null || action == null) { return CompletableFuture.completedFuture(false); } logger.debug("Interacting with object {} using action: {}", object.getName(), action); return CompletableFuture.supplyAsync(() -> { try { // TODO: Check if object is in range // TODO: Send interaction packet to server // TODO: Handle interaction response Position playerPos = clientCore.getPlayerState().getPosition(); if (playerPos == null) { logger.warn("Cannot interact - player position unknown"); return false; } if (!object.hasAction(action)) { logger.warn("Object {} does not have action: {}", object.getName(), action); return false; } double distance = object.distanceToPlayer(playerPos); if (distance > 5) { logger.warn("Object too far away for interaction: {} tiles", distance); return false; } // Simulate interaction time Thread.sleep(600); // 1 game tick logger.debug("Interaction completed with object {}", object.getName()); return true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.warn("Object interaction interrupted"); return false; } catch (Exception e) { logger.error("Error during object interaction", e); return false; } }); } /** * Pick up a ground item. */ public CompletableFuture pickupItem(GroundItem item) { if (item == null) { return CompletableFuture.completedFuture(false); } logger.debug("Picking up ground item: {} x{}", item.getName(), item.getQuantity()); return CompletableFuture.supplyAsync(() -> { try { // TODO: Check if item is in range // TODO: Check if inventory has space // TODO: Send pickup packet to server Position playerPos = clientCore.getPlayerState().getPosition(); if (playerPos == null) { logger.warn("Cannot pickup - player position unknown"); return false; } double distance = item.distanceToPlayer(playerPos); if (distance > 2) { logger.warn("Item too far away for pickup: {} tiles", distance); return false; } // Check inventory space if (clientCore.getInventoryState().isInventoryFull() && !item.getName().equals("Coins")) { logger.warn("Inventory full - cannot pickup item"); return false; } // Simulate pickup time Thread.sleep(600); // 1 game tick logger.debug("Pickup completed for item {}", item.getName()); return true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.warn("Item pickup interrupted"); return false; } catch (Exception e) { logger.error("Error during item pickup", e); return false; } }); } /** * Use an inventory item. */ public CompletableFuture useItem(int slot) { logger.debug("Using item in slot: {}", slot); return CompletableFuture.supplyAsync(() -> { try { // TODO: Validate slot // TODO: Send use item packet Item item = clientCore.getInventoryState().getInventorySlot(slot); if (item == null || item.isEmpty()) { logger.warn("No item in slot {} to use", slot); return false; } // Simulate use time Thread.sleep(600); // 1 game tick logger.debug("Used item: {}", item.getName()); return true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.warn("Item use interrupted"); return false; } catch (Exception e) { logger.error("Error using item", e); return false; } }); } /** * Use an item on another item. */ public CompletableFuture useItemOnItem(int sourceSlot, int targetSlot) { logger.debug("Using item in slot {} on item in slot {}", sourceSlot, targetSlot); return CompletableFuture.supplyAsync(() -> { try { // TODO: Validate both slots // TODO: Send use item on item packet Item sourceItem = clientCore.getInventoryState().getInventorySlot(sourceSlot); Item targetItem = clientCore.getInventoryState().getInventorySlot(targetSlot); if (sourceItem == null || sourceItem.isEmpty()) { logger.warn("No item in source slot {} to use", sourceSlot); return false; } if (targetItem == null || targetItem.isEmpty()) { logger.warn("No item in target slot {} to use on", targetSlot); return false; } // Simulate combination time Thread.sleep(600); // 1 game tick logger.debug("Used {} on {}", sourceItem.getName(), targetItem.getName()); return true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.warn("Item combination interrupted"); return false; } catch (Exception e) { logger.error("Error combining items", e); return false; } }); } /** * Drop an inventory item. */ public CompletableFuture dropItem(int slot) { logger.debug("Dropping item in slot: {}", slot); return CompletableFuture.supplyAsync(() -> { try { // TODO: Validate slot // TODO: Send drop item packet Item item = clientCore.getInventoryState().getInventorySlot(slot); if (item == null || item.isEmpty()) { logger.warn("No item in slot {} to drop", slot); return false; } // Simulate drop time Thread.sleep(600); // 1 game tick logger.debug("Dropped item: {}", item.getName()); return true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.warn("Item drop interrupted"); return false; } catch (Exception e) { logger.error("Error dropping item", e); return false; } }); } /** * Send a chat message. */ public void sendChatMessage(String message) { if (message == null || message.trim().isEmpty()) { logger.warn("Cannot send empty chat message"); return; } logger.debug("Sending chat message: {}", message); // TODO: Send chat packet to server // TODO: Handle chat message validation and formatting // For now, just log it logger.info("Chat: {}", message); } /** * Enable or disable run mode. */ public CompletableFuture setRunMode(boolean enabled) { logger.debug("Setting run mode: {}", enabled); return CompletableFuture.supplyAsync(() -> { try { // TODO: Send run mode packet to server // TODO: Update player state Thread.sleep(100); // Small delay for packet logger.debug("Run mode set to: {}", enabled); return true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.warn("Run mode change interrupted"); return false; } catch (Exception e) { logger.error("Error changing run mode", e); return false; } }); } }