package ui import ( "fmt" "image/color" "math" "github.com/hajimehoshi/ebiten/v2" "muhq.space/muhqs-game/go/assets" "muhq.space/muhqs-game/go/game" "muhq.space/muhqs-game/go/log" ) type CardGrid struct { WidgetBase cards game.PileOfCards columns, rows int scale float64 cardWidth, cardHeight float64 xOffset, yOffset int highlights map[*game.Card]color.Color grid *ebiten.Image gridCards int } func NewCardGrid(x, y, width, height int, scale float64, cards game.PileOfCards) *CardGrid { w := &CardGrid{ WidgetBase: NewWidgetBase(x, y, width, height), cards: cards, scale: scale, highlights: make(map[*game.Card]color.Color), } w.renderImpl = func() *ebiten.Image { return w.render() } return w } func (w *CardGrid) Columns(columns int) *CardGrid { if w.rows != 0 { log.Panicf("CardGrid can not have fixed columns and rows") } w.columns = columns return w } func (w *CardGrid) Rows(rows int) *CardGrid { if w.columns != 0 { log.Panicf("CardGrid can not have fixed columns and rows") } w.rows = rows return w } func (w *CardGrid) render() *ebiten.Image { img := ebiten.NewImage(w.Width, w.Height) cards := w.cards.Cards() nCards := len(cards) if nCards == 0 { return img } if nCards != w.gridCards { if w.cardWidth == 0 && w.cardHeight == 0 { cardImg := assets.GetCard(w.cards.Cards()[0].Path(), "en") b := cardImg.Bounds() // Fit the row height to the widgets height if w.rows == 1 && w.scale == -1 { w.scale = float64(w.Height) / float64(b.Dy()) } w.cardWidth = float64(b.Dx()) * w.scale w.cardHeight = float64(b.Dy()) * w.scale } var width, height int if w.rows != 0 { width = int(math.Ceil(float64(nCards)/float64(w.rows)) * w.cardWidth) height = int(float64(w.rows) * w.cardHeight) } else if w.columns != 0 { width = int(float64(w.columns) * w.cardWidth) height = int(math.Ceil(float64(nCards)/float64(w.columns)) * w.cardHeight) } else { log.Panicf("CardGrid must have have fixed columns or rows") } w.gridCards = nCards w.grid = ebiten.NewImage(width, height) log.Debug(fmt.Sprintf("create grid %d, %d\n", width, height)) // TODO: support big card grids xOffset, yOffset := 0.0, 0.0 for i, card := range cards { cardImg := assets.GetCard(card.Path(), "en") if cardImg == nil { log.Panicf("No image found for %s", card.Path()) } op := &ebiten.DrawImageOptions{} op.GeoM.Scale(w.scale, w.scale) op.GeoM.Translate(xOffset, yOffset) if c, found := w.highlights[card]; found { op.ColorScale.ScaleWithColor(c) } w.grid.DrawImage(cardImg, op) columns := w.columns if columns == 0 { columns = nCards / w.rows } if i != 0 && i%columns == columns-1 { xOffset = 0.0 yOffset += w.cardHeight } else { xOffset += w.cardWidth } } } op := &ebiten.DrawImageOptions{} op.GeoM.Translate(float64(w.xOffset), float64(w.yOffset)) img.DrawImage(w.grid, op) return img } func (w *CardGrid) FindObjectAt(_x, _y int) any { if !w.Contains(_x, _y) || w.grid == nil { return nil } x, y := _x-w.X-w.xOffset, _y-w.Y-w.yOffset gridBounds := w.grid.Bounds() if x > gridBounds.Dx() || y > gridBounds.Dy() { return nil } column := int(float64(x) / w.cardWidth) row := int(float64(y) / w.cardHeight) var i int if w.rows != 0 { i = row*w.cards.Size()/w.rows + column } else { i = row*w.columns + column } if i >= w.cards.Size() { return nil } return w.cards.Cards()[i] } func (w *CardGrid) forceRedrawGrid() { // Reset the grid card count to force grid to be redrawn containing highlight changes w.gridCards = 0 w.ForceRedraw() } func (w *CardGrid) setHighlights(cards []*game.Card, col color.Color) { highlights := make(map[*game.Card]color.Color) for _, c := range cards { highlights[c] = col } w.highlights = highlights w.forceRedrawGrid() } func (w *CardGrid) HighlightCard(card *game.Card, color color.Color) { w.setHighlights([]*game.Card{card}, color) } func (w *CardGrid) AddHighlightCard(card *game.Card, color color.Color) { log.Debug("Add card highlight", "card", card.Name, "pointer", fmt.Sprintf("%p@", card), "color", color) w.highlights[card] = color w.forceRedrawGrid() } func (w *CardGrid) ClearHighlights() { w.setHighlights([]*game.Card{}, nil) } func (*CardGrid) IsScrollable() bool { return true } func (w *CardGrid) Scroll(x, y int) { w.xOffset += x * 10 if w.xOffset > 0 { w.xOffset = 0 } w.yOffset += y * 10 if w.yOffset > 0 { w.yOffset = 0 } gridBounds := w.grid.Bounds() if w.xOffset < -gridBounds.Dx()+w.Width { w.xOffset = -gridBounds.Dx() + w.Width } if w.yOffset < -gridBounds.Dy()+w.Height { w.yOffset = -gridBounds.Dy() + w.Height } w.ForceRedraw() }