mirror of
https://github.com/BigJk/end_of_eden.git
synced 2026-02-06 10:48:09 +00:00
Added tooltip system.
This commit is contained in:
parent
504cab6d9a
commit
a1e2f79948
@ -36,6 +36,7 @@ type Model struct {
|
||||
inOpponentSelection bool
|
||||
inEnemyView bool
|
||||
animations []tea.Model
|
||||
ctrlDown bool
|
||||
|
||||
event tea.Model
|
||||
merchant tea.Model
|
||||
@ -118,6 +119,24 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.selectedCard = lo.Clamp(m.selectedCard-1, 0, len(m.Session.GetFight().Hand)-1)
|
||||
case tea.KeyRight:
|
||||
m.selectedCard = lo.Clamp(m.selectedCard+1, 0, len(m.Session.GetFight().Hand)-1)
|
||||
case tea.KeyCtrlDown:
|
||||
m.ctrlDown = true
|
||||
case tea.KeyCtrlU:
|
||||
m.ctrlDown = false
|
||||
}
|
||||
|
||||
// Show tooltip
|
||||
if msg.String() == "x" {
|
||||
for i := 0; i < m.Session.GetOpponentCount(game.PlayerActorID); i++ {
|
||||
if m.zones.Get(fmt.Sprintf("%s%d", ZoneEnemy, i)).InBounds(m.LastMouse) {
|
||||
cmds = append(cmds, root.TooltipCreate(root.ToolTip{
|
||||
ID: "ENEMY",
|
||||
Content: m.fightEnemyInspectTooltipView(),
|
||||
X: m.LastMouse.X,
|
||||
Y: m.LastMouse.Y,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// Mouse
|
||||
@ -126,6 +145,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.LastMouse = msg
|
||||
|
||||
if msg.Type == tea.MouseLeft {
|
||||
// Kill all enemy tooltips
|
||||
cmds = append(cmds, root.TooltipDelete("ENEMY"))
|
||||
|
||||
switch m.Session.GetGameState() {
|
||||
case game.GameStateFight:
|
||||
if m.zones.Get(ZoneEndTurn).InBounds(msg) {
|
||||
@ -142,8 +164,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if m.zones.Get(fmt.Sprintf("%s%d", ZoneEnemy, i)).InBounds(msg) {
|
||||
if msg.Type == tea.MouseLeft && m.selectedOpponent == i {
|
||||
m = m.tryCast()
|
||||
} else {
|
||||
m.selectedOpponent = i
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -408,6 +428,20 @@ func (m Model) fightCardViewHeight() int {
|
||||
return m.Size.Height - m.fightEnemyViewHeight() - 1 - 4 - 4
|
||||
}
|
||||
|
||||
func (m Model) fightEnemyInspectTooltipView() string {
|
||||
enemy := m.Session.GetOpponents(game.PlayerActorID)[m.selectedOpponent]
|
||||
|
||||
intend := lipgloss.NewStyle().Bold(true).Underline(true).Foreground(style.BaseWhite).Render("Intend:") + "\n\n" + m.Session.GetActorIntend(enemy.GUID) + "\n\n"
|
||||
|
||||
status := lipgloss.NewStyle().Bold(true).Underline(true).Foreground(style.BaseWhite).Render("Status Effects:") + "\n\n" + strings.Join(lo.Map(enemy.StatusEffects.ToSlice(), func(guid string, index int) string {
|
||||
return components.StatusEffect(m.Session, guid) + ": " + m.Session.GetStatusEffectState(guid)
|
||||
}), "\n\n")
|
||||
|
||||
return lipgloss.NewStyle().Border(lipgloss.ThickBorder(), true).Padding(1, 2).BorderForeground(style.BaseRedDarker).Render(
|
||||
lipgloss.NewStyle().Width(30).Render(intend + status),
|
||||
)
|
||||
}
|
||||
|
||||
func (m Model) fightEnemyInspectView() string {
|
||||
enemy := m.Session.GetOpponents(game.PlayerActorID)[m.selectedOpponent]
|
||||
|
||||
|
||||
132
ui/overlay/overlay.go
Normal file
132
ui/overlay/overlay.go
Normal file
@ -0,0 +1,132 @@
|
||||
package overlay
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/muesli/ansi"
|
||||
"github.com/muesli/reflow/truncate"
|
||||
)
|
||||
|
||||
// Code borrowed and cut down from @mrusme and https://github.com/charmbracelet/lipgloss/pull/102
|
||||
|
||||
// Split a string into lines, additionally returning the size of the widest line.
|
||||
func getLines(s string) (lines []string, widest int) {
|
||||
lines = strings.Split(s, "\n")
|
||||
|
||||
for _, l := range lines {
|
||||
w := ansi.PrintableRuneWidth(l)
|
||||
if widest < w {
|
||||
widest = w
|
||||
}
|
||||
}
|
||||
|
||||
return lines, widest
|
||||
}
|
||||
|
||||
// PlaceOverlay places overlay on top of background.
|
||||
func PlaceOverlay(x, y int, overlay, background string) string {
|
||||
overlayLines, overlayWidth := getLines(overlay)
|
||||
backgroundLines, backgroundWidth := getLines(background)
|
||||
backgroundHeight := len(backgroundLines)
|
||||
overlayHeight := len(overlayLines)
|
||||
|
||||
if overlayWidth >= backgroundWidth && overlayHeight >= backgroundHeight {
|
||||
return overlay
|
||||
}
|
||||
|
||||
x = clamp(x, 0, backgroundWidth-overlayWidth)
|
||||
y = clamp(y, 0, backgroundHeight-overlayHeight)
|
||||
|
||||
var b strings.Builder
|
||||
for i, backgroundLine := range backgroundLines {
|
||||
if i > 0 {
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
if i < y || i >= y+overlayHeight {
|
||||
b.WriteString(backgroundLine)
|
||||
continue
|
||||
}
|
||||
|
||||
pos := 0
|
||||
if x > 0 {
|
||||
left := truncate.String(backgroundLine, uint(x))
|
||||
pos = ansi.PrintableRuneWidth(left)
|
||||
b.WriteString(left)
|
||||
if pos < x {
|
||||
pos = x
|
||||
}
|
||||
}
|
||||
|
||||
overlayLine := overlayLines[i-y]
|
||||
b.WriteString(overlayLine)
|
||||
pos += ansi.PrintableRuneWidth(overlayLine)
|
||||
|
||||
right := cutLeft(backgroundLine, pos)
|
||||
b.WriteString(right)
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// cutLeft cuts printable characters from the left.
|
||||
// This function is heavily based on muesli's ansi and truncate packages.
|
||||
func cutLeft(s string, cutWidth int) string {
|
||||
var (
|
||||
pos int
|
||||
isAnsi bool
|
||||
ab bytes.Buffer
|
||||
b bytes.Buffer
|
||||
)
|
||||
|
||||
for _, c := range s {
|
||||
var w int
|
||||
if c == ansi.Marker || isAnsi {
|
||||
isAnsi = true
|
||||
ab.WriteRune(c)
|
||||
if ansi.IsTerminator(c) {
|
||||
isAnsi = false
|
||||
if bytes.HasSuffix(ab.Bytes(), []byte("[0m")) {
|
||||
ab.Reset()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
w = runewidth.RuneWidth(c)
|
||||
}
|
||||
|
||||
if pos >= cutWidth {
|
||||
if b.Len() == 0 {
|
||||
if ab.Len() > 0 {
|
||||
b.Write(ab.Bytes())
|
||||
}
|
||||
if pos-cutWidth > 1 {
|
||||
b.WriteByte(' ')
|
||||
continue
|
||||
}
|
||||
}
|
||||
b.WriteRune(c)
|
||||
}
|
||||
pos += w
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func clamp(v, lower, upper int) int {
|
||||
return min(max(v, lower), upper)
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"github.com/BigJk/end_of_eden/game"
|
||||
"github.com/BigJk/end_of_eden/ui"
|
||||
"github.com/BigJk/end_of_eden/ui/menus/lua_error"
|
||||
"github.com/BigJk/end_of_eden/ui/overlay"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
zone "github.com/lrstanley/bubblezone"
|
||||
"github.com/samber/lo"
|
||||
@ -17,21 +18,47 @@ func Push(model tea.Model) tea.Cmd {
|
||||
}
|
||||
}
|
||||
|
||||
type ToolTip struct {
|
||||
ID string
|
||||
Content string
|
||||
X int
|
||||
Y int
|
||||
}
|
||||
|
||||
type ToolTipMsg ToolTip
|
||||
|
||||
func TooltipCreate(tip ToolTip) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
return ToolTipMsg(tip)
|
||||
}
|
||||
}
|
||||
|
||||
type ToolTipDeleteMsg string
|
||||
|
||||
func TooltipDelete(id string) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
return ToolTipDeleteMsg(id)
|
||||
}
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
zones *zone.Manager
|
||||
stack []tea.Model
|
||||
size tea.WindowSizeMsg
|
||||
zones *zone.Manager
|
||||
stack []tea.Model
|
||||
size tea.WindowSizeMsg
|
||||
tooltips map[string]ToolTip
|
||||
}
|
||||
|
||||
func New(zones *zone.Manager, root tea.Model) Model {
|
||||
return Model{
|
||||
zones: zones,
|
||||
stack: []tea.Model{root},
|
||||
zones: zones,
|
||||
stack: []tea.Model{root},
|
||||
tooltips: map[string]ToolTip{},
|
||||
}
|
||||
}
|
||||
|
||||
func (m Model) PushModel(model tea.Model) Model {
|
||||
m.stack = append(m.stack, model)
|
||||
m.tooltips = map[string]ToolTip{}
|
||||
return m
|
||||
}
|
||||
|
||||
@ -56,6 +83,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if msg.String() == "ctrl+c" {
|
||||
return m, tea.Quit
|
||||
}
|
||||
case ToolTipMsg:
|
||||
m.tooltips[msg.ID] = ToolTip(msg)
|
||||
case ToolTipDeleteMsg:
|
||||
delete(m.tooltips, string(msg))
|
||||
case PushModelMsg:
|
||||
m = m.PushModel(msg)
|
||||
}
|
||||
@ -82,7 +113,14 @@ func (m Model) View() string {
|
||||
if len(m.stack) == 0 {
|
||||
return "stack empty!"
|
||||
}
|
||||
return m.zones.Scan(m.stack[len(m.stack)-1].View())
|
||||
|
||||
view := m.zones.Scan(m.stack[len(m.stack)-1].View())
|
||||
|
||||
for _, v := range m.tooltips {
|
||||
view = overlay.PlaceOverlay(v.X, v.Y, v.Content, view)
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
func CheckLuaErrors(zones *zone.Manager, s *game.Session) tea.Cmd {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user