package com.openosrs.client.api; import com.openosrs.client.core.ClientCore; import com.openosrs.client.engine.GameEngine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.concurrent.CompletableFuture; /** * CombatAPI - Handles combat-related actions and state. * * This API module provides methods for: * - Combat actions (attacking, eating, drinking potions) * - Combat state monitoring * - Health and prayer management * - Combat calculations and utilities */ public class CombatAPI { private static final Logger logger = LoggerFactory.getLogger(CombatAPI.class); private final ClientCore clientCore; private final GameEngine gameEngine; \n // Common food item IDs (for auto-eating)\n private static final int[] FOOD_IDS = {\n 385, // Shark\n 379, // Lobster\n 373, // Swordfish\n 365, // Bass\n 361, // Tuna\n 329, // Salmon\n 315, // Shrimps\n 7946, // Monkfish\n 3144, // Karambwan\n 6297, // Anglerfish\n 385, // Shark\n 12625, // Saradomin brew\n };\n \n // Common potion IDs\n private static final int[] COMBAT_POTIONS = {\n 2428, // Attack potion(4)\n 113, // Strength potion(4)\n 2440, // Super attack(4)\n 2442, // Super strength(4)\n 2436, // Super defence(4)\n 2444, // Ranging potion(4)\n 3024, // Super energy(4)\n 3016, // Energy potion(4)\n 2434, // Prayer potion(4)\n 12625, // Saradomin brew(4)\n 12631, // Super restore(4)\n };\n \n public CombatAPI(ClientCore clientCore, GameEngine gameEngine) {\n this.clientCore = clientCore;\n this.gameEngine = gameEngine;\n }\n \n /**\n * Attack an NPC.\n */\n public CompletableFuture attackNPC(NPC npc) {\n if (npc == null) {\n return CompletableFuture.completedFuture(false);\n }\n \n logger.debug(\"Attacking NPC: {} (ID: {})\", npc.getName(), npc.getId());\n \n return CompletableFuture.supplyAsync(() -> {\n try {\n // Check if NPC is attackable\n if (!npc.isInteractable()) {\n logger.warn(\"NPC {} is not attackable\", npc.getName());\n return false;\n }\n \n Position playerPos = clientCore.getPlayerState().getPosition();\n if (playerPos == null) {\n logger.warn(\"Cannot attack - player position unknown\");\n return false;\n }\n \n double distance = npc.distanceToPlayer(playerPos);\n if (distance > 10) {\n logger.warn(\"NPC too far away for attack: {} tiles\", distance);\n return false;\n }\n \n // Check if already in combat\n if (clientCore.getPlayerState().isInCombat()) {\n logger.debug(\"Already in combat, switching targets\");\n }\n \n // TODO: Send attack packet to server\n // TODO: Update combat state\n \n // Simulate attack time\n Thread.sleep(600); // 1 game tick\n \n logger.debug(\"Attack initiated on NPC: {}\", npc.getName());\n return true;\n \n } catch (InterruptedException e) {\n Thread.currentThread().interrupt();\n logger.warn(\"Attack interrupted\");\n return false;\n } catch (Exception e) {\n logger.error(\"Error during attack\", e);\n return false;\n }\n });\n }\n \n /**\n * Eat food from inventory (automatically finds food).\n */\n public CompletableFuture eatFood() {\n logger.debug(\"Attempting to eat food\");\n \n return CompletableFuture.supplyAsync(() -> {\n try {\n // Find food in inventory\n Item[] inventory = clientCore.getInventoryState().getInventory();\n \n for (int slot = 0; slot < inventory.length; slot++) {\n Item item = inventory[slot];\n if (item != null && !item.isEmpty() && isFood(item.getItemId())) {\n logger.debug(\"Eating food: {}\", item.getName());\n \n // TODO: Send eat packet to server\n // TODO: Update player health\n \n // Simulate eating time\n Thread.sleep(1800); // 3 game ticks\n \n logger.debug(\"Food consumed: {}\", item.getName());\n return true;\n }\n }\n \n logger.warn(\"No food found in inventory\");\n return false;\n \n } catch (InterruptedException e) {\n Thread.currentThread().interrupt();\n logger.warn(\"Eating interrupted\");\n return false;\n } catch (Exception e) {\n logger.error(\"Error eating food\", e);\n return false;\n }\n });\n }\n \n /**\n * Drink a specific potion.\n */\n public CompletableFuture drinkPotion(int itemId) {\n logger.debug(\"Attempting to drink potion: {}\", itemId);\n \n return CompletableFuture.supplyAsync(() -> {\n try {\n // Find potion in inventory\n int slot = clientCore.getInventoryState().findItemSlot(itemId);\n if (slot == -1) {\n logger.warn(\"Potion {} not found in inventory\", itemId);\n return false;\n }\n \n Item potion = clientCore.getInventoryState().getInventorySlot(slot);\n logger.debug(\"Drinking potion: {}\", potion.getName());\n \n // TODO: Send drink packet to server\n // TODO: Update player stats based on potion effect\n \n // Simulate drinking time\n Thread.sleep(1800); // 3 game ticks\n \n logger.debug(\"Potion consumed: {}\", potion.getName());\n return true;\n \n } catch (InterruptedException e) {\n Thread.currentThread().interrupt();\n logger.warn(\"Drinking interrupted\");\n return false;\n } catch (Exception e) {\n logger.error(\"Error drinking potion\", e);\n return false;\n }\n });\n }\n \n /**\n * Get current combat target.\n */\n public NPC getCombatTarget() {\n // TODO: Track current combat target\n // For now, return null as we don't have combat state tracking yet\n return null;\n }\n \n /**\n * Check if the player should eat (low health).\n */\n public boolean shouldEat() {\n int currentHp = clientCore.getPlayerState().getSkillLevel(AgentAPI.Skill.HITPOINTS);\n int maxHp = clientCore.getPlayerState().getSkillRealLevel(AgentAPI.Skill.HITPOINTS);\n \n double healthPercentage = (double) currentHp / maxHp;\n return healthPercentage < 0.6; // Eat when below 60% health\n }\n \n /**\n * Check if the player should drink a prayer potion.\n */\n public boolean shouldDrinkPrayerPotion() {\n int currentPrayer = clientCore.getPlayerState().getSkillLevel(AgentAPI.Skill.PRAYER);\n int maxPrayer = clientCore.getPlayerState().getSkillRealLevel(AgentAPI.Skill.PRAYER);\n \n double prayerPercentage = (double) currentPrayer / maxPrayer;\n return prayerPercentage < 0.25; // Drink when below 25% prayer\n }\n \n /**\n * Automatically handle combat (eat food, drink potions if needed).\n */\n public CompletableFuture autoManageCombat() {\n return CompletableFuture.supplyAsync(() -> {\n try {\n boolean actionTaken = false;\n \n // Check if we need to eat\n if (shouldEat()) {\n logger.debug(\"Health low, attempting to eat\");\n if (eatFood().get()) {\n actionTaken = true;\n }\n }\n \n // Check if we need prayer\n if (shouldDrinkPrayerPotion()) {\n logger.debug(\"Prayer low, attempting to drink prayer potion\");\n // Try to drink prayer potion (4)\n if (drinkPotion(2434).get()) {\n actionTaken = true;\n }\n }\n \n return actionTaken;\n \n } catch (Exception e) {\n logger.error(\"Error during auto combat management\", e);\n return false;\n }\n });\n }\n \n /**\n * Check if an item ID is food.\n */\n private boolean isFood(int itemId) {\n return Arrays.stream(FOOD_IDS).anyMatch(foodId -> foodId == itemId);\n }\n \n /**\n * Check if an item ID is a combat potion.\n */\n private boolean isCombatPotion(int itemId) {\n return Arrays.stream(COMBAT_POTIONS).anyMatch(potionId -> potionId == itemId);\n }\n \n /**\n * Calculate the player's max hit (simplified).\n */\n public int calculateMaxHit() {\n // TODO: Implement proper max hit calculation\n // This would need to consider strength level, equipment, potions, etc.\n int strLevel = clientCore.getPlayerState().getSkillLevel(AgentAPI.Skill.STRENGTH);\n return Math.max(1, strLevel / 10); // Very simplified calculation\n }\n \n /**\n * Calculate combat effectiveness against an NPC.\n */\n public double calculateCombatEffectiveness(NPC npc) {\n if (npc == null) {\n return 0.0;\n }\n \n // TODO: Implement proper effectiveness calculation\n // This would consider combat levels, equipment, NPC defence, etc.\n int playerCombatLevel = clientCore.getPlayerState().getCombatLevel();\n int npcCombatLevel = npc.getCombatLevel();\n \n if (npcCombatLevel == 0) {\n return 1.0; // Non-combat NPC\n }\n \n return Math.min(1.0, (double) playerCombatLevel / npcCombatLevel);\n }\n \n /**\n * Check if it's safe to engage in combat.\n */\n public boolean isSafeToCombat() {\n // Check health\n if (shouldEat() && !hasFood()) {\n logger.warn(\"Not safe to combat - low health and no food\");\n return false;\n }\n \n // Check if already in dangerous combat\n int currentHp = clientCore.getPlayerState().getSkillLevel(AgentAPI.Skill.HITPOINTS);\n if (currentHp <= 20 && clientCore.getPlayerState().isInCombat()) {\n logger.warn(\"Not safe to combat - critical health in combat\");\n return false;\n }\n \n return true;\n }\n \n /**\n * Check if the player has food in inventory.\n */\n public boolean hasFood() {\n Item[] inventory = clientCore.getInventoryState().getInventory();\n \n for (Item item : inventory) {\n if (item != null && !item.isEmpty() && isFood(item.getItemId())) {\n return true;\n }\n }\n \n return false;\n }\n \n /**\n * Get the amount of food in inventory.\n */\n public int getFoodCount() {\n Item[] inventory = clientCore.getInventoryState().getInventory();\n int count = 0;\n \n for (Item item : inventory) {\n if (item != null && !item.isEmpty() && isFood(item.getItemId())) {\n count += item.getQuantity();\n }\n }\n \n return count;\n }\n}"