Some checks failed
Release / release (push) Has been cancelled
Full-stack dashboard for controlling, automating, and analyzing Artifacts MMO characters via the game's HTTP API. Backend (FastAPI): - Async Artifacts API client with rate limiting and retry - 6 automation strategies (combat, gathering, crafting, trading, task, leveling) - Automation engine with runner, manager, cooldown tracker, pathfinder - WebSocket relay (game server -> frontend) - Game data cache, character snapshots, price history, analytics - 9 API routers, 7 database tables, 3 Alembic migrations - 108 unit tests Frontend (Next.js 15 + shadcn/ui): - Live character dashboard with HP/XP bars and cooldowns - Character detail with stats, equipment, inventory, skills, manual actions - Automation management with live log streaming - Interactive canvas map with content-type coloring and zoom/pan - Bank management, Grand Exchange with price charts - Events, logs, analytics pages with Recharts - WebSocket auto-reconnect with query cache invalidation - Settings page, error boundaries, dark theme Infrastructure: - Docker Compose (dev + prod) - GitHub Actions CI/CD - Documentation (Architecture, Automation, Deployment, API)
77 lines
2.5 KiB
Python
77 lines
2.5 KiB
Python
import logging
|
|
|
|
from app.engine.strategies.base import ActionPlan, ActionType
|
|
from app.schemas.game import CharacterSchema
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class HealPolicy:
|
|
"""Encapsulates healing decision logic.
|
|
|
|
Used by strategies to determine *if* and *how* a character should heal.
|
|
"""
|
|
|
|
@staticmethod
|
|
def should_heal(character: CharacterSchema, threshold: int) -> bool:
|
|
"""Return ``True`` if the character's HP is below *threshold* percent.
|
|
|
|
Parameters
|
|
----------
|
|
character:
|
|
The character to evaluate.
|
|
threshold:
|
|
HP percentage (0-100) below which healing is recommended.
|
|
"""
|
|
if character.max_hp == 0:
|
|
return False
|
|
hp_pct = (character.hp / character.max_hp) * 100.0
|
|
return hp_pct < threshold
|
|
|
|
@staticmethod
|
|
def is_full_health(character: CharacterSchema) -> bool:
|
|
"""Return ``True`` if the character is at maximum HP."""
|
|
return character.hp >= character.max_hp
|
|
|
|
@staticmethod
|
|
def choose_heal_method(character: CharacterSchema, config: dict) -> ActionPlan:
|
|
"""Decide between resting and using a consumable.
|
|
|
|
Parameters
|
|
----------
|
|
character:
|
|
Current character state.
|
|
config:
|
|
Strategy config dict containing ``heal_method``,
|
|
``consumable_code``, etc.
|
|
|
|
Returns
|
|
-------
|
|
An :class:`ActionPlan` for the chosen healing action.
|
|
"""
|
|
heal_method = config.get("heal_method", "rest")
|
|
consumable_code: str | None = config.get("consumable_code")
|
|
|
|
if heal_method == "consumable" and consumable_code:
|
|
# Verify the character actually has the consumable
|
|
has_item = any(
|
|
slot.code == consumable_code for slot in character.inventory
|
|
)
|
|
if has_item:
|
|
return ActionPlan(
|
|
ActionType.USE_ITEM,
|
|
params={"code": consumable_code, "quantity": 1},
|
|
reason=f"Using {consumable_code} to heal ({character.hp}/{character.max_hp} HP)",
|
|
)
|
|
else:
|
|
logger.info(
|
|
"Consumable %s not in inventory for %s, falling back to rest",
|
|
consumable_code,
|
|
character.name,
|
|
)
|
|
|
|
# Default / fallback: rest
|
|
return ActionPlan(
|
|
ActionType.REST,
|
|
reason=f"Resting to heal ({character.hp}/{character.max_hp} HP)",
|
|
)
|