feat: experimental animation viewer

This commit is contained in:
Daniel Schmidt 2023-12-16 22:10:00 +01:00
parent dfaf395bfa
commit cbaf16f764
9 changed files with 292 additions and 11 deletions

View File

@ -0,0 +1,81 @@
package gifviewer
import (
"fmt"
"github.com/BigJk/end_of_eden/image"
"github.com/BigJk/end_of_eden/ui"
tea "github.com/charmbracelet/bubbletea"
"math/rand"
"time"
)
type GifAnimationFinished string
type GifAnimationFrame string
type Model struct {
ui.MenuBase
id string
parent tea.Model
fps int
lastFrame int64
elapsedMs int64
frames []string
}
func New(parent tea.Model, file string, fps int, width int, height int) (tea.Model, error) {
var options []image.Option
if width > 0 && height == 0 {
options = append(options, image.WithMaxWidth(width))
} else if width > 0 && height > 0 {
options = append(options, image.WithResize(width, height))
} else {
options = append(options, image.WithMaxWidth(100))
}
frames, err := image.FetchAnimation(file, options...)
if err != nil {
return nil, err
}
return Model{id: fmt.Sprint(rand.Int()), parent: parent, fps: fps, frames: frames, lastFrame: time.Now().UnixMilli()}, nil
}
func (m Model) Init() tea.Cmd {
return nil
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.Size = msg
case GifAnimationFrame:
if string(msg) != m.id {
return m, nil
}
m.elapsedMs += time.Now().UnixMilli() - m.lastFrame
m.lastFrame = time.Now().UnixMilli()
if m.Frame() == len(m.frames)-1 {
return m, func() tea.Msg {
return GifAnimationFinished(m.id)
}
}
}
return m, tea.Tick(time.Second/time.Duration(m.fps), func(t time.Time) tea.Msg {
return GifAnimationFrame(m.id)
})
}
func (m Model) Frame() int {
return int(m.elapsedMs / (1000 / int64(m.fps)) % int64(len(m.frames)))
}
func (m Model) View() string {
return m.frames[m.Frame()]
}

View File

@ -0,0 +1,80 @@
package loader
import (
"github.com/BigJk/end_of_eden/ui"
"github.com/BigJk/end_of_eden/ui/style"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"math"
"strings"
"time"
)
type LoaderTick string
var loaderFrames = strings.Split("◐◓◑◒", "")
type Model struct {
ui.MenuBase
parent tea.Model
text chan string
done chan bool
currentMessage string
lastFrame int64
elapsedMs int64
}
func New(parent tea.Model, loadingMessage string) (Model, chan bool, chan string) {
m := Model{
parent: parent,
text: make(chan string, 1),
done: make(chan bool, 1),
currentMessage: loadingMessage,
lastFrame: time.Now().UnixMilli(),
}
return m, m.done, m.text
}
func (m Model) Init() tea.Cmd {
return nil
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if len(m.done) > 0 {
<-m.done
return m.parent, nil
}
if len(m.text) > 0 {
m.currentMessage = <-m.text
}
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.Size = msg
case LoaderTick:
m.elapsedMs += time.Now().UnixMilli() - m.lastFrame
m.lastFrame = time.Now().UnixMilli()
}
return m, tea.Tick(time.Second/5, func(t time.Time) tea.Msg {
return LoaderTick("")
})
}
func (m Model) Frame() int {
return int(m.elapsedMs / (1000 / 10) % int64(len(loaderFrames)))
}
func (m Model) loadingIndicator() string {
width := 20.0
leftPad := ((1 + math.Sin(float64(m.elapsedMs)/1000)) / 2) * width
rightPad := width - leftPad
return "[ " + strings.Repeat(" ", int(leftPad)) + style.RedText.Render("=") + strings.Repeat(" ", int(rightPad)) + " ]"
}
func (m Model) View() string {
return lipgloss.Place(m.Size.Width, m.Size.Height, lipgloss.Center, lipgloss.Center, lipgloss.JoinVertical(lipgloss.Center, style.RedText.Render(ui.Title), "", m.currentMessage, "", m.loadingIndicator()))
}

View File

@ -0,0 +1,39 @@
package waitfor
import (
tea "github.com/charmbracelet/bubbletea"
)
type Model struct {
model tea.Model
cond func(msg tea.Msg) bool
}
func New(model tea.Model, cond func(msg tea.Msg) bool) tea.Model {
return Model{
model: model,
cond: cond,
}
}
func (m Model) Init() tea.Cmd {
return nil
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.cond(msg) {
return nil, nil
}
model, modelCmd := m.model.Update(msg)
if model == nil {
return nil, nil
}
m.model = model
return m, modelCmd
}
func (m Model) View() string {
return m.model.View()
}

View File

@ -20,9 +20,8 @@ var (
type Model struct {
ui.MenuBase
zones *zone.Manager
lastMouse tea.MouseMsg
parent tea.Model
zones *zone.Manager
parent tea.Model
}
func New(parent tea.Model, zones *zone.Manager) Model {

View File

@ -9,7 +9,6 @@ import (
"github.com/BigJk/end_of_eden/ui/root"
"github.com/BigJk/end_of_eden/ui/style"
"github.com/BigJk/end_of_eden/util"
"github.com/BigJk/imeji"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/glamour"
@ -185,7 +184,7 @@ func (m Model) eventUpdateContent() Model {
}
res = string(ansRes)
} else {
imgRes, err := image.Fetch(file, imeji.WithMaxWidth(100))
imgRes, err := image.Fetch(file, image.WithMaxWidth(100))
if err != nil {
continue
}

View File

@ -427,7 +427,7 @@ func (m Model) fightStatusBottom() string {
lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color(style.BaseWhite)).Padding(0, 4, 0, 4).Render(fmt.Sprintf("Deck: %d", len(fight.Deck))),
lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#FFFF00")).Padding(0, 4, 0, 0).Render(fmt.Sprintf("Used: %d", len(fight.Used))),
lipgloss.NewStyle().Bold(true).Foreground(style.BaseRed).Padding(0, 4, 0, 0).Render(fmt.Sprintf("Exhausted: %d", len(fight.Exhausted))),
lipgloss.NewStyle().Bold(true).Foreground(style.BaseGreen).Padding(0, 4, 0, 0).Render(fmt.Sprintf("Action Points: %d", fight.CurrentPoints)),
lipgloss.NewStyle().Bold(true).Foreground(style.BaseGreen).Padding(0, 4, 0, 0).Render(fmt.Sprintf("Action Points: (%d) %s", fight.CurrentPoints, strings.Repeat("• ", fight.CurrentPoints))),
m.zones.Mark(ZonePlayerInspect, components.StatusEffects(m.Session, m.Session.GetPlayer())),
),
),

70
ui/menus/intro/intro.go Normal file
View File

@ -0,0 +1,70 @@
package intro
import (
"github.com/BigJk/end_of_eden/ui"
"github.com/BigJk/end_of_eden/ui/components/gifviewer"
"github.com/BigJk/end_of_eden/ui/style"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
var frameStyle = lipgloss.NewStyle().
Border(lipgloss.ThickBorder(), true).
BorderForeground(style.BaseRedDarker).
Padding(0, 1)
type Model struct {
ui.MenuBase
parent tea.Model
gif tea.Model
}
func New(parent tea.Model) Model {
return Model{
parent: parent,
}
}
func (m Model) Init() tea.Cmd {
return nil
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.gif == nil {
gif, err := gifviewer.New(m, "intro.gif", 10, 100, 0)
if err != nil {
return m.parent, nil
}
m.gif = gif
}
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.Size = msg
case tea.KeyMsg:
if msg.Type == tea.KeyEscape {
return m.parent, nil
}
}
var cmd tea.Cmd
m.gif, cmd = m.gif.Update(msg)
return m, cmd
}
func (m Model) View() string {
if m.gif == nil {
return ""
}
frame := m.gif.View()
frameFramed := frameStyle.Render(
lipgloss.NewStyle().MaxWidth(100 - 4).Render(frame[:len(frame)-1]))
return lipgloss.Place(m.Size.Width, m.Size.Height, lipgloss.Center, lipgloss.Center, lipgloss.JoinVertical(
lipgloss.Center,
frameFramed,
frameStyle.Render(lipgloss.NewStyle().Width(100-4).Render(ui.About)),
))
}

View File

@ -7,13 +7,14 @@ import (
"github.com/BigJk/end_of_eden/image"
"github.com/BigJk/end_of_eden/settings"
"github.com/BigJk/end_of_eden/ui"
"github.com/BigJk/end_of_eden/ui/components/loader"
"github.com/BigJk/end_of_eden/ui/menus/about"
"github.com/BigJk/end_of_eden/ui/menus/gameview"
"github.com/BigJk/end_of_eden/ui/menus/intro"
"github.com/BigJk/end_of_eden/ui/menus/mods"
uiset "github.com/BigJk/end_of_eden/ui/menus/settings"
"github.com/BigJk/end_of_eden/ui/root"
"github.com/BigJk/end_of_eden/ui/style"
"github.com/BigJk/imeji"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
zone "github.com/lrstanley/bubblezone"
@ -34,10 +35,11 @@ type Model struct {
settingValues []uiset.Value
settingSaver uiset.Saver
didLoad bool
}
func NewModel(zones *zone.Manager, settings settings.Settings, values []uiset.Value, saver uiset.Saver) Model {
img, _ := image.Fetch("title.png", imeji.WithResize(180, 9))
img, _ := image.Fetch("title.png", image.WithResize(180, 9))
audio.PlayMusic("planet_mining")
@ -58,6 +60,19 @@ func (m Model) Init() tea.Cmd {
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if !m.didLoad {
m.didLoad = true
if m.settings.GetBool("experimental") {
l, done, _ := loader.New(intro.New(m), "Initial loading")
go func() {
_, _ = image.FetchAnimation("intro.gif", image.WithMaxWidth(100))
done <- true
}()
return l, nil
}
}
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.Size = msg

View File

@ -8,6 +8,4 @@ var Title = `▄▄▄ . ▐ ▄ ·▄▄▄▄ ·▄▄▄ ▄▄
`
// About is the about text.
var About = `About End Of Eden
Welcome to a world 500 years in the future, ravaged by climate change and nuclear wars. The remaining humans have become few and far between, replaced by mutated and plant-based creatures. In this gonzo-fantasy setting, you find yourself awakening from cryo sleep in an underground facility, long forgotten and alone. With all other cryosleep capsules broken, it's up to you to navigate this strange and dangerous world and uncover the secrets that led to your isolation...`
var About = `Welcome to a world 500 years in the future, ravaged by climate change and nuclear wars. The remaining humans have become few and far between, replaced by mutated and plant-based creatures. In this gonzo-fantasy setting, you find yourself awakening from cryo sleep in an underground facility, long forgotten and alone. With all other cryosleep capsules broken, it's up to you to navigate this strange and dangerous world and uncover the secrets that led to your isolation...`