Initial project structure: reusable isometric bot engine with D2R implementation

This commit is contained in:
Hoid 2026-02-14 08:50:36 +00:00
commit e0282a7111
44 changed files with 3433 additions and 0 deletions

View file

View file

@ -0,0 +1,89 @@
"""In-game state detection for D2R.
Detects health/mana, location, enemies, items on ground, etc.
"""
from typing import Optional, List, Tuple
import logging
import numpy as np
from engine.vision.color import ColorAnalyzer
from engine.vision.detector import ElementDetector, Detection
from games.d2r.config import D2RConfig
logger = logging.getLogger(__name__)
class InGameDetector:
"""Detects in-game state from screen captures."""
def __init__(self, config: D2RConfig):
self.config = config
self.color = ColorAnalyzer()
self.detector = ElementDetector()
def is_in_game(self, screen: np.ndarray) -> bool:
"""Check if we're in an active game (health/mana orbs visible)."""
health = self.get_health_percentage(screen)
return health > 0
def get_health_percentage(self, screen: np.ndarray) -> float:
"""Read current health from the health orb."""
return self.color.read_bar_percentage(
screen,
self.config.regions.health_orb,
self.config.colors.health_filled,
)
def get_mana_percentage(self, screen: np.ndarray) -> float:
"""Read current mana from the mana orb."""
return self.color.read_bar_percentage(
screen,
self.config.regions.mana_orb,
self.config.colors.mana_filled,
)
def is_dead(self, screen: np.ndarray) -> bool:
"""Check if character is dead."""
# Health at 0 + death screen elements
return self.get_health_percentage(screen) == 0
def should_use_health_potion(self, screen: np.ndarray) -> bool:
"""Check if health is below potion threshold."""
return self.get_health_percentage(screen) < self.config.health_potion_threshold
def should_chicken(self, screen: np.ndarray) -> bool:
"""Check if health is critically low (exit game)."""
return self.get_health_percentage(screen) < self.config.chicken_threshold
def find_items_on_ground(self, screen: np.ndarray) -> List[Detection]:
"""Detect item labels on the ground."""
items = []
if self.config.pickup_uniques:
items.extend(self.detector.find_by_color(
screen, *self.config.colors.item_unique,
min_area=50, label="unique",
))
if self.config.pickup_sets:
items.extend(self.detector.find_by_color(
screen, *self.config.colors.item_set,
min_area=50, label="set",
))
return items
def find_portal(self, screen: np.ndarray) -> Optional[Detection]:
"""Detect a town portal on screen."""
portals = self.detector.find_by_color(
screen, *self.config.colors.portal_blue,
min_area=200, label="portal",
)
return portals[0] if portals else None
def is_inventory_open(self, screen: np.ndarray) -> bool:
"""Check if the inventory panel is open."""
# TODO: Template match inventory panel
return False

View file

@ -0,0 +1,40 @@
"""Inventory management for D2R.
Handles inventory scanning, item identification, stash management.
"""
from typing import List, Optional, Tuple
import logging
import numpy as np
from engine.vision.detector import ElementDetector, Detection
from games.d2r.config import D2RConfig
logger = logging.getLogger(__name__)
class InventoryManager:
"""Manages inventory state and item operations."""
def __init__(self, config: D2RConfig):
self.config = config
self.detector = ElementDetector()
def is_full(self, screen: np.ndarray) -> bool:
"""Check if inventory is full."""
# TODO: Scan inventory grid for empty slots
return False
def find_empty_slot(self, screen: np.ndarray) -> Optional[Tuple[int, int]]:
"""Find an empty inventory slot."""
# TODO: Grid scanning
return None
def count_items(self, screen: np.ndarray) -> int:
"""Count items in inventory."""
return 0
def should_go_to_town(self, screen: np.ndarray) -> bool:
"""Check if inventory is full enough to warrant a town trip."""
return self.is_full(screen)

45
games/d2r/screens/menu.py Normal file
View file

@ -0,0 +1,45 @@
"""Main menu and character select screen detection for D2R."""
from typing import Optional
import logging
import numpy as np
from engine.vision.detector import ElementDetector, Detection
from games.d2r.config import D2RConfig
logger = logging.getLogger(__name__)
class MenuDetector:
"""Detects D2R menu screens (main menu, character select, lobby)."""
def __init__(self, config: D2RConfig):
self.config = config
self.detector = ElementDetector()
def is_main_menu(self, screen: np.ndarray) -> bool:
"""Check if we're on the main menu."""
# TODO: Template match for main menu elements
return False
def is_character_select(self, screen: np.ndarray) -> bool:
"""Check if we're on character select screen."""
return False
def is_lobby(self, screen: np.ndarray) -> bool:
"""Check if we're in the game lobby."""
return False
def is_loading(self, screen: np.ndarray) -> bool:
"""Check if a loading screen is active."""
return False
def select_character(self, screen: np.ndarray, char_index: int = 0) -> Optional[Detection]:
"""Find and return the character slot to click."""
# TODO: Detect character slots
return None
def find_create_game_button(self, screen: np.ndarray) -> Optional[Detection]:
"""Find the create game button."""
return None