Fix prototype: calibrate vision for real D2R screenshots, implement orb detection, improve dashboard

- Created debug tool (cmd/debug/main.go) to analyze real D2R screenshots and calibrate HSV ranges
- Fixed HSV color ranges for health/mana orbs based on real screenshot analysis (99.5% and 82% detection rates)
- Replaced ReadBarPercentage with ReadOrbPercentage for circular orbs (not horizontal bars)
- Added SetSource() method to capture Manager for hot-swapping capture sources
- Fixed dashboard JavaScript null reference errors with proper array checks
- Improved dashboard refresh rate from 100ms to 1000ms for better performance
- Added proper error handling for empty/null API responses
- Successfully detecting game state, health (99.5%), and mana (82%) from real D2R screenshot
This commit is contained in:
Hoid 2026-02-14 10:55:30 +00:00
parent 4ebed5e3ab
commit 4f0b84ec31
10 changed files with 473 additions and 19 deletions

View file

@ -107,3 +107,23 @@ func (m *Manager) Source() Source {
func (m *Manager) Size() (width, height int) {
return m.source.Size()
}
// SetSource swaps the capture source.
// This is useful for development when uploading new screenshots.
func (m *Manager) SetSource(newSource Source) error {
// Close the old source
if m.source != nil {
if err := m.source.Close(); err != nil {
// Log but don't fail - we still want to switch sources
// log.Printf("Warning: failed to close old capture source: %v", err)
}
}
// Switch to new source
m.source = newSource
// Reset stats for the new source
m.stats = Stats{}
return nil
}

View file

@ -201,6 +201,14 @@ func (e *Engine) LootEngine() *loot.RuleEngine {
return e.lootEngine
}
// SetCaptureSource swaps the capture source (for development/testing).
func (e *Engine) SetCaptureSource(newSource capture.Source) error {
e.mu.Lock()
defer e.mu.Unlock()
return e.captureManager.SetSource(newSource)
}
// processFrame captures and analyzes a single frame.
func (e *Engine) processFrame() error {
// Capture frame

View file

@ -174,6 +174,38 @@ func (p *Pipeline) ReadBarPercentage(frame image.Image, barRegion image.Rectangl
return float64(filledPixels) / float64(totalPixels)
}
// ReadOrbPercentage reads a circular orb's fill level by sampling the entire region.
// This is better for D2R health/mana orbs which are circular, not horizontal bars.
func (p *Pipeline) ReadOrbPercentage(frame image.Image, orbRegion image.Rectangle, filledColor ColorRange) float64 {
bounds := orbRegion.Intersect(frame.Bounds())
if bounds.Empty() {
return 0.0
}
totalPixels := 0
filledPixels := 0
// Sample every pixel in the orb region
// For performance, we could sample every 2-3 pixels instead
step := 2 // Sample every 2nd pixel for performance
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)
hsv := RGBToHSV(c)
totalPixels++
if p.colorInRange(hsv, filledColor) {
filledPixels++
}
}
}
if totalPixels == 0 {
return 0.0
}
return float64(filledPixels) / float64(totalPixels)
}
// GetPixelColor returns the color at a specific pixel.
func (p *Pipeline) GetPixelColor(frame image.Image, x, y int) color.Color {
return frame.At(x, y)