Fix dashboard + vision: region checkboxes, upload auto-switch, state detection, analyze endpoint

- Region checkboxes now control which overlays are drawn (?regions= param)
- Upload auto-switches capture source (no restart needed)
- State detection: check in-game BEFORE loading, require 90% dark for loading
- Tighten HSV ranges: health H 0-30 S 50+ V 30+, mana H 200-240 S 40+ V 20+
- Add /api/analyze endpoint with per-region color analysis
This commit is contained in:
Hoid 2026-02-14 11:27:46 +00:00
parent 67f2e5536a
commit a665253d4d
5 changed files with 277 additions and 47 deletions

View file

@ -56,11 +56,11 @@ type Config struct {
func DefaultConfig() Config {
return Config{
Colors: Colors{
// Updated ranges based on real D2R screenshot analysis
// Health orb - includes the actual colors found (olive/brown when low, reds when high)
HealthFilled: HSVRange{0, 30, 10, 100, 255, 255}, // Wide range: reds through yellows/browns
// Mana orb - includes the actual colors found (dark/brown when low, blues when high)
ManaFilled: HSVRange{40, 20, 10, 250, 255, 255}, // Browns through blues
// Tightened HSV ranges based on real D2R screenshot calibration
// Health orb is RED: H 0-30 (covering red wrapping), S 50-255, V 30-255
HealthFilled: HSVRange{0, 50, 30, 30, 255, 255},
// Mana orb is BLUE: H 200-240, S 40-255, V 20-255
ManaFilled: HSVRange{200, 40, 20, 240, 255, 255},
ItemUnique: HSVRange{15, 100, 180, 30, 255, 255},
ItemSet: HSVRange{35, 100, 150, 55, 255, 255},
ItemRare: HSVRange{15, 50, 200, 25, 150, 255},

View file

@ -27,12 +27,19 @@ func NewDetector(config Config, services plugin.EngineServices) *Detector {
// DetectState analyzes a screenshot and returns the current game state.
func (d *Detector) DetectState(frame image.Image) plugin.GameState {
// Priority-based detection:
// 1. Check for loading screen
// 2. Check for main menu
// 3. Check for character select
// 4. Check for in-game (health orb visible)
// 1. Check for in-game first (health orb visible)
// 2. Check for loading screen (but only if not in-game)
// 3. Check for main menu
// 4. Check for character select
// 5. Check for death screen
if d.isInGame(frame) {
vitals := d.ReadVitals(frame)
if vitals.HealthPct <= 0.01 { // Consider very low health as dead
return plugin.StateDead
}
return plugin.StateInGame
}
if d.isLoading(frame) {
return plugin.StateLoading
}
@ -42,13 +49,6 @@ func (d *Detector) DetectState(frame image.Image) plugin.GameState {
if d.isCharacterSelect(frame) {
return plugin.StateCharacterSelect
}
if d.isInGame(frame) {
vitals := d.ReadVitals(frame)
if vitals.HealthPct <= 0.01 { // Consider very low health as dead
return plugin.StateDead
}
return plugin.StateInGame
}
return plugin.StateUnknown
}
@ -98,12 +98,12 @@ func (d *Detector) IsInGame(frame image.Image) bool {
}
func (d *Detector) isLoading(frame image.Image) bool {
// Check for loading screen by looking for mostly black screen
// This is a simple heuristic - could be improved
// Check for loading screen by looking for mostly black screen (>90%)
// AND ensuring no health orb colors are present in the health orb region
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 {
@ -112,7 +112,7 @@ func (d *Detector) isLoading(frame image.Image) bool {
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 {
@ -120,13 +120,35 @@ func (d *Detector) isLoading(frame image.Image) bool {
}
}
}
if totalPixels == 0 {
return false
}
// If more than 70% of screen is dark, likely loading
return float64(darkPixels)/float64(totalPixels) > 0.7
// Require more than 90% dark screen
darkPercentage := float64(darkPixels) / float64(totalPixels)
if darkPercentage <= 0.9 {
return false
}
// Additional check: ensure no health colors in health orb region
healthRegion := d.services.Region("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,
}
// If health colors are detected in health orb region, it's not loading
if d.vision.HasColorInRegion(frame, healthRegion, healthColor) {
return false
}
}
return true
}
func (d *Detector) isMainMenu(frame image.Image) bool {