diff --git a/ui/components/artifact.go b/ui/components/artifact.go index 6bad55f..54029d9 100644 --- a/ui/components/artifact.go +++ b/ui/components/artifact.go @@ -13,11 +13,11 @@ var ( artifactStyle = lipgloss.NewStyle().Padding(1, 2).Margin(0, 2) ) -func ArtifactCard(session *game.Session, guid string, baseHeight int, maxHeight int, optionalWidth ...int) string { +func ArtifactCard(session *game.Session, guid string, baseHeight int, width int) string { art, _ := session.GetArtifact(guid) - width := 30 - if len(optionalWidth) > 0 { - width = optionalWidth[0] + + if width <= 0 { + width = 30 } artifactStyle := artifactStyle.Copy(). diff --git a/ui/components/card.go b/ui/components/card.go index 1f3077d..53e98e2 100644 --- a/ui/components/card.go +++ b/ui/components/card.go @@ -17,10 +17,10 @@ var ( cantCastStyle = lipgloss.NewStyle().Foreground(style.BaseRed) ) -func HalfCard(session *game.Session, guid string, active bool, baseHeight int, maxHeight int, minimal bool, optionalWidth ...int) string { +func HalfCard(session *game.Session, guid string, active bool, baseHeight int, maxHeight int, minimal bool, width int, checkCasting bool) string { fight := session.GetFight() card, _ := session.GetCard(guid) - canCast := fight.CurrentPoints >= card.PointCost + canCast := !checkCasting || fight.CurrentPoints >= card.PointCost cardState := session.GetCardState(guid) pointText := strings.Repeat("•", card.PointCost) @@ -29,9 +29,8 @@ func HalfCard(session *game.Session, guid string, active bool, baseHeight int, m cardCol, _ := colorful.Hex(card.Color) bgCol, _ := colorful.MakeColor(style.BaseGrayDarker) - width := 30 - if len(optionalWidth) > 0 { - width = optionalWidth[0] + if width <= 0 { + width = 30 } cardStyle := cardStyle.Copy(). diff --git a/ui/menu.go b/ui/menu.go index a353a7b..769c7cc 100644 --- a/ui/menu.go +++ b/ui/menu.go @@ -1,9 +1,14 @@ package ui -import tea "github.com/charmbracelet/bubbletea" +import ( + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + zone "github.com/lrstanley/bubblezone" +) // Menu is a tea.Model that keeps track of its size. It is intended to // trigger the re-distribution of the tea.WindowSizeMsg for nested models. +// It also contains some common functionality for menus. type Menu interface { tea.Model HasSize() bool @@ -13,9 +18,56 @@ type Menu interface { type MenuBase struct { Size tea.WindowSizeMsg LastMouse tea.MouseMsg + Zones *zone.Manager +} + +// NewMenuBase returns a new MenuBase. +func NewMenuBase() MenuBase { + return MenuBase{} +} + +// WithSize returns a new MenuBase with the given size. +func (m MenuBase) WithSize(size tea.WindowSizeMsg) MenuBase { + m.Size = size + return m +} + +// WithZones returns a new MenuBase with the given zone manager. +func (m MenuBase) WithZones(zones *zone.Manager) MenuBase { + m.Zones = zones + return m } // HasSize returns true if the menu has a saved size. func (m MenuBase) HasSize() bool { return m.Size.Width > 0 } + +// ZoneBackground returns the background color for a zone. +func (m MenuBase) ZoneBackground(zone string, hover lipgloss.Color, normal lipgloss.Color) lipgloss.Color { + if m.Zones == nil { + return normal + } + + if m.Zones.Get(zone).InBounds(m.LastMouse) { + return hover + } + + return normal +} + +// ZoneInBounds returns true if the last mouse position is in the bounds of the given zone. +func (m MenuBase) ZoneInBounds(zone string) bool { + if m.Zones == nil { + return false + } + return m.Zones.Get(zone).InBounds(m.LastMouse) +} + +// ZoneMark returns the given string marked with the given zone. +func (m MenuBase) ZoneMark(zone string, str string) string { + if m.Zones == nil { + return str + } + return m.Zones.Mark(zone, str) +} diff --git a/ui/menus/carousel/carousel.go b/ui/menus/carousel/carousel.go index 40f7248..e4c807c 100644 --- a/ui/menus/carousel/carousel.go +++ b/ui/menus/carousel/carousel.go @@ -20,22 +20,20 @@ const ( type Model struct { ui.MenuBase - zones *zone.Manager - parent tea.Model - lastMouse tea.MouseMsg - title string - items []string - selected int + parent tea.Model + title string + items []string + selected int onceFn func() } func New(parent tea.Model, zones *zone.Manager, title string, items []string) Model { return Model{ - zones: zones, - parent: parent, - title: title, - items: items, + MenuBase: ui.NewMenuBase().WithZones(zones), + parent: parent, + title: title, + items: items, } } @@ -48,9 +46,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.WindowSizeMsg: m.Size = msg case tea.KeyMsg: - if msg.Type == tea.KeyEscape { - return m.parent, nil - } else if msg.Type == tea.KeyEnter { + if msg.Type == tea.KeyEnter { + audio.Play("btn_menu") return m.parent, nil } else if msg.Type == tea.KeyLeft { if m.selected > 0 { @@ -65,20 +62,20 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case tea.MouseMsg: m.LastMouse = msg - if msg.Type == tea.MouseLeft { - if m.zones.Get(ZoneLeftButton).InBounds(msg) { + if msg.Button != tea.MouseButtonNone { + if m.ZoneInBounds(ZoneLeftButton) { if m.selected > 0 { m.selected-- audio.Play("btn_menu") } } - if m.zones.Get(ZoneLeftButton).InBounds(msg) { + if m.ZoneInBounds(ZoneRightButton) { if m.selected < len(m.items)-1 { m.selected++ audio.Play("btn_menu") } } - if m.zones.Get(ZoneDoneButton).InBounds(msg) { + if m.ZoneInBounds(ZoneDoneButton) { audio.Play("btn_menu") return m.parent, nil } @@ -88,15 +85,31 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil } +func (m Model) leftButton() string { + background := m.ZoneBackground(ZoneLeftButton, style.BaseRed, style.BaseRedDarker) + if m.selected == 0 { + background = style.BaseGrayDarker + } + + return m.ZoneMark(ZoneLeftButton, style.HeaderStyle.Copy().Background(background).Margin(0, 2).Render("<--")) +} + +func (m Model) rightButton() string { + background := m.ZoneBackground(ZoneRightButton, style.BaseRed, style.BaseRedDarker) + if m.selected == len(m.items)-1 { + background = style.BaseGrayDarker + } + + return m.ZoneMark(ZoneRightButton, style.HeaderStyle.Copy().Background(background).Margin(0, 2).Render("-->")) +} + func (m Model) View() string { title := style.BoldStyle.Copy().MarginBottom(4).Render(m.title) - leftButton := m.zones.Mark(ZoneLeftButton, style.HeaderStyle.Copy().Background(lo.Ternary(m.zones.Get(ZoneLeftButton).InBounds(m.LastMouse), style.BaseRed, style.BaseRedDarker)).Margin(0, 2).Render("<--")) - rightButton := m.zones.Mark(ZoneRightButton, style.HeaderStyle.Copy().Background(lo.Ternary(m.zones.Get(ZoneRightButton).InBounds(m.LastMouse), style.BaseRed, style.BaseRedDarker)).Margin(0, 2).Render("-->")) middle := lipgloss.JoinHorizontal(lipgloss.Center, - leftButton, + m.leftButton(), m.items[m.selected], - rightButton, + m.rightButton(), ) dots := lipgloss.NewStyle().Margin(2, 0).Render(strings.Join(lo.Map(m.items, func(item string, index int) string { @@ -106,7 +119,7 @@ func (m Model) View() string { return "○" }), " ")) - doneButton := m.zones.Mark(ZoneDoneButton, style.HeaderStyle.Copy().Background(lo.Ternary(m.zones.Get(ZoneDoneButton).InBounds(m.LastMouse), style.BaseRed, style.BaseRedDarker)).MarginTop(2).Render("Continue")) + doneButton := style.HeaderStyle.Copy().Background(m.ZoneBackground(ZoneDoneButton, style.BaseRed, style.BaseRedDarker)).Render(m.ZoneMark(ZoneDoneButton, "Continue")) return lipgloss.Place(m.Size.Width, m.Size.Height, lipgloss.Center, lipgloss.Center, lipgloss.JoinVertical(lipgloss.Center, title, middle, dots, doneButton)) } diff --git a/ui/menus/gameview/gameview.go b/ui/menus/gameview/gameview.go index da6c4b0..9640d99 100644 --- a/ui/menus/gameview/gameview.go +++ b/ui/menus/gameview/gameview.go @@ -262,8 +262,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.lastEvent = m.Session.GetEventID() if len(diff) > 0 { - fmt.Println("DIFF", len(diff)) - artifacts := lo.Map(lo.Filter(diff, func(item game.StateCheckpoint, index int) bool { added, ok := item.Events[game.StateEventArtifactAdded].(game.StateEventArtifactAddedData) return ok && !lo.SomeBy(diff, func(item game.StateCheckpoint) bool { @@ -271,7 +269,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return ok && added.GUID == removed.GUID }) }), func(item game.StateCheckpoint, index int) string { - return components.ArtifactCard(m.Session, item.Events[game.StateEventArtifactAdded].(game.StateEventArtifactAddedData).GUID, 20, 20, 45) + return components.ArtifactCard(m.Session, item.Events[game.StateEventArtifactAdded].(game.StateEventArtifactAddedData).GUID, 20, 45) }) cards := lo.Map(lo.Filter(diff, func(item game.StateCheckpoint, index int) bool { @@ -281,7 +279,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return ok && added.GUID == removed.GUID }) }), func(item game.StateCheckpoint, index int) string { - return components.HalfCard(m.Session, item.Events[game.StateEventCardAdded].(game.StateEventCardAddedData).GUID, false, 20, 20, false, 45) + return components.HalfCard(m.Session, item.Events[game.StateEventCardAdded].(game.StateEventCardAddedData).GUID, false, 20, 20, false, 45, false) }) var pushModels []tea.Model @@ -591,7 +589,7 @@ func (m Model) fightEnemyView() string { func (m Model) fightCardView() string { fight := m.Session.GetFight() var cardBoxes = lo.Map(fight.Hand, func(guid string, index int) string { - return components.HalfCard(m.Session, guid, index == m.selectedCard, m.fightCardViewHeight()/2, m.fightCardViewHeight()-1, len(fight.Hand)*35 >= m.Size.Width) + return components.HalfCard(m.Session, guid, index == m.selectedCard, m.fightCardViewHeight()/2, m.fightCardViewHeight()-1, len(fight.Hand)*35 >= m.Size.Width, 0, true) }) cardBoxes = lo.Map(cardBoxes, func(item string, i int) string { diff --git a/ui/menus/merchant/merchant.go b/ui/menus/merchant/merchant.go index a311da7..f2c4ce3 100644 --- a/ui/menus/merchant/merchant.go +++ b/ui/menus/merchant/merchant.go @@ -217,7 +217,7 @@ func (m Model) View() string { selectedItemLook = components.ArtifactCard(m.session, item.ID, 20, 20) canBuy = m.session.GetPlayer().Gold >= item.Price case *game.Card: - selectedItemLook = components.HalfCard(m.session, item.ID, false, 20, 20, false) + selectedItemLook = components.HalfCard(m.session, item.ID, false, 20, 20, false, 0, false) canBuy = m.session.GetPlayer().Gold >= item.Price } @@ -241,7 +241,7 @@ func (m Model) View() string { selectedItem := m.playerCardGetSelected() var selectedItemLook string if len(selectedItem) > 0 { - selectedItemLook = components.HalfCard(m.session, selectedItem, false, 20, 20, false) + selectedItemLook = components.HalfCard(m.session, selectedItem, false, 20, 20, false, 0, false) } rightLook = lipgloss.JoinVertical(lipgloss.Top, diff --git a/ui/menus/overview/overview.go b/ui/menus/overview/overview.go index d9b1771..f765fde 100644 --- a/ui/menus/overview/overview.go +++ b/ui/menus/overview/overview.go @@ -283,7 +283,7 @@ func (m MenuModel) View() string { case ChoiceArtifacts: var selected string if m.artifactTable.Cursor() < len(m.Session.GetArtifacts(game.PlayerActorID)) { - selected = components.ArtifactCard(m.Session, m.Session.GetArtifacts(game.PlayerActorID)[m.artifactTable.Cursor()], 20, 40, 45) + selected = components.ArtifactCard(m.Session, m.Session.GetArtifacts(game.PlayerActorID)[m.artifactTable.Cursor()], 20, 45) } contentBox = contentStyle.Render(lipgloss.JoinVertical(lipgloss.Left, @@ -296,7 +296,7 @@ func (m MenuModel) View() string { case ChoiceCards: var selected string if m.artifactTable.Cursor() < len(m.Session.GetCards(game.PlayerActorID)) { - selected = components.HalfCard(m.Session, m.Session.GetCards(game.PlayerActorID)[m.cardTable.Cursor()], false, 20, 40, false, 45) + selected = components.HalfCard(m.Session, m.Session.GetCards(game.PlayerActorID)[m.cardTable.Cursor()], false, 20, 40, false, 45, false) } contentBox = contentStyle.Render(lipgloss.JoinVertical(lipgloss.Left,