Working prototype with dev dashboard

- Created core Engine that coordinates all subsystems
- Extended API with comprehensive endpoints for dev dashboard
- Implemented pure Go vision processing (no GoCV dependency)
- Built full-featured dev dashboard with live capture viewer, region overlays, pixel inspector
- Added D2R detector with actual health/mana reading from orb regions
- Fixed resolution profile registration and region validation
- Generated synthetic test data for development
- Added dev mode support with file backend for testing
- Fixed build tag issues for cross-platform compilation

Prototype features:
 Live capture viewer with region overlays
 Real-time state detection (game state, health %, mana %)
 Pixel inspector (hover for RGB/HSV values)
 Capture stats monitoring (FPS, frame count)
 Region management with toggle visibility
 File upload for testing screenshots
 Dark theme dev-focused UI
 CORS enabled for dev convenience

Ready for: go run ./cmd/iso-bot --dev --api :8080
This commit is contained in:
Hoid 2026-02-14 10:23:31 +00:00
parent 80ba9b1b90
commit 6a9562c406
10 changed files with 1884 additions and 62 deletions

View file

@ -99,8 +99,8 @@ func RegisterProfiles(registry *resolution.Registry) error {
"minimap": image.Rect(1600, 0, 1920, 320),
"inventory": image.Rect(960, 330, 1490, 770),
"stash": image.Rect(430, 330, 960, 770),
"skill_left": image.Rect(194, 1036, 246, 1088),
"skill_right": image.Rect(1674, 1036, 1726, 1088),
"skill_left": image.Rect(194, 1030, 246, 1078),
"skill_right": image.Rect(1674, 1030, 1726, 1078),
},
},
// 1280x720 (720p) - Secondary resolution
@ -116,8 +116,8 @@ func RegisterProfiles(registry *resolution.Registry) error {
"minimap": image.Rect(1067, 0, 1280, 213),
"inventory": image.Rect(640, 220, 993, 513),
"stash": image.Rect(287, 220, 640, 513),
"skill_left": image.Rect(129, 691, 164, 726),
"skill_right": image.Rect(1116, 691, 1151, 726),
"skill_left": image.Rect(129, 685, 164, 718),
"skill_right": image.Rect(1116, 685, 1151, 718),
},
},
}

View file

@ -5,12 +5,14 @@ import (
"image"
"git.cloonar.com/openclawd/iso-bot/pkg/plugin"
"git.cloonar.com/openclawd/iso-bot/pkg/engine/vision"
)
// Detector implements plugin.GameDetector for D2R.
type Detector struct {
config Config
services plugin.EngineServices
vision *vision.Pipeline
}
// NewDetector creates a D2R state detector.
@ -18,6 +20,7 @@ func NewDetector(config Config, services plugin.EngineServices) *Detector {
return &Detector{
config: config,
services: services,
vision: vision.NewPipeline(0.8), // 80% confidence threshold
}
}
@ -41,7 +44,7 @@ func (d *Detector) DetectState(frame image.Image) plugin.GameState {
}
if d.isInGame(frame) {
vitals := d.ReadVitals(frame)
if vitals.HealthPct == 0 {
if vitals.HealthPct <= 0.01 { // Consider very low health as dead
return plugin.StateDead
}
return plugin.StateInGame
@ -51,15 +54,42 @@ func (d *Detector) DetectState(frame image.Image) plugin.GameState {
// ReadVitals reads health and mana from the orbs.
func (d *Detector) ReadVitals(frame image.Image) plugin.VitalStats {
// TODO: Analyze health/mana orb regions using color detection
// Get region coordinates from the engine services
healthRegion := d.services.Region("health_orb")
manaRegion := d.services.Region("mana_orb")
_ = healthRegion // Use these regions for color analysis
_ = manaRegion
var healthPct, manaPct float64
return plugin.VitalStats{}
// Read health percentage from red-filled pixels in health orb
if !healthRegion.Empty() {
healthColor := vision.ColorRange{
LowerH: d.config.Colors.HealthFilled.LowerH,
UpperH: d.config.Colors.HealthFilled.UpperH,
LowerS: d.config.Colors.HealthFilled.LowerS,
UpperS: d.config.Colors.HealthFilled.UpperS,
LowerV: d.config.Colors.HealthFilled.LowerV,
UpperV: d.config.Colors.HealthFilled.UpperV,
}
healthPct = d.vision.ReadBarPercentage(frame, healthRegion, healthColor)
}
// Read mana percentage from blue-filled pixels in mana orb
if !manaRegion.Empty() {
manaColor := vision.ColorRange{
LowerH: d.config.Colors.ManaFilled.LowerH,
UpperH: d.config.Colors.ManaFilled.UpperH,
LowerS: d.config.Colors.ManaFilled.LowerS,
UpperS: d.config.Colors.ManaFilled.UpperS,
LowerV: d.config.Colors.ManaFilled.LowerV,
UpperV: d.config.Colors.ManaFilled.UpperV,
}
manaPct = d.vision.ReadBarPercentage(frame, manaRegion, manaColor)
}
return plugin.VitalStats{
HealthPct: healthPct,
ManaPct: manaPct,
XPPct: 0.0, // TODO: Implement XP bar reading
}
}
// IsInGame returns true if health orb is visible.
@ -68,21 +98,64 @@ func (d *Detector) IsInGame(frame image.Image) bool {
}
func (d *Detector) isLoading(frame image.Image) bool {
// TODO: Check for loading screen (mostly black with loading bar)
return false
// Check for loading screen by looking for mostly black screen
// This is a simple heuristic - could be improved
bounds := frame.Bounds()
totalPixels := 0
darkPixels := 0
// Sample every 10 pixels for performance
step := 10
for y := bounds.Min.Y; y < bounds.Max.Y; y += step {
for x := bounds.Min.X; x < bounds.Max.X; x += step {
c := frame.At(x, y)
r, g, b, _ := c.RGBA()
// Convert to 8-bit
r, g, b = r>>8, g>>8, b>>8
totalPixels++
// Consider pixel dark if all channels are below 30
if r < 30 && g < 30 && b < 30 {
darkPixels++
}
}
}
if totalPixels == 0 {
return false
}
// If more than 70% of screen is dark, likely loading
return float64(darkPixels)/float64(totalPixels) > 0.7
}
func (d *Detector) isMainMenu(frame image.Image) bool {
// TODO: Template match main menu elements
// TODO: Template match main menu elements or check for specific colors/text
// For now, this is a placeholder
return false
}
func (d *Detector) isCharacterSelect(frame image.Image) bool {
// TODO: Template match character select screen
// For now, this is a placeholder
return false
}
func (d *Detector) isInGame(frame image.Image) bool {
// TODO: Check if health orb region contains red pixels
return false
// Check if health orb region contains red pixels indicating health
healthRegion := d.services.Region("health_orb")
if healthRegion.Empty() {
return false
}
healthColor := vision.ColorRange{
LowerH: d.config.Colors.HealthFilled.LowerH,
UpperH: d.config.Colors.HealthFilled.UpperH,
LowerS: d.config.Colors.HealthFilled.LowerS,
UpperS: d.config.Colors.HealthFilled.UpperS,
LowerV: d.config.Colors.HealthFilled.LowerV,
UpperV: d.config.Colors.HealthFilled.UpperV,
}
return d.vision.HasColorInRegion(frame, healthRegion, healthColor)
}