feat: transition model, private tags

This commit is contained in:
Daniel Schmidt 2024-01-14 22:05:45 +01:00
parent 131824d7fe
commit 68d9de1e7c
13 changed files with 211 additions and 67 deletions

View File

@ -0,0 +1,9 @@
remember to use your consumables
watch out for the intent of your enemies
collect gold to spend at the merchant
may your death be a glorious one
rise and shine
smell the ashes
welcome back from sleep
the right crowbar in the wrong place...
body slots like HND or ARM can only hold 1 artifact

View File

@ -1,10 +1,10 @@
register_event("RUST_MITE", {
name = "Tasty metals...",
description = [[
You are walking through the facility hoping to find a way out. After a few turns you hear a strange noise. You look around and come across a strange being.
It seems to be eating the metal from the walls. It looks at you and after a few seconds it rushes towards you.
**It seems to be hostile!**
You are walking through the facility hoping to find a way out. After a few turns you hear a strange noise. You look around and come across a strange being.
It seems to be eating the metal from the walls. It looks at you and after a few seconds it rushes towards you.
**It seems to be hostile!**
]],
tags = {"ACT_0"},
choices = {
@ -21,11 +21,11 @@ register_event("RUST_MITE", {
register_event("CLEAN_BOT", {
name = "Corpse. Clean. Engage.",
description = [[
While exploring the facility you hear a strange noise. Suddenly a strange robot appears from one of the corridors.
It seems to be cleaning up the area, but it's not working properly anymore and you can see small sparks coming out of it.
It looks at you and says "Corpse. Clean. Engage.".
**You're not sure what it means, but it doesn't seem to be friendly!**
While exploring the facility you hear a strange noise. Suddenly a strange robot appears from one of the corridors.
It seems to be cleaning up the area, but it's not working properly anymore and you can see small sparks coming out of it.
It looks at you and says "Corpse. Clean. Engage.".
**You're not sure what it means, but it doesn't seem to be friendly!**
]],
tags = {"ACT_0"},
choices = {

View File

@ -3,6 +3,8 @@ package game
import (
"encoding/gob"
"github.com/BigJk/end_of_eden/internal/lua/luhelp"
"github.com/samber/lo"
"strings"
)
func init() {
@ -21,6 +23,12 @@ type Artifact struct {
BaseGame bool
}
func (a Artifact) PublicTags() []string {
return lo.Filter(a.Tags, func(s string, i int) bool {
return !strings.HasPrefix(s, "_")
})
}
type ArtifactInstance struct {
TypeID string
GUID string

View File

@ -3,6 +3,8 @@ package game
import (
"encoding/gob"
"github.com/BigJk/end_of_eden/internal/lua/luhelp"
"github.com/samber/lo"
"strings"
)
func init() {
@ -28,6 +30,12 @@ type Card struct {
BaseGame bool
}
func (c Card) PublicTags() []string {
return lo.Filter(c.Tags, func(s string, i int) bool {
return !strings.HasPrefix(s, "_")
})
}
// CardInstance represents an instance of a card owned by some actor.
type CardInstance struct {
TypeID string

View File

@ -28,7 +28,7 @@ func ArtifactCard(session *game.Session, guid string, baseHeight int, width int)
Background(lipgloss.Color("#343a40")).
Foreground(style.BaseWhite)
tagsText := strings.Join(art.Tags, ", ")
tagsText := strings.Join(art.PublicTags(), ", ")
return artifactStyle.
Height(baseHeight).

View File

@ -24,7 +24,7 @@ func HalfCard(session *game.Session, guid string, active bool, baseHeight int, m
cardState := session.GetCardState(guid)
pointText := strings.Repeat("•", card.PointCost)
tagsText := strings.Join(card.Tags, ", ")
tagsText := strings.Join(card.PublicTags(), ", ")
cardCol, _ := colorful.Hex(card.Color)
bgCol, _ := colorful.MakeColor(style.BaseGrayDarker)

View File

@ -55,10 +55,12 @@ func New(parent tea.Model, zones *zone.Manager, session *game.Session) Model {
session.Log(game.LogTypeSuccess, "Game started! Good luck...")
return Model{
zones: zones,
parent: parent,
event: eventview.New(zones, session),
merchant: merchant.New(zones, session),
zones: zones,
parent: parent,
event: eventview.New(zones, session),
merchant: merchant.New(zones, session),
lastGameState: session.GetGameState(),
lastEvent: session.GetEventID(),
Session: session,
Start: session.MarkState(),
@ -242,7 +244,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, cmd
}
switch m.Session.GetGameState() {
currentState := m.Session.GetGameState()
currentEvent := m.Session.GetEventID()
switch currentState {
case game.GameStateFight:
case game.GameStateMerchant:
m.merchant, cmd = m.merchant.Update(msg)
@ -254,12 +259,16 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return gameover.New(m.zones, m.Session, m.Start), nil
}
if m.Session.GetGameState() != m.lastGameState || m.Session.GetEventID() != m.lastEvent {
//
// Show "New Artifacts" / "New Cards" if there are any.
//
if currentState != m.lastGameState || currentEvent != m.lastEvent {
diff := m.BeforeStateSwitch.Diff(m.Session)
m.BeforeStateSwitch = m.Session.MarkState()
m.lastGameState = m.Session.GetGameState()
m.lastEvent = m.Session.GetEventID()
m.lastGameState = currentState
m.lastEvent = currentEvent
if len(diff) > 0 {
artifacts := lo.Map(lo.Filter(diff, func(item game.StateCheckpoint, index int) bool {

View File

@ -112,7 +112,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
log.Println("Error loading save:", err)
} else {
m.choices = m.choices.Clear()
return gameview.New(m, m.zones, session), cmd
return m, tea.Sequence(
cmd,
root.Push(gameview.New(m, m.zones, session)),
)
}
}
@ -131,11 +134,14 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
})...)
m.choices = m.choices.Clear()
return m, root.Push(gameview.New(m, m.zones, game.NewSession(
game.WithLogging(log.New(f, "SESSION ", log.Ldate|log.Ltime|log.Lshortfile)),
game.WithMods(m.settings.GetStrings("mods")),
lo.Ternary(os.Getenv("EOE_DEBUG") == "1", game.WithDebugEnabled(8272), nil),
)))
return m, tea.Sequence(
cmd,
root.Push(gameview.New(m, m.zones, game.NewSession(
game.WithLogging(log.New(f, "SESSION ", log.Ldate|log.Ltime|log.Lshortfile)),
game.WithMods(m.settings.GetStrings("mods")),
lo.Ternary(os.Getenv("EOE_DEBUG") == "1", game.WithDebugEnabled(8272), nil),
))),
)
case ChoiceAbout:
audio.Play("btn_menu")

View File

@ -6,6 +6,7 @@ import (
"github.com/BigJk/end_of_eden/system/audio"
"github.com/BigJk/end_of_eden/ui"
"github.com/BigJk/end_of_eden/ui/components"
"github.com/BigJk/end_of_eden/ui/root"
"github.com/BigJk/end_of_eden/ui/style"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/list"
@ -143,14 +144,14 @@ func (m MenuModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// Update card table
m.cardTable.SetRows(lo.Map(cards, func(guid string, index int) table.Row {
card, instance := m.Session.GetCard(guid)
return table.Row{m.zones.Mark(ZoneCards+fmt.Sprint(index), card.Name), strings.Join(card.Tags, ", "), fmt.Sprint(instance.Level)}
return table.Row{m.zones.Mark(ZoneCards+fmt.Sprint(index), card.Name), strings.Join(card.PublicTags(), ", "), fmt.Sprint(instance.Level)}
}))
m.cardTable.SetHeight(m.Size.Height - style.HeaderStyle.GetVerticalFrameSize() - 1 - 2)
// Update artifact table
m.artifactTable.SetRows(lo.Map(artifacts, func(guid string, index int) table.Row {
art, _ := m.Session.GetArtifact(guid)
return table.Row{m.zones.Mark(ZoneArtifacts+fmt.Sprint(index), art.Name), strings.Join(art.Tags, ", "), fmt.Sprintf("%d$", art.Price)}
return table.Row{m.zones.Mark(ZoneArtifacts+fmt.Sprint(index), art.Name), strings.Join(art.PublicTags(), ", "), fmt.Sprintf("%d$", art.Price)}
}))
m.artifactTable.SetHeight(m.Size.Height - style.HeaderStyle.GetVerticalFrameSize() - 1 - 2)
@ -190,7 +191,7 @@ func (m MenuModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.KeyEnter:
if m.list.SelectedItem().(choiceItem).key == ChoiceQuit {
m.Session.Close()
return nil, nil
return nil, root.RemovePushTransitionFunc()
}
case tea.KeyDown:
fallthrough

View File

@ -0,0 +1,73 @@
package transition
import (
"github.com/BigJk/end_of_eden/system/gen"
"github.com/BigJk/end_of_eden/ui"
"github.com/BigJk/end_of_eden/ui/root"
"github.com/BigJk/end_of_eden/ui/style"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"strings"
"time"
)
const (
charFull = "●"
charHalfLeft = "◐"
charHalfRight = "◑"
charEmpty = "○"
)
type TickMsg string
type Model struct {
ui.MenuBase
parent tea.Model
created time.Time
line string
}
func New(parent tea.Model) Model {
return Model{parent: parent, line: gen.GetRandom("loading_lines")}
}
func (m Model) Start() tea.Msg {
m.created = time.Now()
return m
}
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 root.ModelGettingVisibleMsg:
m.created = time.Now()
case TickMsg:
if !m.created.IsZero() && time.Since(m.created) > time.Millisecond*800 {
return m.parent, tea.Sequence(root.GettingVisible(), func() tea.Msg {
return tea.WindowSizeMsg{Width: m.Size.Width, Height: m.Size.Height}
})
}
}
return m, tea.Tick(time.Second/30, func(t time.Time) tea.Msg {
return TickMsg("tick")
})
}
func (m Model) View() string {
elapsed := time.Since(m.created)
spinner := strings.Split(strings.Repeat(charEmpty, 10), "")
pos := elapsed.Milliseconds() / 80 % int64(len(spinner))
spinner[pos] = charFull
spinner[(pos+1)%int64(len(spinner))] = charHalfLeft
spinner[((pos-1)+int64(len(spinner)))%int64(len(spinner))] = charHalfRight
return lipgloss.Place(m.Size.Width, m.Size.Height, lipgloss.Center, lipgloss.Center, lipgloss.JoinVertical(lipgloss.Center, m.line+"\n", style.RedText.Render(strings.Join(spinner, ""))))
}

View File

@ -29,37 +29,18 @@ func GettingVisible() tea.Cmd {
}
}
// Tooltip represents a tooltip aka overlay message that should be displayed.
type Tooltip struct {
ID string
Content string
X int
Y int
}
type PushTransitionFuncMsg func(parent tea.Model) tea.Model
type TooltipMsg Tooltip
// TooltipCreate creates a new tooltip.
func TooltipCreate(tip Tooltip) tea.Cmd {
// PushTransitionFunc pushes a new transition model on the root ui that will be shown between models on the stack.
func PushTransitionFunc(fn func(parent tea.Model) tea.Model) tea.Cmd {
return func() tea.Msg {
return TooltipMsg(tip)
return PushTransitionFuncMsg(fn)
}
}
type TooltipDeleteMsg string
// TooltipDelete deletes a tooltip.
func TooltipDelete(id string) tea.Cmd {
// RemovePushTransitionFunc removes the transition model from the root ui.
func RemovePushTransitionFunc() tea.Cmd {
return func() tea.Msg {
return TooltipDeleteMsg(id)
}
}
type TooltipClearMsg struct{}
// TooltipClear clears all tooltips.
func TooltipClear() tea.Cmd {
return func() tea.Msg {
return TooltipClearMsg(struct{}{})
return PushTransitionFuncMsg(nil)
}
}

View File

@ -14,10 +14,11 @@ import (
// the zone manager. The top model of the internal stack is the current model
// and will be rendered.
type Model struct {
zones *zone.Manager
stack []tea.Model
size tea.WindowSizeMsg
tooltips map[string]Tooltip
zones *zone.Manager
stack []tea.Model
size tea.WindowSizeMsg
tooltips map[string]Tooltip
transitionModel func(parent tea.Model) tea.Model
}
// New creates a new root model.
@ -31,7 +32,11 @@ func New(zones *zone.Manager, root tea.Model) Model {
// PushModel pushes a new model on the stack.
func (m Model) PushModel(model tea.Model) Model {
m.stack = append(m.stack, model)
if m.transitionModel != nil {
m.stack = append(m.stack, m.transitionModel(model))
} else {
m.stack = append(m.stack, model)
}
m.tooltips = map[string]Tooltip{}
return m
}
@ -71,6 +76,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m = m.PushModel(model)
}
cmds = append(cmds, GettingVisible())
case PushTransitionFuncMsg:
m.transitionModel = msg
}
curIndex := len(m.stack) - 1
@ -82,22 +89,26 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds = append(cmds, cmd)
}
if menu, ok := m.stack[curIndex].(ui.Menu); ok && !menu.HasSize() {
return m, tea.Batch(cmd, func() tea.Msg {
return m.size
})
}
if m.stack[curIndex] == nil {
// If we remove the top model, we need to send a window size message to the new top model
// to avoid the layout to be broken.
cmds = append(cmds,
func() tea.Msg {
return tea.WindowSizeMsg{
Width: m.size.Width,
Height: m.size.Height,
}
},
GettingVisible(),
)
m.stack = m.stack[:len(m.stack)-1]
} else if menu, ok := m.stack[curIndex].(ui.Menu); ok && !menu.HasSize() {
cmds = append(cmds, func() tea.Msg {
return tea.WindowSizeMsg{
Width: m.size.Width,
Height: m.size.Height,
}
}, GettingVisible())
m.stack = m.stack[:len(m.stack)-1]
})
}
return m, tea.Batch(cmds...)

38
ui/root/tooltip.go Normal file
View File

@ -0,0 +1,38 @@
package root
import tea "github.com/charmbracelet/bubbletea"
// Tooltip represents a tooltip aka overlay message that should be displayed.
type Tooltip struct {
ID string
Content string
X int
Y int
}
type TooltipMsg Tooltip
// TooltipCreate creates a new tooltip.
func TooltipCreate(tip Tooltip) tea.Cmd {
return func() tea.Msg {
return TooltipMsg(tip)
}
}
type TooltipDeleteMsg string
// TooltipDelete deletes a tooltip.
func TooltipDelete(id string) tea.Cmd {
return func() tea.Msg {
return TooltipDeleteMsg(id)
}
}
type TooltipClearMsg struct{}
// TooltipClear clears all tooltips.
func TooltipClear() tea.Cmd {
return func() tea.Msg {
return TooltipClearMsg(struct{}{})
}
}