package ui import ( "fmt" "image/color" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/inpututil" "github.com/hajimehoshi/ebiten/v2/text" "muhq.space/muhqs-game/go/font" "muhq.space/muhqs-game/go/game" ) var ( TEXTBOX_DEFAULT_BACKGROUND = color.Black TEXTBOX_DEFAULT_FOREGROUND = color.White TEXTBOX_DEFAULT_MARGIN = 3 TEXTBOX_AUTO_DIMENSION = -1 ) type TextBox struct { WidgetBase text string fg color.Color bg color.Color XMargin int YMargin int center bool } func NewFixedTextBox(x, y int, width, height int, t string) *TextBox { tb := &TextBox{ WidgetBase: NewWidgetBase(x, y, width, height), text: t, fg: TEXTBOX_DEFAULT_FOREGROUND, bg: TEXTBOX_DEFAULT_BACKGROUND, XMargin: TEXTBOX_DEFAULT_MARGIN, YMargin: TEXTBOX_DEFAULT_MARGIN, } tb.renderImpl = func() *ebiten.Image { return tb.render() } return tb } func NewAutoTextBox(x, y int, text string) *TextBox { return NewFixedTextBox(x, y, TEXTBOX_AUTO_DIMENSION, TEXTBOX_AUTO_DIMENSION, text) } func (tb *TextBox) Bg(bg color.Color) *TextBox { tb.bg = bg return tb } func (tb *TextBox) Fg(fg color.Color) *TextBox { tb.fg = fg return tb } func (tb *TextBox) Centering(centering bool) *TextBox { tb.center = centering return tb } func NewUnitInfo(x, y int, u *game.Unit) *TextBox { info := fmt.Sprintf("%s\nDamage: %d\nHealth: %d\nUpkeep: %d", u.String(), u.Damage(), u.Health, u.UpkeepCost()) if u.Movement != game.INVALID_MOVEMENT() { info = fmt.Sprintf("%s\nMovement: %s", info, u.Movement.String()) } if !game.INVALID_ATTACK(u.Attack) { info = fmt.Sprintf("%s\nAttack: %s", info, u.Attack.String()) } if len(u.Pile()) > 0 { info = fmt.Sprintf("%s\nPile:", info) for _, p := range u.Pile() { info = fmt.Sprintf("%s\n %s", info, p.Card().Name) } } return NewAutoTextBox(x, y, info) } func NewGenericPermInfo(x, y int, p game.Permanent) *TextBox { info := fmt.Sprintf("%s\nDamage: %d", p.String(), p.Damage()) return NewAutoTextBox(x, y, info) } func NewPermInfo(x, y int, p game.Permanent) *TextBox { switch p := p.(type) { case *game.Unit: return NewUnitInfo(x, y, p) default: return NewGenericPermInfo(x, y, p) } } func (tb *TextBox) render() *ebiten.Image { var x, y int b := text.BoundString(font.Font24, tb.text) if tb.Width == -1 || tb.Height == -1 { tb.Width = b.Dx() + 2*tb.XMargin tb.Height = b.Dy() + 2*tb.YMargin x = tb.XMargin + b.Min.X y = tb.YMargin - b.Min.Y } else { if !tb.center { x = tb.XMargin + b.Min.X } else { x = tb.Width/2 - centerTextXOffset(b) } y = tb.Height/2 - centerTextYOffset(b) } img := ebiten.NewImage(tb.Width, tb.Height) img.Fill(tb.bg) text.Draw(img, tb.text, font.Font24, x, y, tb.fg) return img } type PocList struct { TextBox hoverCardView centerX int bottomY int poc game.PileOfCards } func NewPocList(centerX, bottomY int, poc game.PileOfCards) *PocList { w := &PocList{ TextBox: *(NewAutoTextBox(-1, -1, "").Centering(true)), centerX: centerX, bottomY: bottomY, poc: poc, } w.hoverCardView.init(w) w.renderImpl = func() *ebiten.Image { w.setText() return w.render() } return w } func (w *PocList) setText() { w.Width, w.Height = -1, -1 t := "" for _, c := range w.poc.Cards() { t = fmt.Sprintf("%s%s\n", t, c.Name) } if t != "" { w.text = t[:len(t)-1] } else { w.text = "No cards" } b := text.BoundString(font.Font24, w.text) w.X = w.centerX - b.Dx()/2 - w.XMargin w.Y = w.bottomY - b.Dy() - w.YMargin } func (w *PocList) FindObjectAt(_x, _y int) interface{} { if !w.Contains(_x, _y) { return nil } cards := w.poc.Cards() if len(cards) == 0 { return nil } y := _y - w.Y b := text.BoundString(font.Font24, w.text) i := y / (b.Dy() / w.poc.Size()) if i >= len(cards) { return nil } return cards[i] } type TextInput struct { TextBox input string label string } func NewTextInput(x, y int, width, height int, label string) *TextInput { w := &TextInput{ TextBox: *(NewFixedTextBox(x, y, width, height, "").Centering(true)), input: "", label: label, } w.renderImpl = func() *ebiten.Image { if w.input == "" { w.text = w.label } else { w.text = w.input } return w.render() } return w } func (ti *TextInput) SetInput(input string) { ti.input = input ti.ForceRedraw() } func (ti *TextInput) AddInput(input []rune) { ti.SetInput(ti.input + string(input)) } func (ti *TextInput) HandleKey() { if inpututil.IsKeyJustPressed(ebiten.KeyBackspace) { if len(ti.input) > 0 { ti.input = ti.input[:len(ti.input)-1] ti.ForceRedraw() } } } func (ti *TextInput) Text() string { return ti.input }