Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
5f2537120d | |||
e4ca42165d |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
|||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
|
|
||||||
|
cmd/tins2020/*rice-box.*
|
||||||
|
@ -49,6 +49,7 @@ func (a *Animation) Run() {
|
|||||||
}
|
}
|
||||||
a.active = true
|
a.active = true
|
||||||
a.start = time.Now()
|
a.start = time.Now()
|
||||||
|
a.lastUpdate = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Animation) SetInterval(interval time.Duration) {
|
func (a *Animation) SetInterval(interval time.Duration) {
|
||||||
|
@ -9,6 +9,9 @@ import (
|
|||||||
"opslag.de/schobers/tins2020"
|
"opslag.de/schobers/tins2020"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:generate go get -u github.com/GeertJohan/go.rice/rice
|
||||||
|
//go:generate rice embed
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
err := run()
|
err := run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -96,7 +99,7 @@ func run() error {
|
|||||||
app := tins2020.NewContainer()
|
app := tins2020.NewContainer()
|
||||||
|
|
||||||
overlays := tins2020.NewContainer()
|
overlays := tins2020.NewContainer()
|
||||||
dialogs := tins2020.NewDialogs()
|
dialogs := tins2020.NewDialogs(game)
|
||||||
|
|
||||||
overlays.AddChild(dialogs)
|
overlays.AddChild(dialogs)
|
||||||
overlays.AddChild(&tins2020.FPS{Show: &game.Debug})
|
overlays.AddChild(&tins2020.FPS{Show: &game.Debug})
|
||||||
@ -112,7 +115,7 @@ func run() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dialogs.ShowIntro()
|
dialogs.ShowIntro(ctx)
|
||||||
|
|
||||||
w, h := window.GetSize()
|
w, h := window.GetSize()
|
||||||
app.Arrange(ctx, tins2020.Rect(0, 0, w, h))
|
app.Arrange(ctx, tins2020.Rect(0, 0, w, h))
|
||||||
|
8
color.go
8
color.go
@ -11,6 +11,14 @@ var Transparent = sdl.Color{R: 0, G: 0, B: 0, A: 0}
|
|||||||
var TransparentWhite = sdl.Color{R: 255, G: 255, B: 255, A: 31}
|
var TransparentWhite = sdl.Color{R: 255, G: 255, B: 255, A: 31}
|
||||||
var White = sdl.Color{R: 255, G: 255, B: 255, A: 255}
|
var White = sdl.Color{R: 255, G: 255, B: 255, A: 255}
|
||||||
|
|
||||||
|
func HexColor(s string) (sdl.Color, error) {
|
||||||
|
c, err := img.HexColor(s)
|
||||||
|
if err != nil {
|
||||||
|
return sdl.Color{}, err
|
||||||
|
}
|
||||||
|
return sdl.Color(c), nil
|
||||||
|
}
|
||||||
|
|
||||||
func MustHexColor(s string) sdl.Color { return sdl.Color(img.MustHexColor(s)) }
|
func MustHexColor(s string) sdl.Color { return sdl.Color(img.MustHexColor(s)) }
|
||||||
|
|
||||||
func SetDrawColor(renderer *sdl.Renderer, color sdl.Color) {
|
func SetDrawColor(renderer *sdl.Renderer, color sdl.Color) {
|
||||||
|
16
control.go
16
control.go
@ -22,6 +22,7 @@ func EmptyEvent(fn EventFn) EventContextFn {
|
|||||||
type ControlBase struct {
|
type ControlBase struct {
|
||||||
Bounds Rectangle
|
Bounds Rectangle
|
||||||
|
|
||||||
|
IsDisabled bool
|
||||||
IsMouseOver bool
|
IsMouseOver bool
|
||||||
|
|
||||||
OnLeftMouseButtonClick EventContextFn
|
OnLeftMouseButtonClick EventContextFn
|
||||||
@ -32,30 +33,27 @@ func (b *ControlBase) Arrange(ctx *Context, bounds Rectangle) { b.Bounds = bound
|
|||||||
func (b *ControlBase) Init(*Context) error { return nil }
|
func (b *ControlBase) Init(*Context) error { return nil }
|
||||||
|
|
||||||
func (b *ControlBase) Handle(ctx *Context, event sdl.Event) bool {
|
func (b *ControlBase) Handle(ctx *Context, event sdl.Event) bool {
|
||||||
b.HandleNoFeedback(ctx, event)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *ControlBase) HandleNoFeedback(ctx *Context, event sdl.Event) {
|
|
||||||
switch e := event.(type) {
|
switch e := event.(type) {
|
||||||
case *sdl.MouseMotionEvent:
|
case *sdl.MouseMotionEvent:
|
||||||
b.IsMouseOver = b.Bounds.IsPointInside(e.X, e.Y)
|
b.IsMouseOver = b.Bounds.IsPointInside(e.X, e.Y)
|
||||||
case *sdl.MouseButtonEvent:
|
case *sdl.MouseButtonEvent:
|
||||||
if b.IsMouseOver && e.Button == sdl.BUTTON_LEFT && e.Type == sdl.MOUSEBUTTONDOWN {
|
if !b.IsDisabled && b.IsMouseOver && e.Button == sdl.BUTTON_LEFT && e.Type == sdl.MOUSEBUTTONDOWN {
|
||||||
b.Invoke(ctx, b.OnLeftMouseButtonClick)
|
return b.Invoke(ctx, b.OnLeftMouseButtonClick)
|
||||||
}
|
}
|
||||||
case *sdl.WindowEvent:
|
case *sdl.WindowEvent:
|
||||||
if e.Event == sdl.WINDOWEVENT_LEAVE {
|
if e.Event == sdl.WINDOWEVENT_LEAVE {
|
||||||
b.IsMouseOver = false
|
b.IsMouseOver = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *ControlBase) Invoke(ctx *Context, fn EventContextFn) {
|
func (b *ControlBase) Invoke(ctx *Context, fn EventContextFn) bool {
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
fn(ctx)
|
fn(ctx)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *ControlBase) Render(*Context) {}
|
func (b *ControlBase) Render(*Context) {}
|
||||||
|
39
dialogs.go
39
dialogs.go
@ -5,25 +5,37 @@ type Dialogs struct {
|
|||||||
|
|
||||||
intro Control
|
intro Control
|
||||||
settings Control
|
settings Control
|
||||||
|
research Control
|
||||||
|
|
||||||
dialogClosed *Events
|
dialogClosed *Events
|
||||||
dialogOpened *Events
|
dialogOpened *Events
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDialogs() *Dialogs {
|
func NewDialogs(game *Game) *Dialogs {
|
||||||
return &Dialogs{
|
return &Dialogs{
|
||||||
|
intro: &Intro{},
|
||||||
|
settings: &LargeDialog{},
|
||||||
|
research: NewResearch(game),
|
||||||
|
|
||||||
dialogClosed: NewEvents(),
|
dialogClosed: NewEvents(),
|
||||||
dialogOpened: NewEvents(),
|
dialogOpened: NewEvents(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Dialogs) showDialog(ctx *Context, control Control) {
|
||||||
|
d.SetContent(ctx, control)
|
||||||
|
control.(Dialog).ShowDialog(ctx, d.Close)
|
||||||
|
d.dialogOpened.Notify(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dialogs) Arrange(ctx *Context, bounds Rectangle) {
|
||||||
|
d.Proxy.Arrange(ctx, bounds)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Dialogs) DialogClosed() EventHandler { return d.dialogClosed }
|
func (d *Dialogs) DialogClosed() EventHandler { return d.dialogClosed }
|
||||||
func (d *Dialogs) DialogOpened() EventHandler { return d.dialogOpened }
|
func (d *Dialogs) DialogOpened() EventHandler { return d.dialogOpened }
|
||||||
|
|
||||||
func (d *Dialogs) Init(ctx *Context) error {
|
func (d *Dialogs) Init(ctx *Context) error {
|
||||||
d.intro = &Intro{}
|
|
||||||
d.settings = &DialogBase{}
|
|
||||||
|
|
||||||
err := d.intro.Init(ctx)
|
err := d.intro.Init(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -32,22 +44,23 @@ func (d *Dialogs) Init(ctx *Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = d.research.Init(ctx)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Dialogs) Close() {
|
func (d *Dialogs) Close() {
|
||||||
d.Proxied = nil
|
d.SetContent(nil, nil)
|
||||||
d.dialogClosed.Notify(nil)
|
d.dialogClosed.Notify(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Dialogs) ShowIntro() {
|
func (d *Dialogs) ShowIntro(ctx *Context) {
|
||||||
d.Proxied = d.intro
|
d.showDialog(ctx, d.intro)
|
||||||
d.intro.(Dialog).ShowDialog(d.Close)
|
|
||||||
d.dialogOpened.Notify(nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Dialogs) ShowSettings() {
|
func (d *Dialogs) ShowResearch(ctx *Context) {
|
||||||
d.Proxied = d.settings
|
d.showDialog(ctx, d.research)
|
||||||
d.settings.(Dialog).ShowDialog(d.Close)
|
}
|
||||||
d.dialogOpened.Notify(nil)
|
|
||||||
|
func (d *Dialogs) ShowSettings(ctx *Context) {
|
||||||
|
d.showDialog(ctx, d.settings)
|
||||||
}
|
}
|
||||||
|
15
game.go
15
game.go
@ -163,6 +163,11 @@ func (g *Game) Load() {
|
|||||||
func (g *Game) Pause() { g.setSpeed(GameSpeedPaused) }
|
func (g *Game) Pause() { g.setSpeed(GameSpeedPaused) }
|
||||||
|
|
||||||
func (g *Game) PlantFlower(id string, tile Point) {
|
func (g *Game) PlantFlower(id string, tile Point) {
|
||||||
|
if g.Terrain.HasFlower(tile) {
|
||||||
|
// TODO: notify user it tried to plant on tile with flower?
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
flower, ok := g.Herbarium.Find(id)
|
flower, ok := g.Herbarium.Find(id)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Println("user was able to plant a flower that doesn't exist")
|
log.Println("user was able to plant a flower that doesn't exist")
|
||||||
@ -213,10 +218,6 @@ func (g *Game) SelectShovel() {
|
|||||||
g.selectTool(&ShovelTool{})
|
g.selectTool(&ShovelTool{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) SelectResearch() {
|
|
||||||
g.Pause()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Game) SpeedChanged() EventHandler { return g.speedChanged }
|
func (g *Game) SpeedChanged() EventHandler { return g.speedChanged }
|
||||||
|
|
||||||
func (g *Game) State() GameState {
|
func (g *Game) State() GameState {
|
||||||
@ -258,6 +259,12 @@ func (g *Game) Tool() Tool { return g.tool }
|
|||||||
|
|
||||||
func (g *Game) ToolChanged() EventHandler { return g.toolChanged }
|
func (g *Game) ToolChanged() EventHandler { return g.toolChanged }
|
||||||
|
|
||||||
|
func (g *Game) UnlockNextFlower() {
|
||||||
|
price := g.Herbarium.UnlockNext()
|
||||||
|
g.Balance -= price
|
||||||
|
g.selectTool(nil)
|
||||||
|
}
|
||||||
|
|
||||||
func (g *Game) Update() {
|
func (g *Game) Update() {
|
||||||
for g.simulation.Animate() {
|
for g.simulation.Animate() {
|
||||||
g.tick()
|
g.tick()
|
||||||
|
@ -87,7 +87,10 @@ func (c *GameControls) Init(ctx *Context) error {
|
|||||||
c.game.SpeedChanged().RegisterItf(c.speedChanged)
|
c.game.SpeedChanged().RegisterItf(c.speedChanged)
|
||||||
c.game.ToolChanged().RegisterItf(c.toolChanged)
|
c.game.ToolChanged().RegisterItf(c.toolChanged)
|
||||||
c.dialogs.DialogOpened().Register(func() { c.game.Pause() })
|
c.dialogs.DialogOpened().Register(func() { c.game.Pause() })
|
||||||
c.dialogs.DialogClosed().Register(func() { c.game.Resume() })
|
c.dialogs.DialogClosed().Register(func() {
|
||||||
|
c.updateFlowerControls(ctx)
|
||||||
|
c.game.Resume()
|
||||||
|
})
|
||||||
|
|
||||||
c.flowers.Background = MustHexColor("#356dad")
|
c.flowers.Background = MustHexColor("#356dad")
|
||||||
c.flowers.ButtonLength = 64
|
c.flowers.ButtonLength = 64
|
||||||
@ -117,7 +120,10 @@ func (c *GameControls) Init(ctx *Context) error {
|
|||||||
|
|
||||||
c.menu.Background = MustHexColor("#356dad")
|
c.menu.Background = MustHexColor("#356dad")
|
||||||
c.menu.Buttons = []Control{
|
c.menu.Buttons = []Control{
|
||||||
NewIconButton("control-settings", func(*Context) { c.dialogs.ShowSettings() }),
|
NewIconButtonConfig("control-settings", c.dialogs.ShowSettings, func(b *IconButton) {
|
||||||
|
b.IsDisabled = true
|
||||||
|
b.IconDisabled = "#afafaf"
|
||||||
|
}),
|
||||||
NewIconButton("control-save", func(*Context) { c.game.Save() }),
|
NewIconButton("control-save", func(*Context) { c.game.Save() }),
|
||||||
NewIconButton("control-load", func(ctx *Context) {
|
NewIconButton("control-load", func(ctx *Context) {
|
||||||
c.game.Load()
|
c.game.Load()
|
||||||
@ -127,11 +133,11 @@ func (c *GameControls) Init(ctx *Context) error {
|
|||||||
c.game.New()
|
c.game.New()
|
||||||
c.updateFlowerControls(ctx)
|
c.updateFlowerControls(ctx)
|
||||||
}),
|
}),
|
||||||
NewIconButton("control-information", func(*Context) { c.dialogs.ShowIntro() }),
|
NewIconButton("control-information", c.dialogs.ShowIntro),
|
||||||
}
|
}
|
||||||
|
|
||||||
c.shovel = NewIconButtonConfig("control-shovel", func(*Context) { c.game.SelectShovel() }, func(b *IconButton) { b.IconHeight = 32 })
|
c.shovel = NewIconButtonConfig("control-shovel", func(*Context) { c.game.SelectShovel() }, func(b *IconButton) { b.IconHeight = 32 })
|
||||||
c.research = NewIconButtonConfig("control-research", func(*Context) { c.game.SelectResearch() }, func(b *IconButton) { b.IconHeight = 32 })
|
c.research = NewIconButtonConfig("control-research", c.dialogs.ShowResearch, func(b *IconButton) { b.IconHeight = 32 })
|
||||||
c.otherTools.Buttons = []Control{c.shovel, c.research}
|
c.otherTools.Buttons = []Control{c.shovel, c.research}
|
||||||
|
|
||||||
c.Container.AddChild(&c.menu)
|
c.Container.AddChild(&c.menu)
|
||||||
@ -160,10 +166,10 @@ func (c *GameControls) Handle(ctx *Context, event sdl.Event) bool {
|
|||||||
case sdl.K_d:
|
case sdl.K_d:
|
||||||
c.game.SelectShovel()
|
c.game.SelectShovel()
|
||||||
case sdl.K_r:
|
case sdl.K_r:
|
||||||
c.game.SelectResearch()
|
c.dialogs.ShowResearch(ctx)
|
||||||
case sdl.K_ESCAPE:
|
case sdl.K_ESCAPE:
|
||||||
if c.game.Tool() == nil {
|
if c.game.Tool() == nil {
|
||||||
c.dialogs.ShowIntro()
|
c.dialogs.ShowIntro(ctx)
|
||||||
} else {
|
} else {
|
||||||
c.game.CancelTool()
|
c.game.CancelTool()
|
||||||
}
|
}
|
||||||
|
33
herbarium.go
33
herbarium.go
@ -36,7 +36,7 @@ func (h *Herbarium) Reset() {
|
|||||||
BuyPrice: 100,
|
BuyPrice: 100,
|
||||||
SellPrice: 20,
|
SellPrice: 20,
|
||||||
Traits: NewLoosestrifeTraits(),
|
Traits: NewLoosestrifeTraits(),
|
||||||
Unlocked: true,
|
Unlocked: false,
|
||||||
})
|
})
|
||||||
h.Add("coneflower", FlowerDescriptor{
|
h.Add("coneflower", FlowerDescriptor{
|
||||||
Name: "Coneflower",
|
Name: "Coneflower",
|
||||||
@ -45,7 +45,7 @@ func (h *Herbarium) Reset() {
|
|||||||
BuyPrice: 500,
|
BuyPrice: 500,
|
||||||
SellPrice: 100,
|
SellPrice: 100,
|
||||||
Traits: NewConeflowerTraits(),
|
Traits: NewConeflowerTraits(),
|
||||||
Unlocked: true,
|
Unlocked: false,
|
||||||
})
|
})
|
||||||
h.Add("tulip", FlowerDescriptor{
|
h.Add("tulip", FlowerDescriptor{
|
||||||
Name: "Tulip",
|
Name: "Tulip",
|
||||||
@ -54,7 +54,7 @@ func (h *Herbarium) Reset() {
|
|||||||
BuyPrice: 20000,
|
BuyPrice: 20000,
|
||||||
SellPrice: 5000,
|
SellPrice: 5000,
|
||||||
Traits: NewTulipTraits(),
|
Traits: NewTulipTraits(),
|
||||||
Unlocked: true,
|
Unlocked: false,
|
||||||
})
|
})
|
||||||
h.Add("ajuga", FlowerDescriptor{
|
h.Add("ajuga", FlowerDescriptor{
|
||||||
Name: "Ajuga",
|
Name: "Ajuga",
|
||||||
@ -63,7 +63,7 @@ func (h *Herbarium) Reset() {
|
|||||||
BuyPrice: 100000,
|
BuyPrice: 100000,
|
||||||
SellPrice: 10000,
|
SellPrice: 10000,
|
||||||
Traits: NewAjugaTraits(),
|
Traits: NewAjugaTraits(),
|
||||||
Unlocked: true,
|
Unlocked: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,3 +114,28 @@ func (h *Herbarium) IsUnlocked(id string) bool {
|
|||||||
}
|
}
|
||||||
return flower.Unlocked
|
return flower.Unlocked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Herbarium) NextFlowerToUnlock() (string, FlowerDescriptor, int) {
|
||||||
|
var previous *FlowerDescriptor
|
||||||
|
for _, id := range h.Flowers() {
|
||||||
|
flower, _ := h.Find(id)
|
||||||
|
if !flower.Unlocked {
|
||||||
|
if previous == nil {
|
||||||
|
return "", FlowerDescriptor{}, 0
|
||||||
|
}
|
||||||
|
return id, flower, previous.BuyPrice * 2
|
||||||
|
}
|
||||||
|
previous = &flower
|
||||||
|
}
|
||||||
|
return "", FlowerDescriptor{}, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Herbarium) UnlockNext() int {
|
||||||
|
id, flower, price := h.NextFlowerToUnlock()
|
||||||
|
if len(id) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
flower.Unlocked = true
|
||||||
|
h.flowers[id] = flower
|
||||||
|
return price
|
||||||
|
}
|
||||||
|
@ -20,7 +20,6 @@ type IconButton struct {
|
|||||||
IconHover HoverEffect
|
IconHover HoverEffect
|
||||||
|
|
||||||
IsActive bool
|
IsActive bool
|
||||||
IsDisabled bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIconButton(icon string, onClick EventContextFn) *IconButton {
|
func NewIconButton(icon string, onClick EventContextFn) *IconButton {
|
||||||
@ -44,6 +43,16 @@ func (b *IconButton) activeTexture(ctx *Context) *Texture {
|
|||||||
if texture != nil {
|
if texture != nil {
|
||||||
return texture
|
return texture
|
||||||
}
|
}
|
||||||
|
|
||||||
|
texture = ctx.Textures.Texture(b.Icon)
|
||||||
|
if len(b.IconDisabled) == 0 {
|
||||||
|
return texture
|
||||||
|
}
|
||||||
|
color, err := HexColor(b.IconDisabled)
|
||||||
|
if err == nil {
|
||||||
|
texture.SetColor(color)
|
||||||
|
}
|
||||||
|
return texture
|
||||||
}
|
}
|
||||||
return ctx.Textures.Texture(b.Icon)
|
return ctx.Textures.Texture(b.Icon)
|
||||||
}
|
}
|
||||||
|
4
label.go
4
label.go
@ -48,10 +48,6 @@ type Paragraph struct {
|
|||||||
Label
|
Label
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (p *Paragraph) Arrange(ctx *Context, bounds Rectangle) {
|
|
||||||
// p.Label.Arrange(ctx, bounds)
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (p *Paragraph) Render(ctx *Context) {
|
func (p *Paragraph) Render(ctx *Context) {
|
||||||
font := ctx.Fonts.Font(p.fontName())
|
font := ctx.Fonts.Font(p.fontName())
|
||||||
color := p.fontColor()
|
color := p.fontColor()
|
||||||
|
@ -6,12 +6,14 @@ type DialogBase struct {
|
|||||||
Container
|
Container
|
||||||
|
|
||||||
content Proxy
|
content Proxy
|
||||||
|
onShow *Events
|
||||||
close EventFn
|
close EventFn
|
||||||
}
|
}
|
||||||
|
|
||||||
type Dialog interface {
|
type Dialog interface {
|
||||||
CloseDialog()
|
CloseDialog()
|
||||||
ShowDialog(EventFn)
|
OnShow() EventHandler
|
||||||
|
ShowDialog(*Context, EventFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DialogBase) CloseDialog() {
|
func (d *DialogBase) CloseDialog() {
|
||||||
@ -21,17 +23,27 @@ func (d *DialogBase) CloseDialog() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DialogBase) Init(ctx *Context) error {
|
||||||
|
d.AddChild(&d.content)
|
||||||
|
return d.Container.Init(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogBase) OnShow() EventHandler {
|
||||||
|
if d.onShow == nil {
|
||||||
|
d.onShow = NewEvents()
|
||||||
|
}
|
||||||
|
return d.onShow
|
||||||
|
}
|
||||||
|
|
||||||
func (d *DialogBase) SetContent(control Control) {
|
func (d *DialogBase) SetContent(control Control) {
|
||||||
d.content.Proxied = control
|
d.content.Proxied = control
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DialogBase) ShowDialog(close EventFn) {
|
func (d *DialogBase) ShowDialog(ctx *Context, close EventFn) {
|
||||||
d.close = close
|
d.close = close
|
||||||
}
|
if d.onShow != nil {
|
||||||
|
d.onShow.Notify(ctx)
|
||||||
func (d *DialogBase) Init(ctx *Context) error {
|
}
|
||||||
d.AddChild(&d.content)
|
|
||||||
return d.Container.Init(ctx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type LargeDialog struct {
|
type LargeDialog struct {
|
||||||
@ -93,3 +105,5 @@ func (d *LargeDialog) Render(ctx *Context) {
|
|||||||
|
|
||||||
d.DialogBase.Render(ctx)
|
d.DialogBase.Render(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *LargeDialog) SetCaption(s string) { d.title.Text = s }
|
||||||
|
5
map.go
5
map.go
@ -24,6 +24,11 @@ func (m *Map) DigFlower(pos Point) string {
|
|||||||
return flower.ID
|
return flower.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Map) HasFlower(pos Point) bool {
|
||||||
|
_, ok := m.Flowers[pos]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Map) NewFlower(pos Point, id string, traits FlowerTraits) Flower {
|
func (m *Map) NewFlower(pos Point, id string, traits FlowerTraits) Flower {
|
||||||
flower := Flower{
|
flower := Flower{
|
||||||
ID: id,
|
ID: id,
|
||||||
|
11
proxy.go
11
proxy.go
@ -6,9 +6,12 @@ var _ Control = &Proxy{}
|
|||||||
|
|
||||||
type Proxy struct {
|
type Proxy struct {
|
||||||
Proxied Control
|
Proxied Control
|
||||||
|
|
||||||
|
bounds Rectangle
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Proxy) Arrange(ctx *Context, bounds Rectangle) {
|
func (p *Proxy) Arrange(ctx *Context, bounds Rectangle) {
|
||||||
|
p.bounds = bounds
|
||||||
if p.Proxied == nil {
|
if p.Proxied == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -35,3 +38,11 @@ func (p *Proxy) Render(ctx *Context) {
|
|||||||
}
|
}
|
||||||
p.Proxied.Render(ctx)
|
p.Proxied.Render(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) SetContent(ctx *Context, content Control) {
|
||||||
|
p.Proxied = content
|
||||||
|
if content == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
content.Arrange(ctx, p.bounds)
|
||||||
|
}
|
||||||
|
227
research.go
Normal file
227
research.go
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
package tins2020
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Research struct {
|
||||||
|
Container
|
||||||
|
|
||||||
|
game *Game
|
||||||
|
botanist Specialist
|
||||||
|
farmer Specialist
|
||||||
|
|
||||||
|
typing string
|
||||||
|
digitCount int
|
||||||
|
|
||||||
|
close func()
|
||||||
|
description Paragraph
|
||||||
|
specialists Paragraph
|
||||||
|
input Label
|
||||||
|
digits []Digit
|
||||||
|
animate Animation
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewResearch(game *Game) Control {
|
||||||
|
research := &Research{
|
||||||
|
game: game,
|
||||||
|
animate: NewAnimation(20 * time.Millisecond),
|
||||||
|
}
|
||||||
|
dialog := &LargeDialog{}
|
||||||
|
dialog.SetCaption("Research")
|
||||||
|
dialog.SetContent(research)
|
||||||
|
dialog.OnShow().RegisterItf(func(state interface{}) {
|
||||||
|
research.onShow(state.(*Context))
|
||||||
|
})
|
||||||
|
research.close = func() { dialog.CloseDialog() }
|
||||||
|
return dialog
|
||||||
|
}
|
||||||
|
|
||||||
|
type Digit struct {
|
||||||
|
ControlBase
|
||||||
|
|
||||||
|
Value string
|
||||||
|
|
||||||
|
highlight int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Digit) Render(ctx *Context) {
|
||||||
|
font := ctx.Fonts.Font("title")
|
||||||
|
color := White
|
||||||
|
if d.highlight > 0 {
|
||||||
|
color = MustHexColor("#15569F")
|
||||||
|
}
|
||||||
|
font.RenderCopyAlign(ctx.Renderer, d.Value, Pt(d.Bounds.X+d.Bounds.W/2, d.Bounds.Y+int32(font.Height())), color, TextAlignmentCenter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Digit) Blink() {
|
||||||
|
d.highlight = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Digit) Tick() {
|
||||||
|
if d.highlight > 0 {
|
||||||
|
d.highlight--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Specialist struct {
|
||||||
|
Cost int
|
||||||
|
Number string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Research) Init(ctx *Context) error {
|
||||||
|
r.AddChild(&r.description)
|
||||||
|
r.AddChild(&r.specialists)
|
||||||
|
r.AddChild(&r.input)
|
||||||
|
r.description.Text = "Call a specialist to conduct research with."
|
||||||
|
r.digits = make([]Digit, 10)
|
||||||
|
for i := range r.digits {
|
||||||
|
r.digits[i].Value = strconv.Itoa(i)
|
||||||
|
r.AddChild(&r.digits[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Research) Arrange(ctx *Context, bounds Rectangle) {
|
||||||
|
r.Container.Arrange(ctx, bounds)
|
||||||
|
r.specialists.Arrange(ctx, RectSize(r.Bounds.X, r.Bounds.Y+40, r.Bounds.W, r.Bounds.H-40))
|
||||||
|
r.input.Arrange(ctx, RectSize(r.Bounds.X, r.Bounds.X+r.Bounds.H-48, r.Bounds.W, 24))
|
||||||
|
r.input.Alignment = TextAlignmentCenter
|
||||||
|
|
||||||
|
center := Pt(r.Bounds.X+r.Bounds.W/2, r.Bounds.Y+r.Bounds.H/2)
|
||||||
|
|
||||||
|
distance := float64(bounds.H) * .3
|
||||||
|
for i := range r.digits {
|
||||||
|
angle := (float64((10-i)%10)*0.16 + .2) * math.Pi
|
||||||
|
pos := Pt(int32(distance*math.Cos(angle)), int32(.8*distance*math.Sin(angle)))
|
||||||
|
digitCenter := center.Add(pos)
|
||||||
|
r.digits[i].Arrange(ctx, RectSize(digitCenter.X-24, digitCenter.Y-24, 48, 48))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Research) userTyped(i int) {
|
||||||
|
r.digits[i].Blink()
|
||||||
|
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()
|
||||||
|
r.close()
|
||||||
|
r.input.Text = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Research) Handle(ctx *Context, event sdl.Event) bool {
|
||||||
|
switch e := event.(type) {
|
||||||
|
case *sdl.KeyboardEvent:
|
||||||
|
if e.Type == sdl.KEYDOWN {
|
||||||
|
switch e.Keysym.Sym {
|
||||||
|
case sdl.K_0:
|
||||||
|
r.userTyped(0)
|
||||||
|
case sdl.K_KP_0:
|
||||||
|
r.userTyped(0)
|
||||||
|
case sdl.K_1:
|
||||||
|
r.userTyped(1)
|
||||||
|
case sdl.K_KP_1:
|
||||||
|
r.userTyped(1)
|
||||||
|
case sdl.K_2:
|
||||||
|
r.userTyped(2)
|
||||||
|
case sdl.K_KP_2:
|
||||||
|
r.userTyped(2)
|
||||||
|
case sdl.K_3:
|
||||||
|
r.userTyped(3)
|
||||||
|
case sdl.K_KP_3:
|
||||||
|
r.userTyped(3)
|
||||||
|
case sdl.K_4:
|
||||||
|
r.userTyped(4)
|
||||||
|
case sdl.K_KP_4:
|
||||||
|
r.userTyped(4)
|
||||||
|
case sdl.K_5:
|
||||||
|
r.userTyped(5)
|
||||||
|
case sdl.K_KP_5:
|
||||||
|
r.userTyped(5)
|
||||||
|
case sdl.K_6:
|
||||||
|
r.userTyped(6)
|
||||||
|
case sdl.K_KP_6:
|
||||||
|
r.userTyped(6)
|
||||||
|
case sdl.K_7:
|
||||||
|
r.userTyped(7)
|
||||||
|
case sdl.K_KP_7:
|
||||||
|
r.userTyped(7)
|
||||||
|
case sdl.K_8:
|
||||||
|
r.userTyped(8)
|
||||||
|
case sdl.K_KP_8:
|
||||||
|
r.userTyped(8)
|
||||||
|
case sdl.K_9:
|
||||||
|
r.userTyped(9)
|
||||||
|
case sdl.K_KP_9:
|
||||||
|
r.userTyped(9)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Research) Render(ctx *Context) {
|
||||||
|
for i := range r.digits {
|
||||||
|
r.digits[i].Tick()
|
||||||
|
}
|
||||||
|
r.Container.Render(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Research) onShow(ctx *Context) {
|
||||||
|
generateNumber := func() string {
|
||||||
|
var number string
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
number += strconv.Itoa(rand.Intn(9) + 1)
|
||||||
|
}
|
||||||
|
return number
|
||||||
|
}
|
||||||
|
r.digitCount = 0
|
||||||
|
r.input.Text = ""
|
||||||
|
|
||||||
|
var specialists string
|
||||||
|
defer func() {
|
||||||
|
r.specialists.Text = specialists
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, _, price := r.game.Herbarium.NextFlowerToUnlock()
|
||||||
|
if price == 0 {
|
||||||
|
specialists += "Botanist (unlocks next flower; unavailable)\n"
|
||||||
|
specialists += "Farmer (fertilizes land; unavailable)\n"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.botanist.Cost = price
|
||||||
|
if r.game.Balance < r.botanist.Cost {
|
||||||
|
r.botanist.Number = "**unavailable**"
|
||||||
|
} else {
|
||||||
|
r.botanist.Number = generateNumber()
|
||||||
|
}
|
||||||
|
|
||||||
|
specialists += fmt.Sprintf("Botanist: no. %s (unlocks next flower; $ %d)\n", r.botanist.Number, r.botanist.Cost)
|
||||||
|
specialists += "Farmer: no. **unavailable** (fertilizes land; $ ---)\n"
|
||||||
|
}
|
@ -34,7 +34,7 @@ func (r *terrainRenderer) Init(ctx *Context) error {
|
|||||||
|
|
||||||
func isControlKeyDown() bool {
|
func isControlKeyDown() bool {
|
||||||
state := sdl.GetKeyboardState()
|
state := sdl.GetKeyboardState()
|
||||||
return state[sdl.SCANCODE_LCTRL] == 1 || state[sdl.SCANCODE_RCTRL] == 1
|
return state[sdl.SCANCODE_LCTRL] == 1 || state[sdl.SCANCODE_RCTRL] == 1 || state[sdl.SCANCODE_LGUI] == 1 || state[sdl.SCANCODE_RGUI] == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) bool {
|
func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) bool {
|
||||||
@ -42,12 +42,13 @@ func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) bool {
|
|||||||
case *sdl.MouseButtonEvent:
|
case *sdl.MouseButtonEvent:
|
||||||
if r.project.windowInteractRect.IsPointInside(e.X, e.Y) {
|
if r.project.windowInteractRect.IsPointInside(e.X, e.Y) {
|
||||||
if e.Type == sdl.MOUSEBUTTONDOWN {
|
if e.Type == sdl.MOUSEBUTTONDOWN {
|
||||||
if e.Button == sdl.BUTTON_MIDDLE || (e.Button == sdl.BUTTON_LEFT && isControlKeyDown()) {
|
controlKeyDown := isControlKeyDown()
|
||||||
|
if e.Button == sdl.BUTTON_MIDDLE || (e.Button == sdl.BUTTON_LEFT && controlKeyDown) {
|
||||||
if !r.drag.IsDragging() {
|
if !r.drag.IsDragging() {
|
||||||
r.drag.Start(Pt(e.X, e.Y))
|
r.drag.Start(Pt(e.X, e.Y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if e.Button == sdl.BUTTON_LEFT {
|
if e.Button == sdl.BUTTON_LEFT && !controlKeyDown {
|
||||||
pos := r.project.screenToMapInt(e.X, e.Y)
|
pos := r.project.screenToMapInt(e.X, e.Y)
|
||||||
r.game.UserClickedTile(pos)
|
r.game.UserClickedTile(pos)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user