Compare commits

..

No commits in common. "4b57ace9d65a89b5964382df426cd5cf68e72c2c" and "3474d7d9739b58ee3723bd638baca737a8ec4c43" have entirely different histories.

10 changed files with 293 additions and 362 deletions

View File

@ -9,10 +9,11 @@ import (
// import "github.com/veandco/go-sdl2/sdl" // import "github.com/veandco/go-sdl2/sdl"
type ButtonBar struct { type ButtonBar struct {
ui.StackPanel ui.ContainerBase
Background color.Color Background color.Color
ButtonLength float32 ButtonLength float32
Orientation ui.Orientation
} }
const buttonBarWidth = 96 const buttonBarWidth = 96

View File

@ -7,12 +7,9 @@ import (
"opslag.de/schobers/fs/ricefs" "opslag.de/schobers/fs/ricefs"
"opslag.de/schobers/geom" "opslag.de/schobers/geom"
"opslag.de/schobers/zntg"
"opslag.de/schobers/zntg/addons/res" "opslag.de/schobers/zntg/addons/res"
"opslag.de/schobers/zntg/ui"
_ "opslag.de/schobers/zntg/sdlui" // rendering backend _ "opslag.de/schobers/zntg/sdlui" // rendering backend
// _ "opslag.de/schobers/zntg/allg5ui" // rendering backend "opslag.de/schobers/zntg/ui"
rice "github.com/GeertJohan/go.rice" rice "github.com/GeertJohan/go.rice"
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
@ -84,29 +81,27 @@ func (a *app) Init(ctx ui.Context) error {
content := tins2020.NewContent(a.dialogs) content := tins2020.NewContent(a.dialogs)
content.AddChild(tins2020.NewTerrainRenderer(a.game)) content.AddChild(tins2020.NewTerrainRenderer(a.game))
controls := tins2020.NewGameControls(a.game, a.dialogs) content.AddChild(tins2020.NewGameControls(a.game, a.dialogs))
content.AddChild(controls)
a.Content = content a.Content = content
a.dialogs.Init(ctx) a.dialogs.Init(ctx)
a.game.Reset(ctx) a.game.Reset(ctx)
controls.Init(ctx)
a.dialogs.ShowIntro(ctx) a.dialogs.ShowIntro(ctx)
return nil return nil
} }
func (a *app) Handle(ctx ui.Context, e ui.Event) bool { func (a *app) Handle(ctx ui.Context, event ui.Event) bool {
switch e := e.(type) { // switch e := event.(type) {
case *ui.DisplayMoveEvent: // case *ui.WindowMoved:
location := e.Bounds.Min.ToInt() // // x, y := window.GetPosition()
a.settings.Window.Location = &location // // settings.Window.Location = ptPtr(x, y)
case *ui.DisplayResizeEvent: // case *ui.WindowSizeChanged:
a.Arrange(ctx, e.Bounds, geom.ZeroPtF32, nil) // w, h := window.GetSize()
size := e.Bounds.Size().ToInt() // app.Arrange(ctx, geom.RectRelF32(0, 0, w, h))
a.settings.Window.Size = &size // settings.Window.Size = ptPtr(w, h)
} // }
return a.Proxy.Handle(ctx, e) return a.Proxy.Handle(ctx, event)
} }
func (a *app) Render(ctx ui.Context) { func (a *app) Render(ctx ui.Context) {
@ -137,7 +132,6 @@ func run() error {
if err != nil { if err != nil {
return err return err
} }
defer settings.Store()
if settings.Window.Location == nil { if settings.Window.Location == nil {
settings.Window.Location = ptPtr(sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED) settings.Window.Location = ptPtr(sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED)
@ -150,7 +144,6 @@ func run() error {
settings.Window.VSync = &vsync settings.Window.VSync = &vsync
} }
renderer, err := ui.NewRenderer("Botanim - TINS 2020", settings.Window.Size.X, settings.Window.Size.Y, ui.NewRendererOptions{ renderer, err := ui.NewRenderer("Botanim - TINS 2020", settings.Window.Size.X, settings.Window.Size.Y, ui.NewRendererOptions{
Location: &geom.PointF32{X: float32(settings.Window.Location.X), Y: float32(settings.Window.Location.Y)},
Resizable: true, Resizable: true,
VSync: *settings.Window.VSync, VSync: *settings.Window.VSync,
}) })
@ -166,20 +159,5 @@ func run() error {
dialogs: tins2020.NewDialogs(game), dialogs: tins2020.NewDialogs(game),
settings: settings, settings: settings,
} }
style := ui.DefaultStyle() return ui.Run(renderer, nil, app)
style.Palette = &ui.Palette{
Background: zntg.MustHexColor(`#5388C3`),
Primary: zntg.MustHexColor(`#5388C3`),
PrimaryDark: zntg.MustHexColor(`#15569F`),
PrimaryLight: zntg.MustHexColor(`#ABCAED`),
Secondary: zntg.MustHexColor(`#4AC69A`),
SecondaryDark: zntg.MustHexColor(`#0AA36D`),
SecondaryLight: zntg.MustHexColor(`#A6EED4`),
Text: color.White,
TextOnPrimary: color.White,
TextOnSecondary: color.White,
TextNegative: zntg.MustHexColor(`F3590E`),
TextPositive: zntg.MustHexColor(`65D80D`),
}
return ui.Run(renderer, style, app)
} }

137
dial.go
View File

@ -1,137 +0,0 @@
package tins2020
import (
"math"
"strconv"
"opslag.de/schobers/geom"
"opslag.de/schobers/zntg/ui"
)
type Dial struct {
ui.ContainerBase
dialer Dialer
typing string // current digit
digitCount int // number of times the digit is pressed
digits []DialDigit // digits
}
func NewDial(dialer Dialer) *Dial {
dial := &Dial{dialer: dialer}
dial.digits = make([]DialDigit, 10)
for i := range dial.digits {
j := i
dial.digits[i].Value = strconv.Itoa(i)
dial.digits[i].ControlClicked().AddHandler(func(ctx ui.Context, _ ui.ControlClickedArgs) {
dial.userTyped(ctx, j)
})
dial.AddChild(&dial.digits[i])
}
return dial
}
func (d *Dial) userTyped(ctx ui.Context, i int) {
d.digits[i].Blink()
digit := strconv.Itoa(i)
if len(d.typing) == 0 || digit != d.typing {
d.typing = digit
d.digitCount = 1
} else {
d.digitCount++
}
if !d.dialer.CanUserType(i) {
d.typing = ""
d.digitCount = 0
d.dialer.UserGaveWrongInput()
} else if d.digitCount == i || d.digitCount == 10 {
d.typing = ""
d.digitCount = 0
d.dialer.UserTyped(ctx, i)
}
}
func (d *Dial) Arrange(ctx ui.Context, bounds geom.RectangleF32, offset geom.PointF32, parent ui.Control) {
d.ControlBase.Arrange(ctx, bounds, offset, parent)
center := bounds.Center()
size := bounds.Size()
distance := size.Y * .3
for i := range d.digits {
angle := (float32((10-i)%10)*0.16 + .2) * math.Pi
pos := geom.PtF32(distance*geom.Cos32(angle), .8*distance*geom.Sin32(angle))
digitCenter := center.Add(pos)
d.digits[i].Arrange(ctx, geom.RectRelF32(digitCenter.X-24, digitCenter.Y-24, 48, 48), offset, d)
}
}
func (d *Dial) DesiredSize(ctx ui.Context, size geom.PointF32) geom.PointF32 {
return geom.PtF32(size.X, geom.NaN32())
}
func (d *Dial) Handle(ctx ui.Context, event ui.Event) bool {
if d.ContainerBase.Handle(ctx, event) {
return true
}
switch e := event.(type) {
case *ui.KeyDownEvent:
switch e.Key {
case ui.Key0:
d.userTyped(ctx, 0)
case ui.KeyPad0:
d.userTyped(ctx, 0)
case ui.Key1:
d.userTyped(ctx, 1)
case ui.KeyPad1:
d.userTyped(ctx, 1)
case ui.Key2:
d.userTyped(ctx, 2)
case ui.KeyPad2:
d.userTyped(ctx, 2)
case ui.Key3:
d.userTyped(ctx, 3)
case ui.KeyPad3:
d.userTyped(ctx, 3)
case ui.Key4:
d.userTyped(ctx, 4)
case ui.KeyPad4:
d.userTyped(ctx, 4)
case ui.Key5:
d.userTyped(ctx, 5)
case ui.KeyPad5:
d.userTyped(ctx, 5)
case ui.Key6:
d.userTyped(ctx, 6)
case ui.KeyPad6:
d.userTyped(ctx, 6)
case ui.Key7:
d.userTyped(ctx, 7)
case ui.KeyPad7:
d.userTyped(ctx, 7)
case ui.Key8:
d.userTyped(ctx, 8)
case ui.KeyPad8:
d.userTyped(ctx, 8)
case ui.Key9:
d.userTyped(ctx, 9)
case ui.KeyPad9:
d.userTyped(ctx, 9)
}
}
return false
}
func (d *Dial) Reset() {
d.typing = ""
d.digitCount = 0
}
func (d *Dial) Tick() {
for i := range d.digits {
d.digits[i].Tick()
}
}

View File

@ -1,34 +0,0 @@
package tins2020
import (
"opslag.de/schobers/geom"
"opslag.de/schobers/zntg"
"opslag.de/schobers/zntg/ui"
)
type DialDigit struct {
ui.ControlBase
Value string
highlight int
}
func (d *DialDigit) Blink() {
d.highlight = 4
}
func (d *DialDigit) Render(ctx ui.Context) {
color := zntg.MustHexColor(`#FFFFFF`)
if d.highlight > 0 {
color = zntg.MustHexColor(`#15569F`)
}
bounds := d.Bounds()
ctx.Fonts().TextAlign("title", geom.PtF32(bounds.Center().X, bounds.Min.Y), color, d.Value, ui.AlignCenter)
}
func (d *DialDigit) Tick() {
if d.highlight > 0 {
d.highlight--
}
}

View File

@ -5,80 +5,66 @@ import (
) )
type Dialogs struct { type Dialogs struct {
ui.Proxy
intro ui.Overlay intro ui.Overlay
research ui.Overlay
settings ui.Overlay settings ui.Overlay
nothing ui.Control research ui.Overlay
open string
closed ui.Events closed ui.Events
opened ui.Events opened ui.Events
} }
const dialogsOverlayName = "dialogs" const introDialogName = "dialog-intro"
const settingsDialogName = "dialog-settings"
const researchDialogName = "dialog-research"
func NewDialogs(game *Game) *Dialogs { func NewDialogs(game *Game) *Dialogs {
intro := NewIntro() return &Dialogs{
research := NewResearch(game) intro: NewIntro(),
settings := NewLargeDialog("Settings", &ui.Label{}) settings: &LargeDialog{},
research: NewResearch(game),
dialogs := &Dialogs{
intro: intro,
settings: settings,
research: research,
nothing: &ui.ControlBase{},
} }
intro.CloseRequested().AddHandlerEmpty(dialogs.Close)
research.CloseRequested().AddHandlerEmpty(dialogs.Close)
settings.CloseRequested().AddHandlerEmpty(dialogs.Close)
return dialogs
} }
func (d *Dialogs) Init(ctx ui.Context) { func (d *Dialogs) Init(ctx ui.Context) {
overlays := ctx.Overlays() overlays := ctx.Overlays()
overlays.AddOnTop(dialogsOverlayName, d, false)
d.Content = d.nothing overlays.AddOnTop(introDialogName, d.intro, false)
overlays.AddOnTop(settingsDialogName, d.settings, false)
overlays.AddOnTop(researchDialogName, d.research, false)
} }
func (d *Dialogs) showDialog(ctx ui.Context, control ui.Control) { func (d *Dialogs) showDialog(ctx ui.Context, name string) {
if control == nil { d.Close(ctx)
ctx.Overlays().Hide(dialogsOverlayName)
d.closed.Notify(ctx, control) ctx.Overlays().Show(name)
d.Content = d.nothing d.open = name
} else {
d.Content = control d.opened.Notify(ctx, name)
ctx.Overlays().Show(dialogsOverlayName)
d.opened.Notify(ctx, control)
}
} }
func (d *Dialogs) Close(ctx ui.Context) { func (d *Dialogs) Close(ctx ui.Context) {
d.showDialog(ctx, nil) name := d.open
if name == "" {
return
}
ctx.Overlays().Hide(name)
d.open = ""
d.closed.Notify(ctx, name)
} }
func (d *Dialogs) DialogClosed() ui.EventHandler { return &d.closed } func (d *Dialogs) DialogClosed() ui.EventHandler { return &d.closed }
func (d *Dialogs) DialogOpened() ui.EventHandler { return &d.opened } func (d *Dialogs) DialogOpened() ui.EventHandler { return &d.opened }
func (d *Dialogs) Hidden() {
d.Proxy.Hidden()
}
func (d *Dialogs) ShowIntro(ctx ui.Context) { func (d *Dialogs) ShowIntro(ctx ui.Context) {
d.showDialog(ctx, d.intro) d.showDialog(ctx, introDialogName)
}
func (d *Dialogs) Shown() {
d.Proxy.Shown()
} }
func (d *Dialogs) ShowResearch(ctx ui.Context) { func (d *Dialogs) ShowResearch(ctx ui.Context) {
d.showDialog(ctx, d.research) d.showDialog(ctx, researchDialogName)
} }
func (d *Dialogs) ShowSettings(ctx ui.Context) { func (d *Dialogs) ShowSettings(ctx ui.Context) {
d.showDialog(ctx, d.settings) d.showDialog(ctx, settingsDialogName)
} }

View File

@ -26,10 +26,8 @@ type GameControls struct {
} }
func NewGameControls(game *Game, dialogs *Dialogs) *GameControls { func NewGameControls(game *Game, dialogs *Dialogs) *GameControls {
return &GameControls{game: game, dialogs: dialogs} c := &GameControls{game: game, dialogs: dialogs}
}
func (c *GameControls) Init(ctx ui.Context) {
c.game.SpeedChanged().AddHandler(c.speedChanged) c.game.SpeedChanged().AddHandler(c.speedChanged)
c.game.ToolChanged().AddHandler(c.toolChanged) c.game.ToolChanged().AddHandler(c.toolChanged)
c.dialogs.DialogOpened().AddHandlerEmpty(func(ctx ui.Context) { c.game.Pause(ctx) }) c.dialogs.DialogOpened().AddHandlerEmpty(func(ctx ui.Context) { c.game.Pause(ctx) })
@ -107,6 +105,8 @@ func (c *GameControls) Init(ctx ui.Context) {
c.AddChild(&c.top) c.AddChild(&c.top)
c.AddChild(&c.flowers) c.AddChild(&c.flowers)
c.AddChild(&c.otherTools) c.AddChild(&c.otherTools)
return c
} }
func (c *GameControls) createBuyFlowerButton(id string) *BuyFlowerButton { func (c *GameControls) createBuyFlowerButton(id string) *BuyFlowerButton {

View File

@ -19,6 +19,7 @@ type IconButton struct {
ui.Button ui.Button
IconDisabled string IconDisabled string
IconHeight int32
// IconScale Scale // IconScale Scale
// IconWidth int32 // IconWidth int32

View File

@ -1,18 +1,13 @@
package tins2020 package tins2020
import ( import "opslag.de/schobers/zntg/ui"
"image/color"
"opslag.de/schobers/zntg/ui"
)
type Intro struct { type Intro struct {
ui.Paragraph ui.Paragraph
} }
func NewIntro() *LargeDialog { func NewIntro() ui.Overlay {
i := &Intro{} i := &Intro{}
i.Font.Color = color.White
i.Text = i.Text =
"Welcome to Botanim!\n\n" + "Welcome to Botanim!\n\n" +
"In Botanim you play the role of botanist and your goal is to cultivate flowers in an open landscape.\n\n" + "In Botanim you play the role of botanist and your goal is to cultivate flowers in an open landscape.\n\n" +

View File

@ -1,26 +1,64 @@
package tins2020 package tins2020
import ( import (
"image/color"
"opslag.de/schobers/geom"
"opslag.de/schobers/zntg" "opslag.de/schobers/zntg"
"opslag.de/schobers/zntg/ui" "opslag.de/schobers/zntg/ui"
) )
// type DialogBase struct {
// }
// type Dialog interface {
// CloseDialog()
// OnShow() zntg.EventHandler
// ShowDialog(ui.Context, zntg.EventFn)
// }
// func (d *DialogBase) CloseDialog() {
// close := d.close
// if close != nil {
// close()
// }
// }
// func (d *DialogBase) Init(ctx ui.Context) error {
// d.AddChild(&d.content)
// return d.Container.Init(ctx)
// }
// func (d *DialogBase) OnShow() zntg.EventHandler {
// if d.onShow == nil {
// d.onShow = NewEvents()
// }
// return d.onShow
// }
// func (d *DialogBase) SetContent(control ui.Control) {
// d.content.Proxied = control
// }
// func (d *DialogBase) ShowDialog(ctx ui.Context, close zntg.EventFn) {
// d.close = close
// if d.onShow != nil {
// d.onShow.Notify(ctx)
// }
// }
type LargeDialog struct { type LargeDialog struct {
ui.StackPanel ui.StackPanel
titleBar *LargeDialogTitleBar titleBar *LargeDialogTitleBar
content ui.Proxy content ui.OverlayProxy
closeRequested ui.Events closeRequested ui.Events
onShow zntg.Events
close zntg.EventFn
} }
func NewLargeDialog(title string, content ui.Control) *LargeDialog { func NewLargeDialog(title string, content ui.Control) *LargeDialog {
dialog := &LargeDialog{} dialog := &LargeDialog{}
dialog.Orientation = ui.OrientationVertical
dialog.titleBar = NewLargeDialogTitleBar(title, func(ctx ui.Context, state interface{}) { dialog.titleBar = NewLargeDialogTitleBar(title, func(ctx ui.Context, state interface{}) {
dialog.closeRequested.Notify(ctx, state) dialog.closeRequested.Notify(ctx, state)
}) })
@ -30,39 +68,8 @@ func NewLargeDialog(title string, content ui.Control) *LargeDialog {
return dialog return dialog
} }
func (d *LargeDialog) CloseRequested() ui.EventHandler { return &d.closeRequested }
// func (d *LargeDialog) Arrange(ctx ui.Context, bounds geom.RectangleF32, offset geom.PointF32, parent ui.Control) {
// d.titleBar.Arrange(ctx, bounds.Min.RectRel2D(bounds.Dx(), titleHeight), offset, d)
// d.content.Arrange(ctx, geom.RectRelF32(bounds.Min.X+titleHeight, bounds.Min.Y, bounds.Dx(), bounds.Dy()-titleHeight), offset, d)
// }
func (d *LargeDialog) Handle(ctx ui.Context, e ui.Event) bool {
if d.StackPanel.Handle(ctx, e) {
return true
}
switch e := e.(type) {
case *ui.KeyDownEvent:
switch e.Key {
case ui.KeyEscape:
d.closeRequested.Notify(ctx, nil)
return true
case ui.KeyEnter:
d.closeRequested.Notify(ctx, nil)
return true
}
}
return false
}
func (d *LargeDialog) Hidden() { d.content.Hidden() } func (d *LargeDialog) Hidden() { d.content.Hidden() }
func (d *LargeDialog) Render(ctx ui.Context) {
ctx.Renderer().Clear(zntg.MustHexColor("#356DAD"))
d.StackPanel.Render(ctx)
}
func (d *LargeDialog) Shown() { d.content.Shown() } func (d *LargeDialog) Shown() { d.content.Shown() }
type LargeDialogTitleBar struct { type LargeDialogTitleBar struct {
@ -78,27 +85,57 @@ func NewLargeDialogTitleBar(title string, closeRequested ui.EventFn) *LargeDialo
titleBar.close.ButtonClicked().AddHandler(func(ctx ui.Context, args ui.ControlClickedArgs) { titleBar.close.ButtonClicked().AddHandler(func(ctx ui.Context, args ui.ControlClickedArgs) {
closeRequested(ctx, args) closeRequested(ctx, args)
}) })
titleBar.title.Font.Color = color.White
titleBar.title.Font.Name = "title"
titleBar.title.Text = title
titleBar.title.TextAlignment = ui.AlignCenter
titleBar.close.Icon = "control-cancel"
titleBar.close.IconHeight = 32
titleBar.close.Type = ui.ButtonTypeIcon
return titleBar return titleBar
} }
func (b *LargeDialogTitleBar) Arrange(ctx ui.Context, bounds geom.RectangleF32, offset geom.PointF32, parent ui.Control) { // func (d *LargeDialog) Arrange(ctx ui.Context, bounds Rectangle) {
b.ControlBase.Arrange(ctx, bounds, offset, parent) // const titleHeight = 64
b.title.Arrange(ctx, bounds, offset, parent) // d.ControlBase.Arrange(ctx, bounds)
height := bounds.Dy() // d.title.Arrange(ctx, Rect(bounds.X, bounds.Y, bounds.W, titleHeight))
b.close.Arrange(ctx, geom.RectRelF32(bounds.Max.X-height, bounds.Min.Y, height, height), offset, parent) // d.close.Arrange(ctx, Rect(bounds.W-64, 0, 64, 64))
b.close.HoverColor = ctx.Style().Palette.PrimaryDark // d.content.Arrange(ctx, Rect(bounds.X+titleHeight, 96, bounds.W-2*titleHeight, bounds.H-titleHeight))
} // }
func (b *LargeDialogTitleBar) DesiredSize(ctx ui.Context, size geom.PointF32) geom.PointF32 { // func (d *LargeDialog) Init(ctx ui.Context) error {
const titleHeight = 64 // d.title.Text = "Botanim"
return geom.PtF32(size.X, titleHeight) // d.title.FontName = "title"
} // d.title.Alignment = TextAlignmentCenter
// d.close = IconButton{
// Icon: "control-cancel",
// IconHover: HoverEffectColor,
// IconWidth: 32,
// }
// d.close.OnLeftMouseButtonClick = EmptyEvent(d.CloseDialog)
// d.AddChild(&d.title)
// d.AddChild(&d.close)
// return d.DialogBase.Init(ctx)
// }
// func (d *LargeDialog) Handle(ctx ui.Context, event sdl.Event) bool {
// if d.DialogBase.Handle(ctx, event) {
// return true
// }
// switch e := event.(type) {
// case *sdl.KeyboardEvent:
// if e.Type == sdl.KEYDOWN {
// switch e.Keysym.Sym {
// case sdl.K_ESCAPE:
// d.CloseDialog()
// return true
// case sdl.K_RETURN:
// d.CloseDialog()
// return true
// }
// }
// }
// return false
// }
// func (d *LargeDialog) Render(ctx ui.Context) {
// SetDrawColor(ctx.Renderer, MustHexColor("#356DAD"))
// ctx.Renderer.FillRect(d.Bounds.SDLPtr())
// d.DialogBase.Render(ctx)
// }

View File

@ -2,6 +2,7 @@ package tins2020
import ( import (
"fmt" "fmt"
"math"
"math/rand" "math/rand"
"strconv" "strconv"
"strings" "strings"
@ -14,68 +15,185 @@ import (
) )
type Research struct { type Research struct {
ui.StackPanel ui.ContainerBase
game *Game game *Game
botanist Specialist botanist Specialist
farmer Specialist farmer Specialist
typing string
digitCount int
close func()
description ui.Paragraph description ui.Paragraph
specialists ui.Paragraph specialists ui.Paragraph
dial *Dial
input ui.Label input ui.Label
digits []Digit
animate zntg.Animation animate zntg.Animation
closeRequested ui.Events
} }
type Dialer interface { func NewResearch(game *Game) ui.Overlay {
CanUserType(int) bool
UserGaveWrongInput()
UserTyped(ui.Context, int)
}
func NewResearch(game *Game) *LargeDialog {
research := &Research{game: game} research := &Research{game: game}
research.animate.Interval = 20 * time.Millisecond research.animate.Interval = 20 * time.Millisecond
research.animate.Start()
research.Children = []ui.Control{&research.description, &research.specialists, &research.input}
research.description.Text = "Call a specialist to conduct research with." research.description.Text = "Call a specialist to conduct research with."
research.dial = NewDial(research) research.digits = make([]Digit, 10)
research.Children = []ui.Control{&research.description, &research.specialists, research.dial, &research.input} for i := range research.digits {
j := i
dialog := NewLargeDialog("Research", ui.Stretch(research)) research.digits[i].Value = strconv.Itoa(i)
research.closeRequested.AddHandlerEmpty(func(ctx ui.Context) { dialog.closeRequested.Notify(ctx, nil) }) research.digits[i].ControlClicked().AddHandler(func(ctx ui.Context, _ ui.ControlClickedArgs) {
research.userTyped(ctx, j)
})
research.AddChild(&research.digits[i])
}
dialog := NewLargeDialog("Research", research)
// dialog.OnShow().RegisterItf(func(state interface{}) {
// research.onShow(state.(ui.Context))
// })
// research.close = func() { dialog.CloseDialog() }
return dialog return dialog
} }
type Digit struct {
ui.ControlBase
Value string
highlight int
}
func (d *Digit) Blink() {
d.highlight = 4
}
func (d *Digit) Render(ctx ui.Context) {
color := zntg.MustHexColor(`#FFFFFF`)
if d.highlight > 0 {
color = zntg.MustHexColor(`#15569F`)
}
bounds := d.Bounds()
ctx.Fonts().TextAlign("title", geom.PtF32(bounds.Center().X, bounds.Min.Y), color, d.Value, ui.AlignCenter)
}
func (d *Digit) Tick() {
if d.highlight > 0 {
d.highlight--
}
}
type Specialist struct { type Specialist struct {
Cost int Cost int
Number string Number string
} }
func (r *Research) Arrange(ctx ui.Context, bounds geom.RectangleF32, offset geom.PointF32, parent ui.Control) { func (r *Research) Arrange(ctx ui.Context, bounds geom.RectangleF32, offset geom.PointF32, parent ui.Control) {
r.ContainerBase.Arrange(ctx, bounds, offset, parent)
size := bounds.Size()
r.specialists.Arrange(ctx, geom.RectRelF32(bounds.Min.X, bounds.Min.Y+40, size.X, size.Y-40), offset, r)
r.input.Arrange(ctx, geom.RectRelF32(bounds.Min.X, bounds.Min.X+size.Y-48, size.X, 24), offset, r)
r.input.TextAlignment = ui.AlignCenter r.input.TextAlignment = ui.AlignCenter
r.StackPanel.Arrange(ctx, bounds, offset, parent)
// size := bounds.Size() center := bounds.Center()
// r.specialists.Arrange(ctx, geom.RectRelF32(bounds.Min.X, bounds.Min.Y+40, size.X, size.Y-40), offset, r)
// r.input.Arrange(ctx, geom.RectRelF32(bounds.Min.X, bounds.Min.X+size.Y-48, size.X, 24), offset, r) distance := size.Y * .3
for i := range r.digits {
angle := (float32((10-i)%10)*0.16 + .2) * math.Pi
pos := geom.PtF32(distance*geom.Cos32(angle), .8*distance*geom.Sin32(angle))
digitCenter := center.Add(pos)
r.digits[i].Arrange(ctx, geom.RectRelF32(digitCenter.X-24, digitCenter.Y-24, 48, 48), offset, r)
}
} }
func (r *Research) CanUserType(digit int) bool { func (r *Research) userTyped(ctx ui.Context, i int) {
typing := strconv.Itoa(digit) r.digits[i].Blink()
return strings.HasPrefix(r.botanist.Number, r.input.Text+typing) digit := strconv.Itoa(i)
if len(r.typing) == 0 || digit != r.typing {
r.typing = digit
r.digitCount = 1
} else {
r.digitCount++
}
if !strings.HasPrefix(r.botanist.Number, r.input.Text+r.typing) {
r.input.Text = ""
r.typing = ""
r.digitCount = 0
} else if r.digitCount == i || r.digitCount == 10 {
r.input.Text += digit
r.typing = ""
r.digitCount = 0
if r.input.Text == r.botanist.Number {
r.game.UnlockNextFlower(ctx)
r.close()
r.input.Text = ""
}
}
} }
func (r *Research) Hidden() {} func (r *Research) Handle(ctx ui.Context, event ui.Event) bool {
if r.ContainerBase.Handle(ctx, event) {
return true
}
switch e := event.(type) {
case *ui.KeyDownEvent:
switch e.Key {
case ui.Key0:
r.userTyped(ctx, 0)
case ui.KeyPad0:
r.userTyped(ctx, 0)
case ui.Key1:
r.userTyped(ctx, 1)
case ui.KeyPad1:
r.userTyped(ctx, 1)
case ui.Key2:
r.userTyped(ctx, 2)
case ui.KeyPad2:
r.userTyped(ctx, 2)
case ui.Key3:
r.userTyped(ctx, 3)
case ui.KeyPad3:
r.userTyped(ctx, 3)
case ui.Key4:
r.userTyped(ctx, 4)
case ui.KeyPad4:
r.userTyped(ctx, 4)
case ui.Key5:
r.userTyped(ctx, 5)
case ui.KeyPad5:
r.userTyped(ctx, 5)
case ui.Key6:
r.userTyped(ctx, 6)
case ui.KeyPad6:
r.userTyped(ctx, 6)
case ui.Key7:
r.userTyped(ctx, 7)
case ui.KeyPad7:
r.userTyped(ctx, 7)
case ui.Key8:
r.userTyped(ctx, 8)
case ui.KeyPad8:
r.userTyped(ctx, 8)
case ui.Key9:
r.userTyped(ctx, 9)
case ui.KeyPad9:
r.userTyped(ctx, 9)
}
}
return false
}
func (r *Research) Render(ctx ui.Context) { func (r *Research) Render(ctx ui.Context) {
r.animate.AnimateFn(r.dial.Tick) for i := range r.digits {
r.StackPanel.Render(ctx) r.digits[i].Tick()
}
r.ContainerBase.Render(ctx)
} }
func (r *Research) Shown() { func (r *Research) onShow(ctx ui.Context) {
generateNumber := func() string { generateNumber := func() string {
var number string var number string
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
@ -83,9 +201,8 @@ func (r *Research) Shown() {
} }
return number return number
} }
r.digitCount = 0
r.input.Text = "" r.input.Text = ""
r.dial.Reset()
var specialists string var specialists string
defer func() { defer func() {
@ -109,16 +226,3 @@ func (r *Research) Shown() {
specialists += fmt.Sprintf("Botanist: no. %s (unlocks next flower; $ %d)\n", r.botanist.Number, r.botanist.Cost) specialists += fmt.Sprintf("Botanist: no. %s (unlocks next flower; $ %d)\n", r.botanist.Number, r.botanist.Cost)
specialists += "Farmer: no. **unavailable** (fertilizes land; $ ---)\n" specialists += "Farmer: no. **unavailable** (fertilizes land; $ ---)\n"
} }
func (r *Research) UserGaveWrongInput() {
r.input.Text = ""
}
func (r *Research) UserTyped(ctx ui.Context, digit int) {
r.input.Text += strconv.Itoa(digit)
if r.input.Text == r.botanist.Number {
r.game.UnlockNextFlower(ctx)
r.input.Text = ""
r.closeRequested.Notify(ctx, nil)
}
}