Compare commits
3 Commits
a72f416650
...
5ebd582498
Author | SHA1 | Date | |
---|---|---|---|
5ebd582498 | |||
a6004a8ab6 | |||
9641719579 |
@ -45,12 +45,8 @@ func (b *ButtonBar) Arrange(ctx *Context, bounds Rectangle) {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *ButtonBar) Handle(ctx *Context, event sdl.Event) {
|
||||
b.Container.Handle(ctx, event)
|
||||
}
|
||||
|
||||
func (b *ButtonBar) Render(ctx *Context) {
|
||||
ctx.Renderer.SetDrawColor(b.Background.R, b.Background.G, b.Background.B, b.Background.A)
|
||||
SetDrawColor(ctx.Renderer, b.Background)
|
||||
ctx.Renderer.FillRect(b.Bounds.SDLPtr())
|
||||
b.Container.Render(ctx)
|
||||
}
|
||||
|
@ -71,24 +71,27 @@ func (b *BuyFlowerButton) Init(ctx *Context) error {
|
||||
return b.updateTexts(ctx)
|
||||
}
|
||||
|
||||
func (b *BuyFlowerButton) Handle(ctx *Context, event sdl.Event) {
|
||||
b.IconButton.Handle(ctx, event)
|
||||
func (b *BuyFlowerButton) Handle(ctx *Context, event sdl.Event) bool {
|
||||
if b.IconButton.Handle(ctx, event) {
|
||||
return true
|
||||
}
|
||||
if b.IsMouseOver && b.hoverAnimation == nil {
|
||||
b.hoverAnimation = NewAnimationPtr(10 * time.Millisecond)
|
||||
b.hoverOffset = b.priceTexture.Size().X
|
||||
} else if !b.IsMouseOver {
|
||||
b.hoverAnimation = nil
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *BuyFlowerButton) Render(ctx *Context) {
|
||||
iconTexture := b.activeTexture(ctx)
|
||||
mouseOverTexture := ctx.Textures.Texture("control-hover")
|
||||
|
||||
pos := Pt(b.Bounds.X, b.Bounds.Y)
|
||||
iconTexture.CopyResize(ctx.Renderer, RectSize(pos.X, pos.Y-60, b.Bounds.W, 120))
|
||||
if (b.IsMouseOver && !b.IsDisabled) || b.IsActive {
|
||||
mouseOverTexture.CopyResize(ctx.Renderer, b.Bounds)
|
||||
SetDrawColor(ctx.Renderer, TransparentWhite)
|
||||
ctx.Renderer.FillRect(b.Bounds.SDLPtr())
|
||||
}
|
||||
|
||||
if b.hoverAnimation != nil {
|
||||
|
BIN
cmd/tins2020/res/images/checkmark.png
Normal file
BIN
cmd/tins2020/res/images/checkmark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
cmd/tins2020/res/images/cross.png
Normal file
BIN
cmd/tins2020/res/images/cross.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
Before Width: | Height: | Size: 297 B |
BIN
cmd/tins2020/res/images/information.png
Normal file
BIN
cmd/tins2020/res/images/information.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
cmd/tins2020/res/images/return.png
Normal file
BIN
cmd/tins2020/res/images/return.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
@ -1,10 +1,11 @@
|
||||
control-hover: images/game_control_hover.png
|
||||
control-shovel: images/genericItem_color_022.png
|
||||
control-research: images/genericItem_color_111.png
|
||||
|
||||
control-settings: images/gear.png
|
||||
control-save: images/save.png
|
||||
control-load: images/import.png
|
||||
control-new: images/return.png
|
||||
control-information: images/information.png
|
||||
control-quit: images/power.png
|
||||
|
||||
control-pause: images/pause.png
|
||||
@ -14,6 +15,9 @@ control-run-disabled: images/forward_disabled.png
|
||||
control-run-fast: images/fastForward.png
|
||||
control-run-fast-disabled: images/fastForward_disabled.png
|
||||
|
||||
control-cancel: images/cross.png
|
||||
control-confirm: images/checkmark.png
|
||||
|
||||
tile-dirt: images/tile_dirt.png
|
||||
tile-grass: images/tile_grass.png
|
||||
tile-snow: images/tile_snow.png
|
||||
|
@ -28,7 +28,7 @@ func run() error {
|
||||
}
|
||||
defer sdl.Quit()
|
||||
|
||||
// logSDLVersion()
|
||||
logSDLVersion()
|
||||
|
||||
if err := ttf.Init(); err != nil {
|
||||
return err
|
||||
@ -56,7 +56,7 @@ func run() error {
|
||||
sdl.SetHint(sdl.HINT_RENDER_VSYNC, "1")
|
||||
}
|
||||
sdl.SetHint(sdl.HINT_RENDER_SCALE_QUALITY, "1")
|
||||
window, err := sdl.CreateWindow("TINS 2020",
|
||||
window, err := sdl.CreateWindow("Botanim - TINS 2020",
|
||||
ctx.Settings.Window.Location.X, ctx.Settings.Window.Location.Y,
|
||||
ctx.Settings.Window.Size.X, ctx.Settings.Window.Size.Y,
|
||||
sdl.WINDOW_SHOWN|sdl.WINDOW_RESIZABLE)
|
||||
@ -74,10 +74,11 @@ func run() error {
|
||||
ctx.Init(renderer)
|
||||
|
||||
err = ctx.Fonts.LoadDesc(
|
||||
tins2020.FontDescriptor{Name: "debug", Path: "fonts/FiraMono-Regular.ttf", Size: 12},
|
||||
tins2020.FontDescriptor{Name: "default", Path: "fonts/OpenSans-Regular.ttf", Size: 16},
|
||||
tins2020.FontDescriptor{Name: "small", Path: "fonts/OpenSans-Regular.ttf", Size: 12},
|
||||
tins2020.FontDescriptor{Name: "balance", Path: "fonts/OpenSans-Bold.ttf", Size: 40},
|
||||
tins2020.FontDescriptor{Name: "debug", Path: "fonts/FiraMono-Regular.ttf", Size: 12},
|
||||
tins2020.FontDescriptor{Name: "default", Path: "fonts/OpenSans-Regular.ttf", Size: 14},
|
||||
tins2020.FontDescriptor{Name: "small", Path: "fonts/OpenSans-Regular.ttf", Size: 12},
|
||||
tins2020.FontDescriptor{Name: "title", Path: "fonts/OpenSans-Bold.ttf", Size: 40},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -93,18 +94,25 @@ func run() error {
|
||||
game := tins2020.NewGame()
|
||||
|
||||
app := tins2020.NewContainer()
|
||||
|
||||
overlays := tins2020.NewContainer()
|
||||
gameControls := tins2020.NewGameControls(game)
|
||||
overlays.AddChild(gameControls)
|
||||
overlays.AddChild(&tins2020.FPS{})
|
||||
content := tins2020.NewContainer()
|
||||
dialogs := tins2020.NewDialogs()
|
||||
|
||||
overlays.AddChild(dialogs)
|
||||
overlays.AddChild(&tins2020.FPS{Show: &game.Debug})
|
||||
|
||||
content := tins2020.NewContent(dialogs)
|
||||
content.AddChild(tins2020.NewTerrainRenderer(game))
|
||||
content.AddChild(tins2020.NewGameControls(game, dialogs))
|
||||
|
||||
app.AddChild(content)
|
||||
app.AddChild(overlays)
|
||||
content.AddChild(tins2020.NewTerrainRenderer(game))
|
||||
|
||||
err = app.Init(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dialogs.ShowIntro()
|
||||
|
||||
w, h := window.GetSize()
|
||||
app.Arrange(ctx, tins2020.Rect(0, 0, w, h))
|
||||
|
13
color.go
13
color.go
@ -6,4 +6,17 @@ import (
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
)
|
||||
|
||||
var Black = sdl.Color{R: 0, G: 0, B: 0, A: 255}
|
||||
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 White = sdl.Color{R: 255, G: 255, B: 255, A: 255}
|
||||
|
||||
func MustHexColor(s string) sdl.Color { return sdl.Color(img.MustHexColor(s)) }
|
||||
|
||||
func SetDrawColor(renderer *sdl.Renderer, color sdl.Color) {
|
||||
renderer.SetDrawColor(color.R, color.G, color.B, color.A)
|
||||
}
|
||||
|
||||
func SetDrawColorHex(renderer *sdl.Renderer, s string) {
|
||||
SetDrawColor(renderer, MustHexColor(s))
|
||||
}
|
||||
|
13
container.go
13
container.go
@ -25,11 +25,16 @@ func (c *Container) Arrange(ctx *Context, bounds Rectangle) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Container) Handle(ctx *Context, event sdl.Event) {
|
||||
c.ControlBase.Handle(ctx, event)
|
||||
for _, child := range c.Children {
|
||||
child.Handle(ctx, event)
|
||||
func (c *Container) Handle(ctx *Context, event sdl.Event) bool {
|
||||
if c.ControlBase.Handle(ctx, event) {
|
||||
return true
|
||||
}
|
||||
for _, child := range c.Children {
|
||||
if child.Handle(ctx, event) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Container) Init(ctx *Context) error {
|
||||
|
28
content.go
Normal file
28
content.go
Normal file
@ -0,0 +1,28 @@
|
||||
package tins2020
|
||||
|
||||
import "github.com/veandco/go-sdl2/sdl"
|
||||
|
||||
// Content shortcuts events when a dialog is opened.
|
||||
type Content struct {
|
||||
Container
|
||||
|
||||
dialogOverlayed bool
|
||||
}
|
||||
|
||||
func NewContent(dialogs *Dialogs) *Content {
|
||||
content := &Content{}
|
||||
dialogs.DialogOpened().Register(func() {
|
||||
content.dialogOverlayed = true
|
||||
})
|
||||
dialogs.DialogClosed().Register(func() {
|
||||
content.dialogOverlayed = false
|
||||
})
|
||||
return content
|
||||
}
|
||||
|
||||
func (c *Content) Handle(ctx *Context, event sdl.Event) bool {
|
||||
if c.dialogOverlayed {
|
||||
return false
|
||||
}
|
||||
return c.Container.Handle(ctx, event)
|
||||
}
|
@ -5,7 +5,7 @@ import "github.com/veandco/go-sdl2/sdl"
|
||||
type Control interface {
|
||||
Init(*Context) error
|
||||
Arrange(*Context, Rectangle)
|
||||
Handle(*Context, sdl.Event)
|
||||
Handle(*Context, sdl.Event) bool
|
||||
Render(*Context)
|
||||
}
|
||||
|
||||
@ -31,7 +31,12 @@ func (b *ControlBase) Arrange(ctx *Context, bounds Rectangle) { b.Bounds = bound
|
||||
|
||||
func (b *ControlBase) Init(*Context) error { return nil }
|
||||
|
||||
func (b *ControlBase) Handle(ctx *Context, event sdl.Event) {
|
||||
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) {
|
||||
case *sdl.MouseMotionEvent:
|
||||
b.IsMouseOver = b.Bounds.IsPointInside(e.X, e.Y)
|
||||
|
53
dialogs.go
Normal file
53
dialogs.go
Normal file
@ -0,0 +1,53 @@
|
||||
package tins2020
|
||||
|
||||
type Dialogs struct {
|
||||
Proxy
|
||||
|
||||
intro Control
|
||||
settings Control
|
||||
|
||||
dialogClosed *Events
|
||||
dialogOpened *Events
|
||||
}
|
||||
|
||||
func NewDialogs() *Dialogs {
|
||||
return &Dialogs{
|
||||
dialogClosed: NewEvents(),
|
||||
dialogOpened: NewEvents(),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Dialogs) DialogClosed() EventHandler { return d.dialogClosed }
|
||||
func (d *Dialogs) DialogOpened() EventHandler { return d.dialogOpened }
|
||||
|
||||
func (d *Dialogs) Init(ctx *Context) error {
|
||||
d.intro = &Intro{}
|
||||
d.settings = &DialogBase{}
|
||||
|
||||
err := d.intro.Init(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.settings.Init(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Dialogs) Close() {
|
||||
d.Proxied = nil
|
||||
d.dialogClosed.Notify(nil)
|
||||
}
|
||||
|
||||
func (d *Dialogs) ShowIntro() {
|
||||
d.Proxied = d.intro
|
||||
d.intro.(Dialog).ShowDialog(d.Close)
|
||||
d.dialogOpened.Notify(nil)
|
||||
}
|
||||
|
||||
func (d *Dialogs) ShowSettings() {
|
||||
d.Proxied = d.settings
|
||||
d.settings.(Dialog).ShowDialog(d.Close)
|
||||
d.dialogOpened.Notify(nil)
|
||||
}
|
@ -10,6 +10,7 @@ import (
|
||||
type FPS struct {
|
||||
ControlBase
|
||||
|
||||
Show *bool
|
||||
start time.Time
|
||||
stamp time.Duration
|
||||
slot int
|
||||
@ -25,6 +26,10 @@ func (f *FPS) Init(*Context) error {
|
||||
}
|
||||
|
||||
func (f *FPS) Render(ctx *Context) {
|
||||
if f.Show == nil || !*f.Show {
|
||||
return
|
||||
}
|
||||
|
||||
elapsed := time.Since(f.start)
|
||||
stamp := elapsed / (20 * time.Millisecond)
|
||||
for f.stamp < stamp {
|
||||
|
42
game.go
42
game.go
@ -7,6 +7,8 @@ import (
|
||||
)
|
||||
|
||||
type Game struct {
|
||||
Debug bool
|
||||
|
||||
Balance int
|
||||
Speed GameSpeed
|
||||
SpeedBeforePause GameSpeed
|
||||
@ -29,29 +31,17 @@ const (
|
||||
)
|
||||
|
||||
const simulationInterval = 120 * time.Millisecond
|
||||
const fastSimulationInterval = 40 * time.Millisecond
|
||||
const fastSimulationInterval = 20 * time.Millisecond
|
||||
|
||||
func NewGame() *Game {
|
||||
terrain := &Map{
|
||||
Temp: NewNoiseMap(rand.Int63()),
|
||||
Humid: NewNoiseMap(rand.Int63()),
|
||||
Variant: NewRandomNoiseMap(rand.Int63()),
|
||||
PlaceX: NewRandomNoiseMap(rand.Int63()),
|
||||
PlaceY: NewRandomNoiseMap(rand.Int63()),
|
||||
Flowers: map[Point]Flower{},
|
||||
}
|
||||
herbarium := NewHerbarium()
|
||||
return &Game{
|
||||
Speed: GameSpeedNormal,
|
||||
Balance: 100,
|
||||
Terrain: terrain,
|
||||
Herbarium: herbarium,
|
||||
|
||||
game := &Game{
|
||||
centerChanged: NewEvents(),
|
||||
speedChanged: NewEvents(),
|
||||
toolChanged: NewEvents(),
|
||||
simulation: NewAnimation(time.Millisecond * 10),
|
||||
}
|
||||
game.Reset()
|
||||
return game
|
||||
}
|
||||
|
||||
func (g *Game) selectTool(t Tool) {
|
||||
@ -127,6 +117,11 @@ func (g *Game) Dig(tile Point) {
|
||||
g.Balance += desc.SellPrice
|
||||
}
|
||||
|
||||
func (g *Game) New() {
|
||||
g.Pause()
|
||||
g.Reset()
|
||||
}
|
||||
|
||||
func (g *Game) Load() {
|
||||
g.CancelTool()
|
||||
g.Pause()
|
||||
@ -181,6 +176,21 @@ func (g *Game) PlantFlower(id string, tile Point) {
|
||||
g.Terrain.AddFlower(tile, id, flower.Traits)
|
||||
}
|
||||
|
||||
func (g *Game) Reset() {
|
||||
g.Balance = 100
|
||||
g.Herbarium = NewHerbarium()
|
||||
g.Terrain = &Map{
|
||||
Temp: NewNoiseMap(rand.Int63()),
|
||||
Humid: NewNoiseMap(rand.Int63()),
|
||||
Variant: NewRandomNoiseMap(rand.Int63()),
|
||||
PlaceX: NewRandomNoiseMap(rand.Int63()),
|
||||
PlaceY: NewRandomNoiseMap(rand.Int63()),
|
||||
Flowers: map[Point]Flower{},
|
||||
}
|
||||
g.CancelTool()
|
||||
g.setSpeed(GameSpeedNormal)
|
||||
}
|
||||
|
||||
func (g *Game) Resume() { g.setSpeed(g.SpeedBeforePause) }
|
||||
|
||||
func (g *Game) Run() { g.setSpeed(GameSpeedNormal) }
|
||||
|
@ -8,6 +8,7 @@ type GameControls struct {
|
||||
Container
|
||||
|
||||
game *Game
|
||||
dialogs *Dialogs
|
||||
|
||||
menu ButtonBar
|
||||
top ButtonBar
|
||||
@ -22,8 +23,8 @@ type GameControls struct {
|
||||
research *IconButton
|
||||
}
|
||||
|
||||
func NewGameControls(game *Game) *GameControls {
|
||||
return &GameControls{game: game}
|
||||
func NewGameControls(game *Game, dialogs *Dialogs) *GameControls {
|
||||
return &GameControls{game: game, dialogs: dialogs}
|
||||
}
|
||||
|
||||
func (c *GameControls) createBuyFlowerButton(id string) *BuyFlowerButton {
|
||||
@ -64,6 +65,16 @@ func (c *GameControls) toolChanged(state interface{}) {
|
||||
c.shovel.IsActive = shovel
|
||||
}
|
||||
|
||||
func (c *GameControls) updateFlowerControls(ctx *Context) {
|
||||
for _, b := range c.flowers.Buttons {
|
||||
button := b.(*BuyFlowerButton)
|
||||
flower, ok := c.game.Herbarium.Find(button.FlowerID)
|
||||
if ok {
|
||||
button.Update(ctx, flower)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *GameControls) Arrange(ctx *Context, bounds Rectangle) {
|
||||
c.Bounds = bounds
|
||||
c.menu.Arrange(ctx, RectSize(bounds.X, bounds.Y, buttonBarWidth, bounds.H))
|
||||
@ -75,6 +86,8 @@ func (c *GameControls) Arrange(ctx *Context, bounds Rectangle) {
|
||||
func (c *GameControls) Init(ctx *Context) error {
|
||||
c.game.SpeedChanged().RegisterItf(c.speedChanged)
|
||||
c.game.ToolChanged().RegisterItf(c.toolChanged)
|
||||
c.dialogs.DialogOpened().Register(func() { c.game.Pause() })
|
||||
c.dialogs.DialogClosed().Register(func() { c.game.Resume() })
|
||||
|
||||
c.flowers.Background = MustHexColor("#356dad")
|
||||
c.flowers.ButtonLength = 64
|
||||
@ -104,18 +117,17 @@ func (c *GameControls) Init(ctx *Context) error {
|
||||
|
||||
c.menu.Background = MustHexColor("#356dad")
|
||||
c.menu.Buttons = []Control{
|
||||
NewIconButton("control-settings", func(*Context) {}),
|
||||
NewIconButton("control-settings", func(*Context) { c.dialogs.ShowSettings() }),
|
||||
NewIconButton("control-save", func(*Context) { c.game.Save() }),
|
||||
NewIconButton("control-load", func(ctx *Context) {
|
||||
c.game.Load()
|
||||
for _, b := range c.flowers.Buttons {
|
||||
button := b.(*BuyFlowerButton)
|
||||
flower, ok := c.game.Herbarium.Find(button.FlowerID)
|
||||
if ok {
|
||||
button.Update(ctx, flower)
|
||||
}
|
||||
}
|
||||
c.updateFlowerControls(ctx)
|
||||
}),
|
||||
NewIconButton("control-new", func(ctx *Context) {
|
||||
c.game.New()
|
||||
c.updateFlowerControls(ctx)
|
||||
}),
|
||||
NewIconButton("control-information", func(*Context) { c.dialogs.ShowIntro() }),
|
||||
}
|
||||
|
||||
c.shovel = NewIconButtonConfig("control-shovel", func(*Context) { c.game.SelectShovel() }, func(b *IconButton) { b.IconHeight = 32 })
|
||||
@ -126,11 +138,14 @@ func (c *GameControls) Init(ctx *Context) error {
|
||||
c.Container.AddChild(&c.top)
|
||||
c.Container.AddChild(&c.flowers)
|
||||
c.Container.AddChild(&c.otherTools)
|
||||
|
||||
return c.Container.Init(ctx)
|
||||
}
|
||||
|
||||
func (c *GameControls) Handle(ctx *Context, event sdl.Event) {
|
||||
c.Container.Handle(ctx, event)
|
||||
func (c *GameControls) Handle(ctx *Context, event sdl.Event) bool {
|
||||
if c.Container.Handle(ctx, event) {
|
||||
return true
|
||||
}
|
||||
|
||||
switch e := event.(type) {
|
||||
case *sdl.KeyboardEvent:
|
||||
@ -148,18 +163,22 @@ func (c *GameControls) Handle(ctx *Context, event sdl.Event) {
|
||||
c.game.SelectResearch()
|
||||
case sdl.K_ESCAPE:
|
||||
if c.game.Tool() == nil {
|
||||
// TODO: display menu
|
||||
c.dialogs.ShowIntro()
|
||||
} else {
|
||||
c.game.CancelTool()
|
||||
}
|
||||
return true
|
||||
case sdl.K_F3:
|
||||
c.game.Debug = !c.game.Debug
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *GameControls) Render(ctx *Context) {
|
||||
topBar := MustHexColor("#0000007f")
|
||||
ctx.Renderer.SetDrawColor(topBar.R, topBar.G, topBar.B, topBar.A)
|
||||
SetDrawColor(ctx.Renderer, topBar)
|
||||
ctx.Renderer.FillRect(Rect(c.menu.Bounds.Right(), 0, c.flowers.Bounds.X, 64).SDLPtr())
|
||||
ctx.Fonts.Font("balance").RenderCopyAlign(ctx.Renderer, FmtMoney(c.game.Balance), Pt(c.top.Bounds.X-8, 58), MustHexColor("#4AC69A"), TextAlignmentRight)
|
||||
|
||||
|
@ -1,5 +1,12 @@
|
||||
package tins2020
|
||||
|
||||
type HoverEffect int
|
||||
|
||||
const (
|
||||
HoverEffectLigthen HoverEffect = iota
|
||||
HoverEffectColor
|
||||
)
|
||||
|
||||
type IconButton struct {
|
||||
ControlBase
|
||||
|
||||
@ -9,17 +16,13 @@ type IconButton struct {
|
||||
IconScale Scale
|
||||
IconWidth int32
|
||||
|
||||
IconActive HoverEffect
|
||||
IconHover HoverEffect
|
||||
|
||||
IsActive bool
|
||||
IsDisabled bool
|
||||
}
|
||||
|
||||
type Scale int
|
||||
|
||||
const (
|
||||
ScaleCenter Scale = iota
|
||||
ScaleStretch
|
||||
)
|
||||
|
||||
func NewIconButton(icon string, onClick EventContextFn) *IconButton {
|
||||
return &IconButton{
|
||||
ControlBase: ControlBase{
|
||||
@ -47,7 +50,11 @@ func (b *IconButton) activeTexture(ctx *Context) *Texture {
|
||||
|
||||
func (b *IconButton) Render(ctx *Context) {
|
||||
iconTexture := b.activeTexture(ctx)
|
||||
mouseOverTexture := ctx.Textures.Texture("control-hover")
|
||||
|
||||
hover := b.IsMouseOver && !b.IsDisabled
|
||||
if (hover && b.IconHover == HoverEffectColor) || (b.IsActive && b.IconActive == HoverEffectColor) {
|
||||
iconTexture.SetColor(MustHexColor("#15569F"))
|
||||
}
|
||||
|
||||
if b.IconScale == ScaleCenter {
|
||||
size := iconTexture.Size()
|
||||
@ -60,7 +67,16 @@ func (b *IconButton) Render(ctx *Context) {
|
||||
} else {
|
||||
iconTexture.CopyResize(ctx.Renderer, b.Bounds)
|
||||
}
|
||||
if (b.IsMouseOver && !b.IsDisabled) || b.IsActive {
|
||||
mouseOverTexture.CopyResize(ctx.Renderer, b.Bounds)
|
||||
if (hover && b.IconHover == HoverEffectLigthen) || (b.IsActive && b.IconActive == HoverEffectLigthen) {
|
||||
SetDrawColor(ctx.Renderer, TransparentWhite)
|
||||
ctx.Renderer.FillRect(b.Bounds.SDLPtr())
|
||||
}
|
||||
iconTexture.SetColor(White)
|
||||
}
|
||||
|
||||
type Scale int
|
||||
|
||||
const (
|
||||
ScaleCenter Scale = iota
|
||||
ScaleStretch
|
||||
)
|
||||
|
27
intro.go
Normal file
27
intro.go
Normal file
@ -0,0 +1,27 @@
|
||||
package tins2020
|
||||
|
||||
type Intro struct {
|
||||
LargeDialog
|
||||
|
||||
welcome Paragraph
|
||||
}
|
||||
|
||||
func (i *Intro) Init(ctx *Context) error {
|
||||
i.welcome.Text =
|
||||
"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" +
|
||||
"Flowers can only grow (well) in certain climates based on two properties: humidity and temperature. Watch out for existing vegetation to get an idea how humid the land is and check the appearance of the tile to see how hot it is. When well placed your planted flower will spread soon but an odd choice might kill your flower almost instantly. So choose carefully. When the flower spread significantly you can dig up flowers again to collect more money.\n\n" +
|
||||
"Controls:\n" +
|
||||
" - D: Selects shovel\n" +
|
||||
" - R: Selects research\n" +
|
||||
" - Spacebar: pauses game\n" +
|
||||
" - 1: runs game at normal speed\n" +
|
||||
" - 2: runs game extra fast\n" +
|
||||
" - Mouse wheel or plus/minus: zooms landscape\n" +
|
||||
" - CTRL + left mouse button or middle mouse button: pans landscape\n" +
|
||||
"\n" +
|
||||
"Have fun playing!"
|
||||
i.SetContent(&i.welcome)
|
||||
|
||||
return i.LargeDialog.Init(ctx)
|
||||
}
|
2
io.go
2
io.go
@ -33,7 +33,7 @@ func UserDir() (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dir := filepath.Join(config, "tins2020_flowers_sim")
|
||||
dir := filepath.Join(config, "tins2020_botanim")
|
||||
err = os.MkdirAll(dir, 0777)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
108
label.go
Normal file
108
label.go
Normal file
@ -0,0 +1,108 @@
|
||||
package tins2020
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
)
|
||||
|
||||
type Label struct {
|
||||
ControlBase
|
||||
|
||||
FontColor sdl.Color
|
||||
FontName string
|
||||
Text string
|
||||
Alignment TextAlignment
|
||||
}
|
||||
|
||||
func (l *Label) fontColor() sdl.Color {
|
||||
var none sdl.Color
|
||||
if l.FontColor == none {
|
||||
return MustHexColor("#ffffff")
|
||||
}
|
||||
return l.FontColor
|
||||
}
|
||||
|
||||
func (l *Label) fontName() string {
|
||||
if len(l.FontName) == 0 {
|
||||
return "default"
|
||||
}
|
||||
return l.FontName
|
||||
}
|
||||
|
||||
func (l *Label) Render(ctx *Context) {
|
||||
font := ctx.Fonts.Font(l.fontName())
|
||||
color := l.fontColor()
|
||||
bottom := l.Bounds.Y + l.Bounds.H
|
||||
switch l.Alignment {
|
||||
case TextAlignmentCenter:
|
||||
font.RenderCopyAlign(ctx.Renderer, l.Text, Pt(l.Bounds.X+l.Bounds.W/2, bottom), color, TextAlignmentCenter)
|
||||
case TextAlignmentLeft:
|
||||
font.RenderCopyAlign(ctx.Renderer, l.Text, Pt(l.Bounds.X, bottom), color, TextAlignmentLeft)
|
||||
case TextAlignmentRight:
|
||||
font.RenderCopyAlign(ctx.Renderer, l.Text, Pt(l.Bounds.X+l.Bounds.W, bottom), color, TextAlignmentRight)
|
||||
}
|
||||
}
|
||||
|
||||
type Paragraph struct {
|
||||
Label
|
||||
}
|
||||
|
||||
// func (p *Paragraph) Arrange(ctx *Context, bounds Rectangle) {
|
||||
// p.Label.Arrange(ctx, bounds)
|
||||
// }
|
||||
|
||||
func (p *Paragraph) Render(ctx *Context) {
|
||||
font := ctx.Fonts.Font(p.fontName())
|
||||
color := p.fontColor()
|
||||
fontHeight := int32(font.Height())
|
||||
lines := strings.Split(p.Text, "\n")
|
||||
|
||||
measure := func(s string) int32 {
|
||||
w, _, _ := font.SizeUTF8(s)
|
||||
return int32(w)
|
||||
}
|
||||
|
||||
spaces := func(s string) []int {
|
||||
var spaces []int
|
||||
offset := 0
|
||||
for {
|
||||
space := strings.Index(s[offset:], " ")
|
||||
if space == -1 {
|
||||
return spaces
|
||||
}
|
||||
offset += space
|
||||
spaces = append(spaces, offset)
|
||||
offset++
|
||||
}
|
||||
}
|
||||
|
||||
fit := func(s string) string {
|
||||
if measure(s) < p.Bounds.W {
|
||||
return s
|
||||
}
|
||||
spaces := spaces(s)
|
||||
for split := len(spaces) - 1; split >= 0; split-- {
|
||||
clipped := s[:spaces[split]]
|
||||
if measure(clipped) < p.Bounds.W {
|
||||
return clipped
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
offset := p.Bounds.Y
|
||||
for _, line := range lines {
|
||||
if len(line) == 0 {
|
||||
offset += fontHeight
|
||||
continue
|
||||
}
|
||||
|
||||
for len(line) > 0 {
|
||||
offset += fontHeight
|
||||
clipped := fit(line)
|
||||
line = strings.TrimLeft(line[len(clipped):], " ")
|
||||
font.RenderCopy(ctx.Renderer, clipped, Pt(p.Bounds.X, offset), color)
|
||||
}
|
||||
}
|
||||
}
|
95
largedialog.go
Normal file
95
largedialog.go
Normal file
@ -0,0 +1,95 @@
|
||||
package tins2020
|
||||
|
||||
import "github.com/veandco/go-sdl2/sdl"
|
||||
|
||||
type DialogBase struct {
|
||||
Container
|
||||
|
||||
content Proxy
|
||||
close EventFn
|
||||
}
|
||||
|
||||
type Dialog interface {
|
||||
CloseDialog()
|
||||
ShowDialog(EventFn)
|
||||
}
|
||||
|
||||
func (d *DialogBase) CloseDialog() {
|
||||
close := d.close
|
||||
if close != nil {
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DialogBase) SetContent(control Control) {
|
||||
d.content.Proxied = control
|
||||
}
|
||||
|
||||
func (d *DialogBase) ShowDialog(close EventFn) {
|
||||
d.close = close
|
||||
}
|
||||
|
||||
func (d *DialogBase) Init(ctx *Context) error {
|
||||
d.AddChild(&d.content)
|
||||
return d.Container.Init(ctx)
|
||||
}
|
||||
|
||||
type LargeDialog struct {
|
||||
DialogBase
|
||||
|
||||
title Label
|
||||
close IconButton
|
||||
}
|
||||
|
||||
func (d *LargeDialog) Arrange(ctx *Context, bounds Rectangle) {
|
||||
const titleHeight = 64
|
||||
d.ControlBase.Arrange(ctx, bounds)
|
||||
d.title.Arrange(ctx, RectSize(bounds.X, bounds.Y, bounds.W, titleHeight))
|
||||
d.close.Arrange(ctx, RectSize(bounds.W-64, 0, 64, 64))
|
||||
d.content.Arrange(ctx, RectSize(bounds.X+titleHeight, 96, bounds.W-2*titleHeight, bounds.H-titleHeight))
|
||||
}
|
||||
|
||||
func (d *LargeDialog) Init(ctx *Context) error {
|
||||
d.title = Label{
|
||||
Text: "Botanim",
|
||||
FontName: "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 *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 *Context) {
|
||||
SetDrawColor(ctx.Renderer, MustHexColor("#356DAD"))
|
||||
ctx.Renderer.FillRect(d.Bounds.SDLPtr())
|
||||
|
||||
d.DialogBase.Render(ctx)
|
||||
}
|
@ -100,3 +100,26 @@ func (p *projection) visibleTiles(action func(int32, int32, Point)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *projection) ZoomOut(ctx *Context, center PointF) {
|
||||
if p.zoom <= .25 {
|
||||
return
|
||||
}
|
||||
p.SetZoom(ctx, center, .5*p.zoom)
|
||||
}
|
||||
|
||||
func (p *projection) ZoomIn(ctx *Context, center PointF) {
|
||||
if p.zoom >= 2 {
|
||||
return
|
||||
}
|
||||
p.SetZoom(ctx, center, 2*p.zoom)
|
||||
}
|
||||
|
||||
func (p *projection) SetZoom(ctx *Context, center PointF, zoom float32) {
|
||||
if p.zoom == zoom {
|
||||
return
|
||||
}
|
||||
p.center = center.Sub(center.Sub(p.center).Mul(p.zoom / zoom))
|
||||
p.zoom = zoom
|
||||
p.update(ctx.Renderer)
|
||||
}
|
||||
|
37
proxy.go
Normal file
37
proxy.go
Normal file
@ -0,0 +1,37 @@
|
||||
package tins2020
|
||||
|
||||
import "github.com/veandco/go-sdl2/sdl"
|
||||
|
||||
var _ Control = &Proxy{}
|
||||
|
||||
type Proxy struct {
|
||||
Proxied Control
|
||||
}
|
||||
|
||||
func (p *Proxy) Arrange(ctx *Context, bounds Rectangle) {
|
||||
if p.Proxied == nil {
|
||||
return
|
||||
}
|
||||
p.Proxied.Arrange(ctx, bounds)
|
||||
}
|
||||
|
||||
func (p *Proxy) Handle(ctx *Context, event sdl.Event) bool {
|
||||
if p.Proxied == nil {
|
||||
return false
|
||||
}
|
||||
return p.Proxied.Handle(ctx, event)
|
||||
}
|
||||
|
||||
func (p *Proxy) Init(ctx *Context) error {
|
||||
if p.Proxied == nil {
|
||||
return nil
|
||||
}
|
||||
return p.Proxied.Init(ctx)
|
||||
}
|
||||
|
||||
func (p *Proxy) Render(ctx *Context) {
|
||||
if p.Proxied == nil {
|
||||
return
|
||||
}
|
||||
p.Proxied.Render(ctx)
|
||||
}
|
@ -37,7 +37,7 @@ func isControlKeyDown() bool {
|
||||
return state[sdl.SCANCODE_LCTRL] == 1 || state[sdl.SCANCODE_RCTRL] == 1
|
||||
}
|
||||
|
||||
func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) {
|
||||
func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) bool {
|
||||
switch e := event.(type) {
|
||||
case *sdl.MouseButtonEvent:
|
||||
if r.project.windowInteractRect.IsPointInside(e.X, e.Y) {
|
||||
@ -78,17 +78,10 @@ func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) {
|
||||
}
|
||||
case *sdl.MouseWheelEvent:
|
||||
if r.hover != nil {
|
||||
zoom := r.project.zoom
|
||||
if e.Y < 0 && r.project.zoom > .25 {
|
||||
zoom *= .5
|
||||
} else if e.Y > 0 && r.project.zoom < 2 {
|
||||
zoom *= 2
|
||||
}
|
||||
if zoom != r.project.zoom {
|
||||
hover := r.hover.ToPtF()
|
||||
r.project.center = hover.Sub(hover.Sub(r.project.center).Mul(r.project.zoom / zoom))
|
||||
r.project.zoom = zoom
|
||||
r.project.update(ctx.Renderer)
|
||||
if e.Y < 0 {
|
||||
r.project.ZoomOut(ctx, r.hover.ToPtF())
|
||||
} else {
|
||||
r.project.ZoomIn(ctx, r.hover.ToPtF())
|
||||
}
|
||||
}
|
||||
case *sdl.WindowEvent:
|
||||
@ -96,8 +89,22 @@ func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) {
|
||||
r.hover = nil
|
||||
r.project.update(ctx.Renderer)
|
||||
}
|
||||
case *sdl.KeyboardEvent:
|
||||
if e.Type == sdl.KEYDOWN {
|
||||
switch e.Keysym.Sym {
|
||||
case sdl.K_PLUS:
|
||||
r.project.ZoomIn(ctx, r.project.center)
|
||||
case sdl.K_KP_PLUS:
|
||||
r.project.ZoomIn(ctx, r.project.center)
|
||||
case sdl.K_MINUS:
|
||||
r.project.ZoomOut(ctx, r.project.center)
|
||||
case sdl.K_KP_MINUS:
|
||||
r.project.ZoomOut(ctx, r.project.center)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *terrainRenderer) Render(ctx *Context) {
|
||||
terrain := r.game.Terrain
|
||||
|
@ -44,6 +44,10 @@ func (t *Texture) CopyResize(renderer *sdl.Renderer, dst Rectangle) {
|
||||
t.CopyPartResize(renderer, Rect(0, 0, t.size.X, t.size.Y), dst)
|
||||
}
|
||||
|
||||
func (t *Texture) SetColor(color sdl.Color) {
|
||||
t.texture.SetColorMod(color.R, color.G, color.B)
|
||||
}
|
||||
|
||||
// func (t *Texture) CopyF(renderer *sdl.Renderer, dst *sdl.FRect) {
|
||||
// renderer.CopyF(t.texture, t.rect, dst) // Depends on SDL >=2.0.10
|
||||
// }
|
||||
|
Loading…
Reference in New Issue
Block a user