Compare commits
4 Commits
c27d43e323
...
c926ae2ef9
Author | SHA1 | Date | |
---|---|---|---|
c926ae2ef9 | |||
013fe4bdb6 | |||
f57a9dd845 | |||
76ac685cbb |
12
README.md
12
README.md
@ -23,13 +23,21 @@ In Botanim you play the role of botanist and your goal is to cultivate flowers i
|
|||||||
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.
|
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.
|
||||||
|
|
||||||
**Controls:**
|
**Controls:**
|
||||||
- D: Selects shovel
|
- H: Selects shovel
|
||||||
- R: Selects research
|
- R: Selects research
|
||||||
- Spacebar: pauses game
|
- Spacebar: pauses game
|
||||||
- 1: runs game at normal speed
|
- 1: runs game at normal speed
|
||||||
- 2: runs game extra fast
|
- 2: runs game extra fast
|
||||||
- Mouse wheel or plus/minus: zooms landscape
|
- Mouse wheel or plus/minus: zooms landscape
|
||||||
- CTRL + left mouse button or middle mouse button: pans landscape
|
- W, A, S, D keys or CTRL + left mouse button or middle mouse button: pans landscape
|
||||||
|
|
||||||
|
** On screen **
|
||||||
|
On the left side of the playing screen you'll find several buttons:
|
||||||
|
- Settings: disabled.
|
||||||
|
- Save: saves the game instantly, only single slot.
|
||||||
|
- Load: loads previously saved game instantly.
|
||||||
|
- New: starts a new game with a different terrain.
|
||||||
|
- Information: shows the intro/information screen (again, also accessible with the Escape key).
|
||||||
|
|
||||||
Have fun playing!
|
Have fun playing!
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ type BuyFlowerButton struct {
|
|||||||
|
|
||||||
func NewBuyFlowerButton(icon, iconDisabled, flowerID string, flower FlowerDescriptor, onClick EventContextFn) *BuyFlowerButton {
|
func NewBuyFlowerButton(icon, iconDisabled, flowerID string, flower FlowerDescriptor, onClick EventContextFn) *BuyFlowerButton {
|
||||||
return &BuyFlowerButton{
|
return &BuyFlowerButton{
|
||||||
IconButton: *NewIconButtonConfig(icon, onClick, func(b *IconButton) {
|
IconButton: *NewIconButtonConfigure(icon, onClick, func(b *IconButton) {
|
||||||
b.IconDisabled = iconDisabled
|
b.IconDisabled = iconDisabled
|
||||||
b.IsDisabled = !flower.Unlocked
|
b.IsDisabled = !flower.Unlocked
|
||||||
}),
|
}),
|
||||||
|
@ -136,6 +136,8 @@ func run() error {
|
|||||||
app.Arrange(ctx, tins2020.RectAbs(0, 0, w, h))
|
app.Arrange(ctx, tins2020.RectAbs(0, 0, w, h))
|
||||||
ctx.Settings.Window.Size = tins2020.PtPtr(w, h)
|
ctx.Settings.Window.Size = tins2020.PtPtr(w, h)
|
||||||
}
|
}
|
||||||
|
case *sdl.MouseMotionEvent:
|
||||||
|
ctx.MousePosition = tins2020.Pt(e.X, e.Y)
|
||||||
}
|
}
|
||||||
app.Handle(ctx, event)
|
app.Handle(ctx, event)
|
||||||
}
|
}
|
||||||
|
13
context.go
13
context.go
@ -6,12 +6,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
Renderer *sdl.Renderer
|
Renderer *sdl.Renderer
|
||||||
Fonts Fonts
|
Fonts Fonts
|
||||||
Resources Resources
|
Resources Resources
|
||||||
Textures Textures
|
Textures Textures
|
||||||
Settings Settings
|
Settings Settings
|
||||||
ShouldQuit bool
|
MousePosition Point
|
||||||
|
ShouldQuit bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContext(res *rice.Box) (*Context, error) {
|
func NewContext(res *rice.Box) (*Context, error) {
|
||||||
|
23
control.go
23
control.go
@ -22,12 +22,35 @@ func EmptyEvent(fn EventFn) EventContextFn {
|
|||||||
type ControlBase struct {
|
type ControlBase struct {
|
||||||
Bounds Rectangle
|
Bounds Rectangle
|
||||||
|
|
||||||
|
FontName string
|
||||||
|
Foreground sdl.Color
|
||||||
|
|
||||||
IsDisabled bool
|
IsDisabled bool
|
||||||
IsMouseOver bool
|
IsMouseOver bool
|
||||||
|
|
||||||
OnLeftMouseButtonClick EventContextFn
|
OnLeftMouseButtonClick EventContextFn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ControlBase) ActualForeground() sdl.Color {
|
||||||
|
var none sdl.Color
|
||||||
|
if c.Foreground == none {
|
||||||
|
return MustHexColor("#ffffff")
|
||||||
|
}
|
||||||
|
return c.Foreground
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ControlBase) ActualFont(ctx *Context) *Font {
|
||||||
|
name := c.ActualFontName()
|
||||||
|
return ctx.Fonts.Font(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ControlBase) ActualFontName() string {
|
||||||
|
if len(c.FontName) == 0 {
|
||||||
|
return "default"
|
||||||
|
}
|
||||||
|
return c.FontName
|
||||||
|
}
|
||||||
|
|
||||||
func (b *ControlBase) Arrange(ctx *Context, bounds Rectangle) { b.Bounds = bounds }
|
func (b *ControlBase) Arrange(ctx *Context, bounds Rectangle) { b.Bounds = bounds }
|
||||||
|
|
||||||
func (b *ControlBase) Init(*Context) error { return nil }
|
func (b *ControlBase) Init(*Context) error { return nil }
|
||||||
|
10
game.go
10
game.go
@ -114,7 +114,15 @@ func (g *Game) Dig(tile Point) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
g.Balance += desc.SellPrice
|
adjacent := g.Terrain.FlowersOnAdjacentTiles(tile)
|
||||||
|
switch adjacent {
|
||||||
|
case 3:
|
||||||
|
g.Balance += (desc.SellPrice * 3 / 2) // 50% bonus
|
||||||
|
case 4:
|
||||||
|
g.Balance += (desc.SellPrice * 2) // 100% bonus
|
||||||
|
default:
|
||||||
|
g.Balance += desc.SellPrice
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) New() {
|
func (g *Game) New() {
|
||||||
|
@ -100,44 +100,61 @@ func (c *GameControls) Init(ctx *Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.top.Orientation = OrientationHorizontal
|
c.top.Orientation = OrientationHorizontal
|
||||||
c.pause = NewIconButtonConfig("control-pause", EmptyEvent(func() {
|
c.pause = NewIconButtonConfigure("control-pause", EmptyEvent(func() {
|
||||||
c.game.Pause()
|
c.game.Pause()
|
||||||
}), func(b *IconButton) {
|
}), func(b *IconButton) {
|
||||||
b.IconDisabled = "control-pause-disabled"
|
b.IconDisabled = "control-pause-disabled"
|
||||||
|
b.Tooltip.Text = "Pause game"
|
||||||
})
|
})
|
||||||
c.run = NewIconButtonConfig("control-run", EmptyEvent(func() {
|
c.run = NewIconButtonConfigure("control-run", EmptyEvent(func() {
|
||||||
c.game.Run()
|
c.game.Run()
|
||||||
}), func(b *IconButton) {
|
}), func(b *IconButton) {
|
||||||
b.IconDisabled = "control-run-disabled"
|
b.IconDisabled = "control-run-disabled"
|
||||||
|
b.Tooltip.Text = "Run game at normal speed"
|
||||||
})
|
})
|
||||||
c.runFast = NewIconButtonConfig("control-run-fast", EmptyEvent(func() {
|
c.runFast = NewIconButtonConfigure("control-run-fast", EmptyEvent(func() {
|
||||||
c.game.RunFast()
|
c.game.RunFast()
|
||||||
}), func(b *IconButton) {
|
}), func(b *IconButton) {
|
||||||
b.IconDisabled = "control-run-fast-disabled"
|
b.IconDisabled = "control-run-fast-disabled"
|
||||||
|
b.Tooltip.Text = "Run game at fast speed"
|
||||||
})
|
})
|
||||||
c.speedChanged(c.game.Speed)
|
c.speedChanged(c.game.Speed)
|
||||||
c.top.Buttons = []Control{c.pause, c.run, c.runFast}
|
c.top.Buttons = []Control{c.pause, c.run, c.runFast}
|
||||||
|
|
||||||
c.menu.Background = MustHexColor("#356dad")
|
c.menu.Background = MustHexColor("#356dad")
|
||||||
c.menu.Buttons = []Control{
|
c.menu.Buttons = []Control{
|
||||||
NewIconButtonConfig("control-settings", c.dialogs.ShowSettings, func(b *IconButton) {
|
NewIconButtonConfigure("control-settings", c.dialogs.ShowSettings, func(b *IconButton) {
|
||||||
b.IsDisabled = true
|
b.IsDisabled = true
|
||||||
b.IconDisabled = "#afafaf"
|
b.IconDisabled = "#afafaf"
|
||||||
}),
|
}),
|
||||||
NewIconButton("control-save", func(*Context) { c.game.Save() }),
|
NewIconButtonConfigure("control-save", func(*Context) { c.game.Save() }, func(b *IconButton) {
|
||||||
NewIconButton("control-load", func(ctx *Context) {
|
b.Tooltip.Text = "Save game (overwrites previous save; no confirmation)"
|
||||||
|
}),
|
||||||
|
NewIconButtonConfigure("control-load", func(ctx *Context) {
|
||||||
c.game.Load()
|
c.game.Load()
|
||||||
c.updateFlowerControls(ctx)
|
c.updateFlowerControls(ctx)
|
||||||
|
}, func(b *IconButton) {
|
||||||
|
b.Tooltip.Text = "Load last saved game (no confirmation)"
|
||||||
}),
|
}),
|
||||||
NewIconButton("control-new", func(ctx *Context) {
|
NewIconButtonConfigure("control-new", func(ctx *Context) {
|
||||||
c.game.New()
|
c.game.New()
|
||||||
c.updateFlowerControls(ctx)
|
c.updateFlowerControls(ctx)
|
||||||
|
}, func(b *IconButton) {
|
||||||
|
b.Tooltip.Text = "Start new game (no confirmation)"
|
||||||
|
}),
|
||||||
|
NewIconButtonConfigure("control-information", c.dialogs.ShowIntro, func(b *IconButton) {
|
||||||
|
b.Tooltip.Text = "Show information/intro"
|
||||||
}),
|
}),
|
||||||
NewIconButton("control-information", c.dialogs.ShowIntro),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.shovel = NewIconButtonConfig("control-shovel", func(*Context) { c.game.SelectShovel() }, func(b *IconButton) { b.IconHeight = 32 })
|
c.shovel = NewIconButtonConfigure("control-shovel", func(*Context) { c.game.SelectShovel() }, func(b *IconButton) {
|
||||||
c.research = NewIconButtonConfig("control-research", c.dialogs.ShowResearch, func(b *IconButton) { b.IconHeight = 32 })
|
b.IconHeight = 32
|
||||||
|
b.Tooltip.Text = "Select harvest tool (key: H)"
|
||||||
|
})
|
||||||
|
c.research = NewIconButtonConfigure("control-research", c.dialogs.ShowResearch, func(b *IconButton) {
|
||||||
|
b.IconHeight = 32
|
||||||
|
b.Tooltip.Text = "Conduct research (key: R)"
|
||||||
|
})
|
||||||
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)
|
||||||
@ -163,7 +180,7 @@ func (c *GameControls) Handle(ctx *Context, event sdl.Event) bool {
|
|||||||
c.game.Run()
|
c.game.Run()
|
||||||
case sdl.K_2:
|
case sdl.K_2:
|
||||||
c.game.RunFast()
|
c.game.RunFast()
|
||||||
case sdl.K_d:
|
case sdl.K_h:
|
||||||
c.game.SelectShovel()
|
c.game.SelectShovel()
|
||||||
case sdl.K_r:
|
case sdl.K_r:
|
||||||
c.dialogs.ShowResearch(ctx)
|
c.dialogs.ShowResearch(ctx)
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
package tins2020
|
package tins2020
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
|
)
|
||||||
|
|
||||||
type HoverEffect int
|
type HoverEffect int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -19,6 +23,7 @@ type IconButton struct {
|
|||||||
IconActive HoverEffect
|
IconActive HoverEffect
|
||||||
IconHover HoverEffect
|
IconHover HoverEffect
|
||||||
|
|
||||||
|
Tooltip Tooltip
|
||||||
IsActive bool
|
IsActive bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +36,7 @@ func NewIconButton(icon string, onClick EventContextFn) *IconButton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIconButtonConfig(icon string, onClick EventContextFn, configure func(*IconButton)) *IconButton {
|
func NewIconButtonConfigure(icon string, onClick EventContextFn, configure func(*IconButton)) *IconButton {
|
||||||
button := NewIconButton(icon, onClick)
|
button := NewIconButton(icon, onClick)
|
||||||
configure(button)
|
configure(button)
|
||||||
return button
|
return button
|
||||||
@ -57,6 +62,31 @@ func (b *IconButton) activeTexture(ctx *Context) *Texture {
|
|||||||
return ctx.Textures.Texture(b.Icon)
|
return ctx.Textures.Texture(b.Icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *IconButton) Arrange(ctx *Context, bounds Rectangle) {
|
||||||
|
b.ControlBase.Arrange(ctx, bounds)
|
||||||
|
b.Tooltip.Arrange(ctx, bounds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *IconButton) Handle(ctx *Context, event sdl.Event) bool {
|
||||||
|
if b.ControlBase.Handle(ctx, event) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if b.Tooltip.Handle(ctx, event) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *IconButton) Init(ctx *Context) error {
|
||||||
|
if err := b.ControlBase.Init(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := b.Tooltip.Init(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (b *IconButton) Render(ctx *Context) {
|
func (b *IconButton) Render(ctx *Context) {
|
||||||
iconTexture := b.activeTexture(ctx)
|
iconTexture := b.activeTexture(ctx)
|
||||||
|
|
||||||
@ -81,6 +111,10 @@ func (b *IconButton) Render(ctx *Context) {
|
|||||||
ctx.Renderer.FillRect(b.Bounds.SDLPtr())
|
ctx.Renderer.FillRect(b.Bounds.SDLPtr())
|
||||||
}
|
}
|
||||||
iconTexture.SetColor(White)
|
iconTexture.SetColor(White)
|
||||||
|
|
||||||
|
if len(b.Tooltip.Text) > 0 && b.IsMouseOver {
|
||||||
|
b.Tooltip.Render(ctx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Scale int
|
type Scale int
|
||||||
|
4
intro.go
4
intro.go
@ -12,13 +12,13 @@ func (i *Intro) Init(ctx *Context) error {
|
|||||||
"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" +
|
||||||
"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" +
|
"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" +
|
"Controls:\n" +
|
||||||
" - D: Selects shovel\n" +
|
" - H: Selects shovel\n" +
|
||||||
" - R: Selects research\n" +
|
" - R: Selects research\n" +
|
||||||
" - Spacebar: pauses game\n" +
|
" - Spacebar: pauses game\n" +
|
||||||
" - 1: runs game at normal speed\n" +
|
" - 1: runs game at normal speed\n" +
|
||||||
" - 2: runs game extra fast\n" +
|
" - 2: runs game extra fast\n" +
|
||||||
" - Mouse wheel or plus/minus: zooms landscape\n" +
|
" - Mouse wheel or plus/minus: zooms landscape\n" +
|
||||||
" - CTRL + left mouse button or middle mouse button: pans landscape\n" +
|
" - W, A, S, D keys or CTRL + left mouse button or middle mouse button: pans landscape\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"Have fun playing!"
|
"Have fun playing!"
|
||||||
i.SetContent(&i.welcome)
|
i.SetContent(&i.welcome)
|
||||||
|
27
label.go
27
label.go
@ -2,37 +2,18 @@ package tins2020
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/veandco/go-sdl2/sdl"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Label struct {
|
type Label struct {
|
||||||
ControlBase
|
ControlBase
|
||||||
|
|
||||||
FontColor sdl.Color
|
|
||||||
FontName string
|
|
||||||
Text string
|
Text string
|
||||||
Alignment TextAlignment
|
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) {
|
func (l *Label) Render(ctx *Context) {
|
||||||
font := ctx.Fonts.Font(l.fontName())
|
font := ctx.Fonts.Font(l.ActualFontName())
|
||||||
color := l.fontColor()
|
color := l.ActualForeground()
|
||||||
bottom := l.Bounds.Y + l.Bounds.H
|
bottom := l.Bounds.Y + l.Bounds.H
|
||||||
switch l.Alignment {
|
switch l.Alignment {
|
||||||
case TextAlignmentCenter:
|
case TextAlignmentCenter:
|
||||||
@ -49,8 +30,8 @@ type Paragraph struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Paragraph) Render(ctx *Context) {
|
func (p *Paragraph) Render(ctx *Context) {
|
||||||
font := ctx.Fonts.Font(p.fontName())
|
font := ctx.Fonts.Font(p.ActualFontName())
|
||||||
color := p.fontColor()
|
color := p.ActualForeground()
|
||||||
fontHeight := int32(font.Height())
|
fontHeight := int32(font.Height())
|
||||||
lines := strings.Split(p.Text, "\n")
|
lines := strings.Split(p.Text, "\n")
|
||||||
|
|
||||||
|
@ -62,11 +62,10 @@ func (d *LargeDialog) Arrange(ctx *Context, bounds Rectangle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *LargeDialog) Init(ctx *Context) error {
|
func (d *LargeDialog) Init(ctx *Context) error {
|
||||||
d.title = Label{
|
d.title.Text = "Botanim"
|
||||||
Text: "Botanim",
|
d.title.FontName = "title"
|
||||||
FontName: "title",
|
d.title.Alignment = TextAlignmentCenter
|
||||||
Alignment: TextAlignmentCenter,
|
|
||||||
}
|
|
||||||
d.close = IconButton{
|
d.close = IconButton{
|
||||||
Icon: "control-cancel",
|
Icon: "control-cancel",
|
||||||
IconHover: HoverEffectColor,
|
IconHover: HoverEffectColor,
|
||||||
|
17
map.go
17
map.go
@ -15,6 +15,23 @@ func (m *Map) AddFlower(pos Point, id string, traits FlowerTraits) {
|
|||||||
m.Flowers[pos] = m.NewFlower(pos, id, traits)
|
m.Flowers[pos] = m.NewFlower(pos, id, traits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Map) FlowersOnAdjacentTiles(pos Point) int {
|
||||||
|
var count int
|
||||||
|
if _, ok := m.Flowers[Pt(pos.X+1, pos.Y)]; ok {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if _, ok := m.Flowers[Pt(pos.X-1, pos.Y)]; ok {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if _, ok := m.Flowers[Pt(pos.X, pos.Y+1)]; ok {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if _, ok := m.Flowers[Pt(pos.X, pos.Y-1)]; ok {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Map) DigFlower(pos Point) string {
|
func (m *Map) DigFlower(pos Point) string {
|
||||||
flower, ok := m.Flowers[pos]
|
flower, ok := m.Flowers[pos]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -101,6 +101,20 @@ func (p *projection) visibleTiles(action func(int32, int32, Point)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *projection) Pan(ctx *Context, delta PointF) {
|
||||||
|
p.center = p.center.Add(delta.Mul(p.zoomInv))
|
||||||
|
p.update(ctx.Renderer)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *projection) ZoomOut(ctx *Context, center PointF) {
|
func (p *projection) ZoomOut(ctx *Context, center PointF) {
|
||||||
if p.zoom <= .25 {
|
if p.zoom <= .25 {
|
||||||
return
|
return
|
||||||
@ -114,12 +128,3 @@ func (p *projection) ZoomIn(ctx *Context, center PointF) {
|
|||||||
}
|
}
|
||||||
p.SetZoom(ctx, center, 2*p.zoom)
|
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)
|
|
||||||
}
|
|
||||||
|
@ -101,6 +101,14 @@ func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) bool {
|
|||||||
r.project.ZoomOut(ctx, r.project.center)
|
r.project.ZoomOut(ctx, r.project.center)
|
||||||
case sdl.K_KP_MINUS:
|
case sdl.K_KP_MINUS:
|
||||||
r.project.ZoomOut(ctx, r.project.center)
|
r.project.ZoomOut(ctx, r.project.center)
|
||||||
|
case sdl.K_w:
|
||||||
|
r.project.Pan(ctx, PtF(-1, -1))
|
||||||
|
case sdl.K_a:
|
||||||
|
r.project.Pan(ctx, PtF(-1, 1))
|
||||||
|
case sdl.K_s:
|
||||||
|
r.project.Pan(ctx, PtF(1, 1))
|
||||||
|
case sdl.K_d:
|
||||||
|
r.project.Pan(ctx, PtF(1, -1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
59
tooltip.go
Normal file
59
tooltip.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package tins2020
|
||||||
|
|
||||||
|
import "github.com/veandco/go-sdl2/sdl"
|
||||||
|
|
||||||
|
type Tooltip struct {
|
||||||
|
ControlBase
|
||||||
|
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
|
const tooltipBorderThickness = 1
|
||||||
|
const tooltipHorizontalPadding = 4
|
||||||
|
const tooltipMouseDistance = 12
|
||||||
|
|
||||||
|
func (t *Tooltip) Handle(ctx *Context, event sdl.Event) bool {
|
||||||
|
if len(t.Text) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
font := ctx.Fonts.Font(t.ActualFontName())
|
||||||
|
windowW, windowH, err := ctx.Renderer.GetOutputSize()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
labelW, labelH, err := font.SizeUTF8(t.Text)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
mouse := ctx.MousePosition
|
||||||
|
width := int32(labelW) + 2*tooltipBorderThickness + 2*tooltipHorizontalPadding
|
||||||
|
height := int32(labelH) + 2*tooltipBorderThickness
|
||||||
|
|
||||||
|
left := mouse.X + tooltipMouseDistance
|
||||||
|
top := mouse.Y + tooltipMouseDistance
|
||||||
|
if left+width > windowW {
|
||||||
|
left = mouse.X - tooltipMouseDistance - width
|
||||||
|
}
|
||||||
|
if top+height > windowH {
|
||||||
|
top = mouse.Y - tooltipMouseDistance - height
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Bounds = Rect(left, top, width, height)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Tooltip) Render(ctx *Context) {
|
||||||
|
SetDrawColor(ctx.Renderer, Black)
|
||||||
|
ctx.Renderer.FillRect(t.Bounds.SDLPtr())
|
||||||
|
|
||||||
|
SetDrawColor(ctx.Renderer, White)
|
||||||
|
ctx.Renderer.DrawRect(t.Bounds.SDLPtr())
|
||||||
|
|
||||||
|
font := t.ActualFont(ctx)
|
||||||
|
|
||||||
|
bottomLeft := Pt(t.Bounds.X+tooltipBorderThickness+tooltipHorizontalPadding, t.Bounds.Y+t.Bounds.H-tooltipBorderThickness)
|
||||||
|
font.RenderCopy(ctx.Renderer, t.Text, bottomLeft, White)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user