package hud import ( "math" "github.com/chompy/roguelike_rpg/internal/client/gfx" "github.com/hajimehoshi/ebiten/v2" ) const targetYOffset = 8 type TargetSelectionType int const ( TargetSingle TargetSelectionType = iota TargetTeam TargetAll ) type Target struct { ID int X int Y int Team int } type TargetSelector struct { BaseElement SelectionType TargetSelectionType Active bool targets []*Target index int gfx *gfx.Context cursorSprite *gfx.Sprite } func NewTargetSelector(gfx *gfx.Context) (*TargetSelector, error) { cursorSprite, err := gfx.SpriteLibrary.GetFirst("hud.dialog.cursor") if err != nil { return nil, err } targetSelector := &TargetSelector{gfx: gfx, cursorSprite: cursorSprite} targetSelector.Reset() return targetSelector, nil } func (t *TargetSelector) Reset() { t.targets = make([]*Target, 0) t.index = 0 } func (t *TargetSelector) Add(id, x, y, team int) { cw, _ := t.cursorSprite.TileSize() t.targets = append(t.targets, &Target{ID: id, X: x + (cw), Y: y + targetYOffset, Team: team}) } func (t *TargetSelector) Index() int { return t.index } func (t *TargetSelector) SetIndex(index int) { t.index = max(0, min(len(t.targets)-1, index)) } func (t *TargetSelector) GetCurrentTarget() *Target { if len(t.targets) > 0 { return t.targets[t.Index()] } return nil } func (t *TargetSelector) GetCurrentTargets() []*Target { if len(t.targets) > 0 { switch t.SelectionType { case TargetSingle: target := t.GetCurrentTarget() if target != nil { return []*Target{target} } case TargetTeam: targets := make([]*Target, 0) currentIndexTarget := t.GetCurrentTarget() for _, target := range t.targets { if target.Team == currentIndexTarget.Team { targets = append(targets, target) } } return targets case TargetAll: return t.targets } } return []*Target{} } func (t *TargetSelector) Input(input HudInput) { switch t.SelectionType { case TargetSingle: currentTarget := t.GetCurrentTarget() if currentTarget == nil { return } bestDist := math.MaxFloat64 bestIndex := -1 dx, dy := 0, 0 switch input { case InputUp: dy = -1 case InputDown: dy = 1 case InputLeft: dx = -1 case InputRight: dx = 1 } for i, target := range t.targets { if i == t.index { continue } relX := target.X - currentTarget.X relY := target.Y - currentTarget.Y if (dx != 0 && (relX*dx) <= 0) || (dy != 0 && (relY*dy) <= 0) { continue } dist := float64(relX*relX + relY*relY) if dist < bestDist { bestDist = dist bestIndex = i } } if bestIndex != -1 { t.SetIndex(bestIndex) } case TargetTeam: if t.index == 0 { t.SetIndex(999) } else { t.SetIndex(0) } } } func (t *TargetSelector) Draw(time float32, screen *ebiten.Image) { if t.Active { targets := t.GetCurrentTargets() for _, target := range targets { op := &ebiten.DrawImageOptions{} op.GeoM.Rotate(math.Pi * .5) op.GeoM.Translate(float64(target.X), float64(target.Y)) t.cursorSprite.Draw(time, screen, op) } } }