Compare commits
6 Commits
cd5ca3f04f
...
3a8ad733a4
Author | SHA1 | Date | |
---|---|---|---|
3a8ad733a4 | |||
a07d3c1c82 | |||
14454cfd5e | |||
3407a93586 | |||
1c208152e8 | |||
243e204f48 |
@ -10,9 +10,8 @@ import (
|
|||||||
type BuyFlowerButton struct {
|
type BuyFlowerButton struct {
|
||||||
IconButton
|
IconButton
|
||||||
|
|
||||||
Name string
|
FlowerID string
|
||||||
Price int
|
Flower FlowerDescriptor
|
||||||
Description string
|
|
||||||
|
|
||||||
hoverAnimation *Animation
|
hoverAnimation *Animation
|
||||||
hoverOffset int32
|
hoverOffset int32
|
||||||
@ -20,27 +19,33 @@ type BuyFlowerButton struct {
|
|||||||
priceTexture *Texture
|
priceTexture *Texture
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBuyFlowerButton(icon, iconDisabled, name string, price int, description string, isDisabled bool, onClick EventFn) *BuyFlowerButton {
|
func NewBuyFlowerButton(icon, iconDisabled, flowerID string, flower FlowerDescriptor, onClick EventContextFn) *BuyFlowerButton {
|
||||||
return &BuyFlowerButton{
|
return &BuyFlowerButton{
|
||||||
IconButton: *NewIconButtonConfig(icon, onClick, func(b *IconButton) {
|
IconButton: *NewIconButtonConfig(icon, onClick, func(b *IconButton) {
|
||||||
b.IconDisabled = iconDisabled
|
b.IconDisabled = iconDisabled
|
||||||
b.IsDisabled = isDisabled
|
b.IsDisabled = !flower.Unlocked
|
||||||
}),
|
}),
|
||||||
Name: name,
|
FlowerID: flowerID,
|
||||||
Price: price,
|
Flower: flower,
|
||||||
Description: description,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BuyFlowerButton) animate() {
|
func (b *BuyFlowerButton) animate() {
|
||||||
b.hoverOffset++
|
b.hoverOffset++
|
||||||
if b.hoverOffset > b.hoverTexture.Size().X+b.Bounds.W {
|
if b.hoverOffset > b.hoverTexture.Size().X+b.Bounds.W {
|
||||||
b.hoverOffset = b.priceTexture.Size().X
|
b.hoverOffset = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BuyFlowerButton) fmtTooltipText() string {
|
||||||
|
if b.Flower.Unlocked {
|
||||||
|
return fmt.Sprintf("%s - %s - %s", FmtMoney(b.Flower.BuyPrice), b.Flower.Name, "Traits are not known yet.")
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s - %s - %s", FmtMoney(b.Flower.BuyPrice), b.Flower.Name, b.Flower.Description)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *BuyFlowerButton) Init(ctx *Context) error {
|
func (b *BuyFlowerButton) Init(ctx *Context) error {
|
||||||
text := fmt.Sprintf("%s - %s - %s", FmtMoney(b.Price), b.Name, b.Description)
|
text := fmt.Sprintf("%s - %s - %s", FmtMoney(b.Flower.BuyPrice), b.Flower.Name, b.Flower.Description)
|
||||||
font := ctx.Fonts.Font("small")
|
font := ctx.Fonts.Font("small")
|
||||||
color := MustHexColor("#ffffff")
|
color := MustHexColor("#ffffff")
|
||||||
texture, err := font.Render(ctx.Renderer, text, color)
|
texture, err := font.Render(ctx.Renderer, text, color)
|
||||||
@ -48,7 +53,7 @@ func (b *BuyFlowerButton) Init(ctx *Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b.hoverTexture = texture
|
b.hoverTexture = texture
|
||||||
texture, err = font.Render(ctx.Renderer, FmtMoney(b.Price), color)
|
texture, err = font.Render(ctx.Renderer, FmtMoney(b.Flower.BuyPrice), color)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -72,7 +77,7 @@ func (b *BuyFlowerButton) Render(ctx *Context) {
|
|||||||
|
|
||||||
pos := Pt(b.Bounds.X, b.Bounds.Y)
|
pos := Pt(b.Bounds.X, b.Bounds.Y)
|
||||||
iconTexture.CopyResize(ctx.Renderer, RectSize(pos.X, pos.Y-40, buttonBarWidth, 120))
|
iconTexture.CopyResize(ctx.Renderer, RectSize(pos.X, pos.Y-40, buttonBarWidth, 120))
|
||||||
if b.IsMouseOver && !b.IsDisabled {
|
if (b.IsMouseOver && !b.IsDisabled) || b.IsActive {
|
||||||
mouseOverTexture.Copy(ctx.Renderer, pos)
|
mouseOverTexture.Copy(ctx.Renderer, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -4,7 +4,7 @@ control-research: images/genericItem_color_111.png
|
|||||||
|
|
||||||
control-settings: images/gear.png
|
control-settings: images/gear.png
|
||||||
control-save: images/save.png
|
control-save: images/save.png
|
||||||
control-load: images/basket.png
|
control-load: images/import.png
|
||||||
control-quit: images/power.png
|
control-quit: images/power.png
|
||||||
|
|
||||||
control-pause: images/pause.png
|
control-pause: images/pause.png
|
||||||
@ -64,32 +64,32 @@ bush-large-2: images/plant_bushLarge_NW.png
|
|||||||
bush-large-3: images/plant_bushLarge_SW.png
|
bush-large-3: images/plant_bushLarge_SW.png
|
||||||
bush-large-4: images/plant_bushLarge_SE.png
|
bush-large-4: images/plant_bushLarge_SE.png
|
||||||
|
|
||||||
flower-poppy-disabled: images/flower_yellowC_NE_disabled.png
|
flower-anemone-disabled: images/flower_yellowC_NE_disabled.png
|
||||||
flower-poppy-1: images/flower_yellowC_NE.png
|
flower-anemone-1: images/flower_yellowC_NE.png
|
||||||
flower-poppy-2: images/flower_yellowC_NW.png
|
flower-anemone-2: images/flower_yellowC_NW.png
|
||||||
flower-poppy-3: images/flower_yellowC_SW.png
|
flower-anemone-3: images/flower_yellowC_SW.png
|
||||||
flower-poppy-4: images/flower_yellowC_SE.png
|
flower-anemone-4: images/flower_yellowC_SE.png
|
||||||
|
|
||||||
flower-red-c-disabled: images/flower_redC_NE_disabled.png
|
flower-loosestrife-disabled: images/flower_redC_NE_disabled.png
|
||||||
flower-red-c-1: images/flower_redC_NE.png
|
flower-loosestrife-1: images/flower_redC_NE.png
|
||||||
flower-red-c-2: images/flower_redC_NW.png
|
flower-loosestrife-2: images/flower_redC_NW.png
|
||||||
flower-red-c-3: images/flower_redC_SW.png
|
flower-loosestrife-3: images/flower_redC_SW.png
|
||||||
flower-red-c-4: images/flower_redC_SE.png
|
flower-loosestrife-4: images/flower_redC_SE.png
|
||||||
|
|
||||||
flower-red-a-disabled: images/flower_redA_NE_disabled.png
|
flower-tulip-disabled: images/flower_redA_NE_disabled.png
|
||||||
flower-red-a-1: images/flower_redA_NE.png
|
flower-tulip-1: images/flower_redA_NE.png
|
||||||
flower-red-a-2: images/flower_redA_NW.png
|
flower-tulip-2: images/flower_redA_NW.png
|
||||||
flower-red-a-3: images/flower_redA_SW.png
|
flower-tulip-3: images/flower_redA_SW.png
|
||||||
flower-red-a-4: images/flower_redA_SE.png
|
flower-tulip-4: images/flower_redA_SE.png
|
||||||
|
|
||||||
flower-purple-a-disabled: images/flower_purpleA_NE_disabled.png
|
flower-ajuga-disabled: images/flower_purpleA_NE_disabled.png
|
||||||
flower-purple-a-1: images/flower_purpleA_NE.png
|
flower-ajuga-1: images/flower_purpleA_NE.png
|
||||||
flower-purple-a-2: images/flower_purpleA_NW.png
|
flower-ajuga-2: images/flower_purpleA_NW.png
|
||||||
flower-purple-a-3: images/flower_purpleA_SW.png
|
flower-ajuga-3: images/flower_purpleA_SW.png
|
||||||
flower-purple-a-4: images/flower_purpleA_SE.png
|
flower-ajuga-4: images/flower_purpleA_SE.png
|
||||||
|
|
||||||
flower-purple-c-disabled: images/flower_purpleC_NE_disabled.png
|
flower-coneflower-disabled: images/flower_purpleC_NE_disabled.png
|
||||||
flower-purple-c-1: images/flower_purpleC_NE.png
|
flower-coneflower-1: images/flower_purpleC_NE.png
|
||||||
flower-purple-c-2: images/flower_purpleC_NW.png
|
flower-coneflower-2: images/flower_purpleC_NW.png
|
||||||
flower-purple-c-3: images/flower_purpleC_SW.png
|
flower-coneflower-3: images/flower_purpleC_SW.png
|
||||||
flower-purple-c-4: images/flower_purpleC_SE.png
|
flower-coneflower-4: images/flower_purpleC_SE.png
|
||||||
|
@ -47,7 +47,14 @@ func run() error {
|
|||||||
if ctx.Settings.Window.Size == nil {
|
if ctx.Settings.Window.Size == nil {
|
||||||
ctx.Settings.Window.Size = tins2020.PtPtr(800, 600)
|
ctx.Settings.Window.Size = tins2020.PtPtr(800, 600)
|
||||||
}
|
}
|
||||||
|
if ctx.Settings.Window.VSync == nil {
|
||||||
|
vsync := true
|
||||||
|
ctx.Settings.Window.VSync = &vsync
|
||||||
|
}
|
||||||
|
|
||||||
|
if *ctx.Settings.Window.VSync {
|
||||||
|
sdl.SetHint(sdl.HINT_RENDER_VSYNC, "1")
|
||||||
|
}
|
||||||
sdl.SetHint(sdl.HINT_RENDER_SCALE_QUALITY, "1")
|
sdl.SetHint(sdl.HINT_RENDER_SCALE_QUALITY, "1")
|
||||||
window, err := sdl.CreateWindow("TINS 2020",
|
window, err := sdl.CreateWindow("TINS 2020",
|
||||||
ctx.Settings.Window.Location.X, ctx.Settings.Window.Location.Y,
|
ctx.Settings.Window.Location.X, ctx.Settings.Window.Location.Y,
|
||||||
@ -93,7 +100,7 @@ func run() error {
|
|||||||
content := tins2020.NewContainer()
|
content := tins2020.NewContainer()
|
||||||
app.AddChild(content)
|
app.AddChild(content)
|
||||||
app.AddChild(overlays)
|
app.AddChild(overlays)
|
||||||
content.AddChild(tins2020.NewTerrainRenderer(game.Terrain))
|
content.AddChild(tins2020.NewTerrainRenderer(game))
|
||||||
err = app.Init(ctx)
|
err = app.Init(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -118,11 +125,6 @@ func run() error {
|
|||||||
app.Arrange(ctx, tins2020.Rect(0, 0, w, h))
|
app.Arrange(ctx, tins2020.Rect(0, 0, w, h))
|
||||||
ctx.Settings.Window.Size = tins2020.PtPtr(w, h)
|
ctx.Settings.Window.Size = tins2020.PtPtr(w, h)
|
||||||
}
|
}
|
||||||
case *sdl.KeyboardEvent:
|
|
||||||
switch e.Keysym.Sym {
|
|
||||||
case sdl.K_ESCAPE:
|
|
||||||
ctx.Quit()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
app.Handle(ctx, event)
|
app.Handle(ctx, event)
|
||||||
}
|
}
|
||||||
|
12
control.go
12
control.go
@ -9,11 +9,13 @@ type Control interface {
|
|||||||
Render(*Context)
|
Render(*Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
type EventFn func(*Context)
|
type EventContextFn func(*Context)
|
||||||
|
|
||||||
type EmptyEventFn func()
|
type EventFn func()
|
||||||
|
|
||||||
func EmptyEvent(fn EmptyEventFn) EventFn {
|
type EventInterfaceFn func(interface{})
|
||||||
|
|
||||||
|
func EmptyEvent(fn EventFn) EventContextFn {
|
||||||
return func(*Context) { fn() }
|
return func(*Context) { fn() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,7 +24,7 @@ type ControlBase struct {
|
|||||||
|
|
||||||
IsMouseOver bool
|
IsMouseOver bool
|
||||||
|
|
||||||
OnLeftMouseButtonClick EventFn
|
OnLeftMouseButtonClick EventContextFn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *ControlBase) Arrange(ctx *Context, bounds Rectangle) { b.Bounds = bounds }
|
func (b *ControlBase) Arrange(ctx *Context, bounds Rectangle) { b.Bounds = bounds }
|
||||||
@ -40,7 +42,7 @@ func (b *ControlBase) Handle(ctx *Context, event sdl.Event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *ControlBase) Invoke(ctx *Context, fn EventFn) {
|
func (b *ControlBase) Invoke(ctx *Context, fn EventContextFn) {
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
33
eventhandler.go
Normal file
33
eventhandler.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package tins2020
|
||||||
|
|
||||||
|
func NewEvents() *Events {
|
||||||
|
return &Events{events: map[int]EventInterfaceFn{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Events struct {
|
||||||
|
nextID int
|
||||||
|
events map[int]EventInterfaceFn
|
||||||
|
}
|
||||||
|
|
||||||
|
type EventHandler interface {
|
||||||
|
Register(EventFn) int
|
||||||
|
RegisterItf(EventInterfaceFn) int
|
||||||
|
Unregister(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Events) Notify(state interface{}) {
|
||||||
|
for _, event := range h.events {
|
||||||
|
event(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Events) Register(fn EventFn) int { return h.RegisterItf(func(interface{}) { fn() }) }
|
||||||
|
|
||||||
|
func (h *Events) RegisterItf(fn EventInterfaceFn) int {
|
||||||
|
id := h.nextID
|
||||||
|
h.nextID++
|
||||||
|
h.events[id] = fn
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Events) Unregister(id int) { delete(h.events, id) }
|
64
flower.go
64
flower.go
@ -1,6 +1,7 @@
|
|||||||
package tins2020
|
package tins2020
|
||||||
|
|
||||||
type Flower struct {
|
type Flower struct {
|
||||||
|
ID string
|
||||||
Traits FlowerTraits
|
Traits FlowerTraits
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,17 +32,68 @@ type FlowerResistance struct {
|
|||||||
Wet float32
|
Wet float32
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPoppyTraits creates the traits of a poppy, a very generic flower that thrives in a moderate climate.
|
// NewAnemoneTraits creates the traits of a anemone, a very generic flower that thrives in a moderate climate.
|
||||||
func NewPoppyTraits() FlowerTraits {
|
func NewAnemoneTraits() FlowerTraits {
|
||||||
return FlowerTraits{
|
return FlowerTraits{
|
||||||
// Spread: 0.0011,
|
Spread: 0.0004,
|
||||||
Spread: 0.0011,
|
|
||||||
Life: 0.99993,
|
Life: 0.99993,
|
||||||
Resistance: FlowerResistance{
|
Resistance: FlowerResistance{
|
||||||
Cold: 0.5,
|
Cold: 0.7,
|
||||||
Hot: 0.5,
|
Hot: 0.7,
|
||||||
Dry: 0.5,
|
Dry: 0.5,
|
||||||
Wet: 0.5,
|
Wet: 0.5,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewLoosestrifeTraits() FlowerTraits {
|
||||||
|
return FlowerTraits{
|
||||||
|
Spread: 0.0005,
|
||||||
|
Life: 0.99993,
|
||||||
|
Resistance: FlowerResistance{
|
||||||
|
Cold: 0.7,
|
||||||
|
Hot: 0.6,
|
||||||
|
Dry: 0.5,
|
||||||
|
Wet: 0.8,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConeflowerTraits() FlowerTraits {
|
||||||
|
return FlowerTraits{
|
||||||
|
Spread: 0.0005,
|
||||||
|
Life: 0.99993,
|
||||||
|
Resistance: FlowerResistance{
|
||||||
|
Cold: 0.7,
|
||||||
|
Hot: 0.8,
|
||||||
|
Dry: 0.8,
|
||||||
|
Wet: 0.5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTulipTraits() FlowerTraits {
|
||||||
|
return FlowerTraits{
|
||||||
|
Spread: 0.0005,
|
||||||
|
Life: 0.99993,
|
||||||
|
Resistance: FlowerResistance{
|
||||||
|
Cold: 0.8,
|
||||||
|
Hot: 0.6,
|
||||||
|
Dry: 0.5,
|
||||||
|
Wet: 0.9,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAjugaTraits() FlowerTraits {
|
||||||
|
return FlowerTraits{
|
||||||
|
Spread: 0.0007,
|
||||||
|
Life: 0.99991,
|
||||||
|
Resistance: FlowerResistance{
|
||||||
|
Cold: 0.9,
|
||||||
|
Hot: 0.5,
|
||||||
|
Dry: 0.75,
|
||||||
|
Wet: 0.75,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
183
game.go
183
game.go
@ -1,6 +1,7 @@
|
|||||||
package tins2020
|
package tins2020
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -8,8 +9,13 @@ import (
|
|||||||
type Game struct {
|
type Game struct {
|
||||||
Balance int
|
Balance int
|
||||||
Speed GameSpeed
|
Speed GameSpeed
|
||||||
|
SpeedBeforePause GameSpeed
|
||||||
|
Herbarium Herbarium
|
||||||
Terrain *Map
|
Terrain *Map
|
||||||
|
|
||||||
|
tool Tool
|
||||||
|
toolChanged *Events
|
||||||
|
speedChanged *Events
|
||||||
simulation Animation
|
simulation Animation
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,29 +27,6 @@ const (
|
|||||||
GameSpeedPaused = "paused"
|
GameSpeedPaused = "paused"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Map struct {
|
|
||||||
Temp NoiseMap
|
|
||||||
Humid NoiseMap
|
|
||||||
Variant NoiseMap
|
|
||||||
PlaceX NoiseMap // displacement map of props
|
|
||||||
PlaceY NoiseMap
|
|
||||||
|
|
||||||
Flowers map[Point]Flower
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Map) AddFlower(pos Point, traits FlowerTraits) {
|
|
||||||
m.Flowers[pos] = m.NewFlower(pos, traits)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Map) NewFlower(pos Point, traits FlowerTraits) Flower {
|
|
||||||
flower := Flower{
|
|
||||||
Traits: traits,
|
|
||||||
}
|
|
||||||
temp, humid := float32(m.Temp.Value(pos.X, pos.Y)), float32(m.Humid.Value(pos.X, pos.Y))
|
|
||||||
flower.Traits.UpdateModifier(temp, humid)
|
|
||||||
return flower
|
|
||||||
}
|
|
||||||
|
|
||||||
const simulationInterval = 120 * time.Millisecond
|
const simulationInterval = 120 * time.Millisecond
|
||||||
const fastSimulationInterval = 40 * time.Millisecond
|
const fastSimulationInterval = 40 * time.Millisecond
|
||||||
|
|
||||||
@ -56,16 +39,91 @@ func NewGame() *Game {
|
|||||||
PlaceY: NewRandomNoiseMap(rand.Int63()),
|
PlaceY: NewRandomNoiseMap(rand.Int63()),
|
||||||
Flowers: map[Point]Flower{},
|
Flowers: map[Point]Flower{},
|
||||||
}
|
}
|
||||||
terrain.AddFlower(Pt(0, 0), NewPoppyTraits())
|
herbarium := NewHerbarium()
|
||||||
|
herbarium.Add("anemone", FlowerDescriptor{
|
||||||
|
Name: "Anemone",
|
||||||
|
Description: "A very generic flower that thrives in a temperate climate.",
|
||||||
|
IconTemplate: "flower-anemone-%s",
|
||||||
|
BuyPrice: 10,
|
||||||
|
SellPrice: 3,
|
||||||
|
Traits: NewAnemoneTraits(),
|
||||||
|
Unlocked: true,
|
||||||
|
})
|
||||||
|
herbarium.Add("loosestrife", FlowerDescriptor{
|
||||||
|
Name: "Loosestrife",
|
||||||
|
Description: "A simple flower that will spread in temperate and wet climates.",
|
||||||
|
IconTemplate: "flower-loosestrife-%s",
|
||||||
|
BuyPrice: 100,
|
||||||
|
SellPrice: 20,
|
||||||
|
Traits: NewLoosestrifeTraits(),
|
||||||
|
Unlocked: false,
|
||||||
|
})
|
||||||
|
herbarium.Add("coneflower", FlowerDescriptor{
|
||||||
|
Name: "Coneflower",
|
||||||
|
Description: "A beautifull flower that can withstand hotter climates.",
|
||||||
|
IconTemplate: "flower-coneflower-%s",
|
||||||
|
BuyPrice: 500,
|
||||||
|
SellPrice: 100,
|
||||||
|
Traits: NewConeflowerTraits(),
|
||||||
|
Unlocked: false,
|
||||||
|
})
|
||||||
|
herbarium.Add("tulip", FlowerDescriptor{
|
||||||
|
Name: "Tulip",
|
||||||
|
Description: "A lovely flower that prefers a bit humid and colder climates.",
|
||||||
|
IconTemplate: "flower-tulip-%s",
|
||||||
|
BuyPrice: 20000,
|
||||||
|
SellPrice: 5000,
|
||||||
|
Traits: NewTulipTraits(),
|
||||||
|
Unlocked: false,
|
||||||
|
})
|
||||||
|
herbarium.Add("ajuga", FlowerDescriptor{
|
||||||
|
Name: "Ajuga",
|
||||||
|
Description: "A flower that is resitant to cold climates and spreads very easily.",
|
||||||
|
IconTemplate: "flower-ajuga-%s",
|
||||||
|
BuyPrice: 100000,
|
||||||
|
SellPrice: 10000,
|
||||||
|
Traits: NewAjugaTraits(),
|
||||||
|
Unlocked: false,
|
||||||
|
})
|
||||||
return &Game{
|
return &Game{
|
||||||
Speed: GameSpeedNormal,
|
Speed: GameSpeedNormal,
|
||||||
Balance: 100,
|
Balance: 100,
|
||||||
Terrain: terrain,
|
Terrain: terrain,
|
||||||
|
Herbarium: herbarium,
|
||||||
|
|
||||||
|
speedChanged: NewEvents(),
|
||||||
|
toolChanged: NewEvents(),
|
||||||
simulation: NewAnimation(time.Millisecond * 10),
|
simulation: NewAnimation(time.Millisecond * 10),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Game) selectTool(t Tool) {
|
||||||
|
g.tool = t
|
||||||
|
g.toolChanged.Notify(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) setSpeed(speed GameSpeed) {
|
||||||
|
if speed == g.Speed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if speed == GameSpeedPaused {
|
||||||
|
g.SpeedBeforePause = g.Speed
|
||||||
|
}
|
||||||
|
g.Speed = speed
|
||||||
|
g.speedChanged.Notify(speed)
|
||||||
|
|
||||||
|
switch speed {
|
||||||
|
case GameSpeedPaused:
|
||||||
|
g.simulation.Pause()
|
||||||
|
case GameSpeedNormal:
|
||||||
|
g.simulation.SetInterval(simulationInterval)
|
||||||
|
g.simulation.Run()
|
||||||
|
case GameSpeedFast:
|
||||||
|
g.simulation.SetInterval(fastSimulationInterval)
|
||||||
|
g.simulation.Run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (g *Game) tick() {
|
func (g *Game) tick() {
|
||||||
randomNeighbor := func(pos Point) Point {
|
randomNeighbor := func(pos Point) Point {
|
||||||
switch rand.Intn(4) {
|
switch rand.Intn(4) {
|
||||||
@ -86,36 +144,87 @@ func (g *Game) tick() {
|
|||||||
dst := randomNeighbor(pos)
|
dst := randomNeighbor(pos)
|
||||||
if _, ok := g.Terrain.Flowers[dst]; !ok {
|
if _, ok := g.Terrain.Flowers[dst]; !ok {
|
||||||
if _, ok := flowers[dst]; !ok {
|
if _, ok := flowers[dst]; !ok {
|
||||||
flowers[dst] = g.Terrain.NewFlower(dst, flower.Traits)
|
flowers[dst] = g.Terrain.NewFlower(dst, flower.ID, flower.Traits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rand.Float32() < flower.Traits.Life*flower.Traits.LifeModifier {
|
if Sqr32(rand.Float32()) < flower.Traits.Life*flower.Traits.LifeModifier {
|
||||||
flowers[pos] = flower
|
flowers[pos] = flower
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g.Terrain.Flowers = flowers
|
g.Terrain.Flowers = flowers
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Pause() {
|
func (g *Game) CancelTool() {
|
||||||
g.Speed = GameSpeedPaused
|
g.selectTool(nil)
|
||||||
g.simulation.Pause()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Run() {
|
func (g *Game) Dig(tile Point) {
|
||||||
g.Speed = GameSpeedNormal
|
id := g.Terrain.DigFlower(tile)
|
||||||
g.simulation.SetInterval(simulationInterval)
|
desc, ok := g.Herbarium.Find(id)
|
||||||
g.simulation.Run()
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.Balance += desc.SellPrice
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) RunFast() {
|
func (g *Game) Pause() { g.setSpeed(GameSpeedPaused) }
|
||||||
g.Speed = GameSpeedFast
|
|
||||||
g.simulation.SetInterval(fastSimulationInterval)
|
func (g *Game) PlantFlower(id string, tile Point) {
|
||||||
g.simulation.Run()
|
flower, ok := g.Herbarium.Find(id)
|
||||||
|
if !ok {
|
||||||
|
log.Println("user was able to plant a flower that doesn't exist")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
if flower.BuyPrice > g.Balance {
|
||||||
|
// TODO: notify user of insufficient balance?
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.Balance -= flower.BuyPrice
|
||||||
|
g.Terrain.AddFlower(tile, id, flower.Traits)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Resume() { g.setSpeed(g.SpeedBeforePause) }
|
||||||
|
|
||||||
|
func (g *Game) Run() { g.setSpeed(GameSpeedNormal) }
|
||||||
|
|
||||||
|
func (g *Game) RunFast() { g.setSpeed(GameSpeedFast) }
|
||||||
|
|
||||||
|
func (g *Game) SelectPlantFlowerTool(id string) {
|
||||||
|
g.selectTool(&PlantFlowerTool{FlowerID: id})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) SelectShovel() {
|
||||||
|
g.selectTool(&ShovelTool{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) SelectResearch() {
|
||||||
|
g.Pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) SpeedChanged() EventHandler { return g.speedChanged }
|
||||||
|
|
||||||
|
func (g *Game) TogglePause() {
|
||||||
|
if g.Speed == GameSpeedPaused {
|
||||||
|
g.Resume()
|
||||||
|
} else {
|
||||||
|
g.Pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Tool() Tool { return g.tool }
|
||||||
|
|
||||||
|
func (g *Game) ToolChanged() EventHandler { return g.toolChanged }
|
||||||
|
|
||||||
func (g *Game) Update() {
|
func (g *Game) Update() {
|
||||||
for g.simulation.Animate() {
|
for g.simulation.Animate() {
|
||||||
g.tick()
|
g.tick()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Game) UserClickedTile(pos Point) {
|
||||||
|
if g.tool == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.tool.ClickedTile(g, pos)
|
||||||
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
package tins2020
|
package tins2020
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
|
)
|
||||||
|
|
||||||
type GameControls struct {
|
type GameControls struct {
|
||||||
Container
|
Container
|
||||||
|
|
||||||
@ -8,63 +12,92 @@ type GameControls struct {
|
|||||||
menu ButtonBar
|
menu ButtonBar
|
||||||
top ButtonBar
|
top ButtonBar
|
||||||
flowers ButtonBar
|
flowers ButtonBar
|
||||||
|
otherTools ButtonBar
|
||||||
|
|
||||||
pause *IconButton
|
pause *IconButton
|
||||||
run *IconButton
|
run *IconButton
|
||||||
runFast *IconButton
|
runFast *IconButton
|
||||||
|
|
||||||
|
shovel *IconButton
|
||||||
|
research *IconButton
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGameControls(game *Game) *GameControls {
|
func NewGameControls(game *Game) *GameControls {
|
||||||
return &GameControls{game: game}
|
return &GameControls{game: game}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GameControls) updateSpeedControls() {
|
func (c *GameControls) createBuyFlowerButton(id string) *BuyFlowerButton {
|
||||||
disable := func(b *IconButton, speed GameSpeed) {
|
flower, _ := c.game.Herbarium.Find(id)
|
||||||
b.IsDisabled = speed == c.game.Speed
|
return NewBuyFlowerButton(
|
||||||
|
flower.IconTemplate.Variant(1),
|
||||||
|
flower.IconTemplate.Disabled(),
|
||||||
|
id,
|
||||||
|
flower,
|
||||||
|
EmptyEvent(func() {
|
||||||
|
c.game.SelectPlantFlowerTool(id)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GameControls) speedChanged(state interface{}) {
|
||||||
|
speed := state.(GameSpeed)
|
||||||
|
disable := func(b *IconButton, expected GameSpeed) {
|
||||||
|
b.IsDisabled = speed == expected
|
||||||
}
|
}
|
||||||
disable(c.pause, GameSpeedPaused)
|
disable(c.pause, GameSpeedPaused)
|
||||||
disable(c.run, GameSpeedNormal)
|
disable(c.run, GameSpeedNormal)
|
||||||
disable(c.runFast, GameSpeedFast)
|
disable(c.runFast, GameSpeedFast)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *GameControls) toolChanged(state interface{}) {
|
||||||
|
tool, _ := state.(Tool)
|
||||||
|
var flowerID string
|
||||||
|
if tool, ok := tool.(*PlantFlowerTool); ok {
|
||||||
|
flowerID = tool.FlowerID
|
||||||
|
}
|
||||||
|
for _, control := range c.flowers.Buttons {
|
||||||
|
button := control.(*BuyFlowerButton)
|
||||||
|
button.IsActive = button.FlowerID == flowerID
|
||||||
|
}
|
||||||
|
_, shovel := tool.(*ShovelTool)
|
||||||
|
c.shovel.IsActive = shovel
|
||||||
|
}
|
||||||
|
|
||||||
func (c *GameControls) Arrange(ctx *Context, bounds Rectangle) {
|
func (c *GameControls) Arrange(ctx *Context, bounds Rectangle) {
|
||||||
c.Bounds = bounds
|
c.Bounds = bounds
|
||||||
c.menu.Arrange(ctx, RectSize(bounds.X, bounds.Y, buttonBarWidth, bounds.H))
|
c.menu.Arrange(ctx, RectSize(bounds.X, bounds.Y, buttonBarWidth, bounds.H))
|
||||||
c.top.Arrange(ctx, Rect(bounds.X+bounds.W/2+8, bounds.Y, bounds.Right(), bounds.Y+64))
|
c.top.Arrange(ctx, Rect(bounds.X+bounds.W/2+8, bounds.Y, bounds.Right(), bounds.Y+64))
|
||||||
c.flowers.Arrange(ctx, RectSize(bounds.Right()-buttonBarWidth, bounds.Y, buttonBarWidth, bounds.H))
|
c.flowers.Arrange(ctx, RectSize(bounds.Right()-buttonBarWidth, bounds.Y, buttonBarWidth, bounds.H))
|
||||||
}
|
c.otherTools.Arrange(ctx, RectSize(bounds.Right()-buttonBarWidth, bounds.Bottom()-2*buttonBarWidth, buttonBarWidth, 2*buttonBarWidth))
|
||||||
|
|
||||||
func (c *GameControls) buyPoppy(ctx *Context) {
|
|
||||||
c.game.Balance -= 10
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GameControls) Init(ctx *Context) error {
|
func (c *GameControls) Init(ctx *Context) error {
|
||||||
|
c.game.SpeedChanged().RegisterItf(c.speedChanged)
|
||||||
|
c.game.ToolChanged().RegisterItf(c.toolChanged)
|
||||||
|
|
||||||
c.flowers.Background = MustHexColor("#356dad") // brown alternative? #4ac69a
|
c.flowers.Background = MustHexColor("#356dad") // brown alternative? #4ac69a
|
||||||
c.flowers.Buttons = []Control{
|
|
||||||
NewBuyFlowerButton("flower-poppy-1", "flower-poppy-disabled", "Poppy", 10, "A very generic flower that thrives in a moderate climate.", false, c.buyPoppy),
|
for _, id := range c.game.Herbarium.Flowers() {
|
||||||
NewBuyFlowerButton("flower-red-c-1", "flower-poppy-disabled", "Unknown", 100, "Traits are not known yet.", true, nil),
|
c.flowers.Buttons = append(c.flowers.Buttons, c.createBuyFlowerButton(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
c.top.Orientation = OrientationHorizontal
|
c.top.Orientation = OrientationHorizontal
|
||||||
c.pause = NewIconButtonConfig("control-pause", EmptyEvent(func() {
|
c.pause = NewIconButtonConfig("control-pause", EmptyEvent(func() {
|
||||||
c.game.Pause()
|
c.game.Pause()
|
||||||
c.updateSpeedControls()
|
|
||||||
}), func(b *IconButton) {
|
}), func(b *IconButton) {
|
||||||
b.IconDisabled = "control-pause-disabled"
|
b.IconDisabled = "control-pause-disabled"
|
||||||
})
|
})
|
||||||
c.run = NewIconButtonConfig("control-run", EmptyEvent(func() {
|
c.run = NewIconButtonConfig("control-run", EmptyEvent(func() {
|
||||||
c.game.Run()
|
c.game.Run()
|
||||||
c.updateSpeedControls()
|
|
||||||
}), func(b *IconButton) {
|
}), func(b *IconButton) {
|
||||||
b.IconDisabled = "control-run-disabled"
|
b.IconDisabled = "control-run-disabled"
|
||||||
})
|
})
|
||||||
c.runFast = NewIconButtonConfig("control-run-fast", EmptyEvent(func() {
|
c.runFast = NewIconButtonConfig("control-run-fast", EmptyEvent(func() {
|
||||||
c.game.RunFast()
|
c.game.RunFast()
|
||||||
c.updateSpeedControls()
|
|
||||||
}), func(b *IconButton) {
|
}), func(b *IconButton) {
|
||||||
b.IconDisabled = "control-run-fast-disabled"
|
b.IconDisabled = "control-run-fast-disabled"
|
||||||
})
|
})
|
||||||
c.updateSpeedControls()
|
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")
|
||||||
@ -74,12 +107,45 @@ func (c *GameControls) Init(ctx *Context) error {
|
|||||||
NewIconButton("control-load", EmptyEvent(func() {})),
|
NewIconButton("control-load", EmptyEvent(func() {})),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.shovel = NewIconButtonConfig("control-shovel", func(*Context) { c.game.SelectShovel() }, func(b *IconButton) { b.IconHeight = 48 })
|
||||||
|
c.research = NewIconButtonConfig("control-research", func(*Context) { c.game.SelectResearch() }, func(b *IconButton) { b.IconHeight = 48 })
|
||||||
|
c.otherTools.Buttons = []Control{c.shovel, c.research}
|
||||||
|
|
||||||
c.Container.AddChild(&c.menu)
|
c.Container.AddChild(&c.menu)
|
||||||
c.Container.AddChild(&c.top)
|
c.Container.AddChild(&c.top)
|
||||||
c.Container.AddChild(&c.flowers)
|
c.Container.AddChild(&c.flowers)
|
||||||
|
c.Container.AddChild(&c.otherTools)
|
||||||
return c.Container.Init(ctx)
|
return c.Container.Init(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *GameControls) Handle(ctx *Context, event sdl.Event) {
|
||||||
|
c.Container.Handle(ctx, event)
|
||||||
|
|
||||||
|
switch e := event.(type) {
|
||||||
|
case *sdl.KeyboardEvent:
|
||||||
|
if e.Type == sdl.KEYDOWN {
|
||||||
|
switch e.Keysym.Sym {
|
||||||
|
case sdl.K_SPACE:
|
||||||
|
c.game.TogglePause()
|
||||||
|
case sdl.K_1:
|
||||||
|
c.game.Run()
|
||||||
|
case sdl.K_2:
|
||||||
|
c.game.RunFast()
|
||||||
|
case sdl.K_d:
|
||||||
|
c.game.SelectShovel()
|
||||||
|
case sdl.K_r:
|
||||||
|
c.game.SelectResearch()
|
||||||
|
case sdl.K_ESCAPE:
|
||||||
|
if c.game.Tool() == nil {
|
||||||
|
// TODO: display menu
|
||||||
|
} else {
|
||||||
|
c.game.CancelTool()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *GameControls) Render(ctx *Context) {
|
func (c *GameControls) Render(ctx *Context) {
|
||||||
topBar := MustHexColor("#0000007f")
|
topBar := MustHexColor("#0000007f")
|
||||||
ctx.Renderer.SetDrawColor(topBar.R, topBar.G, topBar.B, topBar.A)
|
ctx.Renderer.SetDrawColor(topBar.R, topBar.G, topBar.B, topBar.A)
|
||||||
|
46
herbarium.go
Normal file
46
herbarium.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package tins2020
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Herbarium struct {
|
||||||
|
flowers map[string]FlowerDescriptor
|
||||||
|
order []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHerbarium() Herbarium {
|
||||||
|
return Herbarium{map[string]FlowerDescriptor{}, nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlowerDescriptor struct {
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
BuyPrice int
|
||||||
|
SellPrice int
|
||||||
|
|
||||||
|
Unlocked bool
|
||||||
|
IconTemplate IconTemplate
|
||||||
|
Traits FlowerTraits
|
||||||
|
}
|
||||||
|
|
||||||
|
type IconTemplate string
|
||||||
|
|
||||||
|
func (t IconTemplate) Disabled() string { return t.Fmt("disabled") }
|
||||||
|
|
||||||
|
func (t IconTemplate) Fmt(s string) string { return fmt.Sprintf(string(t), s) }
|
||||||
|
|
||||||
|
func (t IconTemplate) Variant(i int) string { return t.Fmt(strconv.Itoa(i)) }
|
||||||
|
|
||||||
|
func (h *Herbarium) Add(id string, desc FlowerDescriptor) {
|
||||||
|
h.flowers[id] = desc
|
||||||
|
h.order = append(h.order, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Herbarium) Find(id string) (FlowerDescriptor, bool) {
|
||||||
|
flower, ok := h.flowers[id]
|
||||||
|
return flower, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Herbarium) Flowers() []string { return h.order }
|
@ -5,9 +5,11 @@ type IconButton struct {
|
|||||||
|
|
||||||
Icon string
|
Icon string
|
||||||
IconDisabled string
|
IconDisabled string
|
||||||
|
IconHeight int32
|
||||||
IconScale Scale
|
IconScale Scale
|
||||||
IconWidth int32
|
IconWidth int32
|
||||||
|
|
||||||
|
IsActive bool
|
||||||
IsDisabled bool
|
IsDisabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,7 +20,7 @@ const (
|
|||||||
ScaleStretch
|
ScaleStretch
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewIconButton(icon string, onClick EventFn) *IconButton {
|
func NewIconButton(icon string, onClick EventContextFn) *IconButton {
|
||||||
return &IconButton{
|
return &IconButton{
|
||||||
ControlBase: ControlBase{
|
ControlBase: ControlBase{
|
||||||
OnLeftMouseButtonClick: onClick,
|
OnLeftMouseButtonClick: onClick,
|
||||||
@ -27,7 +29,7 @@ func NewIconButton(icon string, onClick EventFn) *IconButton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIconButtonConfig(icon string, onClick EventFn, configure func(*IconButton)) *IconButton {
|
func NewIconButtonConfig(icon string, onClick EventContextFn, configure func(*IconButton)) *IconButton {
|
||||||
button := NewIconButton(icon, onClick)
|
button := NewIconButton(icon, onClick)
|
||||||
configure(button)
|
configure(button)
|
||||||
return button
|
return button
|
||||||
@ -51,12 +53,14 @@ func (b *IconButton) Render(ctx *Context) {
|
|||||||
size := iconTexture.Size()
|
size := iconTexture.Size()
|
||||||
if b.IconWidth != 0 {
|
if b.IconWidth != 0 {
|
||||||
size = Pt(b.IconWidth, b.IconWidth*size.Y/size.X)
|
size = Pt(b.IconWidth, b.IconWidth*size.Y/size.X)
|
||||||
|
} else if b.IconHeight != 0 {
|
||||||
|
size = Pt(b.IconHeight*size.X/size.Y, b.IconHeight)
|
||||||
}
|
}
|
||||||
iconTexture.CopyResize(ctx.Renderer, RectSize(b.Bounds.X+(b.Bounds.W-size.X)/2, b.Bounds.Y+(b.Bounds.H-size.Y)/2, size.X, size.Y))
|
iconTexture.CopyResize(ctx.Renderer, RectSize(b.Bounds.X+(b.Bounds.W-size.X)/2, b.Bounds.Y+(b.Bounds.H-size.Y)/2, size.X, size.Y))
|
||||||
} else {
|
} else {
|
||||||
iconTexture.CopyResize(ctx.Renderer, b.Bounds)
|
iconTexture.CopyResize(ctx.Renderer, b.Bounds)
|
||||||
}
|
}
|
||||||
if b.IsMouseOver && !b.IsDisabled {
|
if (b.IsMouseOver && !b.IsDisabled) || b.IsActive {
|
||||||
mouseOverTexture.CopyResize(ctx.Renderer, b.Bounds)
|
mouseOverTexture.CopyResize(ctx.Renderer, b.Bounds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
34
map.go
Normal file
34
map.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package tins2020
|
||||||
|
|
||||||
|
type Map struct {
|
||||||
|
Temp NoiseMap
|
||||||
|
Humid NoiseMap
|
||||||
|
Variant NoiseMap
|
||||||
|
PlaceX NoiseMap // displacement map of props
|
||||||
|
PlaceY NoiseMap
|
||||||
|
|
||||||
|
Flowers map[Point]Flower
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map) AddFlower(pos Point, id string, traits FlowerTraits) {
|
||||||
|
m.Flowers[pos] = m.NewFlower(pos, id, traits)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map) DigFlower(pos Point) string {
|
||||||
|
flower, ok := m.Flowers[pos]
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
delete(m.Flowers, pos)
|
||||||
|
return flower.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map) NewFlower(pos Point, id string, traits FlowerTraits) Flower {
|
||||||
|
flower := Flower{
|
||||||
|
ID: id,
|
||||||
|
Traits: traits,
|
||||||
|
}
|
||||||
|
temp, humid := float32(m.Temp.Value(pos.X, pos.Y)), float32(m.Humid.Value(pos.X, pos.Y))
|
||||||
|
flower.Traits.UpdateModifier(temp, humid)
|
||||||
|
return flower
|
||||||
|
}
|
2
math.go
2
math.go
@ -50,4 +50,6 @@ func Min32(a, b float32) float32 {
|
|||||||
|
|
||||||
func Round32(x float32) float32 { return float32(math.Round(float64(x))) }
|
func Round32(x float32) float32 { return float32(math.Round(float64(x))) }
|
||||||
|
|
||||||
|
func Sqr32(x float32) float32 { return x * x }
|
||||||
|
|
||||||
func Sqrt32(x float32) float32 { return float32(math.Sqrt(float64(x))) }
|
func Sqrt32(x float32) float32 { return float32(math.Sqrt(float64(x))) }
|
||||||
|
2
point.go
2
point.go
@ -10,6 +10,8 @@ func (p Point) Add(q Point) Point { return Pt(p.X+q.X, p.Y+q.Y) }
|
|||||||
|
|
||||||
func (p Point) In(r Rectangle) bool { return r.IsPointInsidePt(p) }
|
func (p Point) In(r Rectangle) bool { return r.IsPointInsidePt(p) }
|
||||||
|
|
||||||
|
func (p Point) Sub(q Point) Point { return Pt(p.X-q.X, p.Y-q.Y) }
|
||||||
|
|
||||||
func (p Point) ToPtF() PointF { return PtF(float32(p.X), float32(p.Y)) }
|
func (p Point) ToPtF() PointF { return PtF(float32(p.X), float32(p.Y)) }
|
||||||
|
|
||||||
type PointF struct {
|
type PointF struct {
|
||||||
|
@ -39,6 +39,11 @@ func (p *projection) screenToMap(x, y int32) PointF {
|
|||||||
return p.center.Add(pos)
|
return p.center.Add(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *projection) screenToMapInt(x, y int32) Point {
|
||||||
|
pos := p.screenToMap(x, y)
|
||||||
|
return Pt(int32(Round32(pos.X)), int32(Round32(pos.Y)))
|
||||||
|
}
|
||||||
|
|
||||||
func (p *projection) screenToMapRel(x, y int32) PointF {
|
func (p *projection) screenToMapRel(x, y int32) PointF {
|
||||||
normX := p.zoomInv * float32(x)
|
normX := p.zoomInv * float32(x)
|
||||||
normY := p.zoomInv * float32(y)
|
normY := p.zoomInv * float32(y)
|
||||||
|
@ -32,4 +32,5 @@ func (s *Settings) Store() error {
|
|||||||
type WindowSettings struct {
|
type WindowSettings struct {
|
||||||
Location *Point
|
Location *Point
|
||||||
Size *Point
|
Size *Point
|
||||||
|
VSync *bool
|
||||||
}
|
}
|
||||||
|
@ -7,21 +7,39 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type terrainRenderer struct {
|
type terrainRenderer struct {
|
||||||
|
game *Game
|
||||||
terrain *Map
|
terrain *Map
|
||||||
hover *Point
|
hover *Point
|
||||||
project projection
|
project projection
|
||||||
|
|
||||||
interact interaction
|
drag Drageable
|
||||||
}
|
}
|
||||||
|
|
||||||
type interaction struct {
|
type Drageable struct {
|
||||||
mousePos Point
|
start *Point
|
||||||
mouseLeftDown bool
|
dragged bool
|
||||||
mouseDrag *Point
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTerrainRenderer(terrain *Map) Control {
|
func (d *Drageable) Cancel() { d.start = nil }
|
||||||
return &terrainRenderer{terrain: terrain, project: newProjection()}
|
|
||||||
|
func (d *Drageable) IsDragging() bool { return d.start != nil }
|
||||||
|
|
||||||
|
func (d *Drageable) HasDragged() bool { return d.dragged }
|
||||||
|
|
||||||
|
func (d *Drageable) Move(p Point) Point {
|
||||||
|
delta := p.Sub(*d.start)
|
||||||
|
d.start = &p
|
||||||
|
d.dragged = true
|
||||||
|
return delta
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Drageable) Start(p Point) {
|
||||||
|
d.start = &p
|
||||||
|
d.dragged = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTerrainRenderer(game *Game) Control {
|
||||||
|
return &terrainRenderer{game: game, terrain: game.Terrain, project: newProjection()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *terrainRenderer) Arrange(ctx *Context, _ Rectangle) {
|
func (r *terrainRenderer) Arrange(ctx *Context, _ Rectangle) {
|
||||||
@ -33,30 +51,48 @@ func (r *terrainRenderer) Init(ctx *Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isControlKeyDown() bool {
|
||||||
|
state := sdl.GetKeyboardState()
|
||||||
|
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) {
|
||||||
switch e := event.(type) {
|
switch e := event.(type) {
|
||||||
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.Button == sdl.BUTTON_MIDDLE || (e.Button == sdl.BUTTON_LEFT && isControlKeyDown()) {
|
||||||
|
if !r.drag.IsDragging() {
|
||||||
|
r.drag.Start(Pt(e.X, e.Y))
|
||||||
|
}
|
||||||
|
}
|
||||||
if e.Button == sdl.BUTTON_LEFT {
|
if e.Button == sdl.BUTTON_LEFT {
|
||||||
r.interact.mouseLeftDown = e.Type == sdl.MOUSEBUTTONDOWN
|
pos := r.project.screenToMapInt(e.X, e.Y)
|
||||||
if r.interact.mouseLeftDown && r.interact.mouseDrag == nil {
|
r.game.UserClickedTile(pos)
|
||||||
r.interact.mouseDrag = PtPtr(e.X, e.Y)
|
}
|
||||||
} else if !r.interact.mouseLeftDown && r.interact.mouseDrag != nil {
|
if e.Button == sdl.BUTTON_RIGHT {
|
||||||
r.interact.mouseDrag = nil
|
if e.Type == sdl.MOUSEBUTTONDOWN {
|
||||||
|
r.game.CancelTool()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if e.Type == sdl.MOUSEBUTTONUP {
|
||||||
|
if r.drag.IsDragging() {
|
||||||
|
r.drag.Cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *sdl.MouseMotionEvent:
|
case *sdl.MouseMotionEvent:
|
||||||
if r.project.windowInteractRect.IsPointInside(e.X, e.Y) {
|
if r.project.windowInteractRect.IsPointInside(e.X, e.Y) {
|
||||||
hover := r.project.screenToMap(e.X, e.Y)
|
hover := r.project.screenToMapInt(e.X, e.Y)
|
||||||
r.hover = PtPtr(int32(Round32(hover.X)), int32(Round32(hover.Y)))
|
r.hover = &hover
|
||||||
} else {
|
} else {
|
||||||
r.hover = nil
|
r.hover = nil
|
||||||
}
|
}
|
||||||
if r.interact.mouseDrag != nil {
|
if r.drag.IsDragging() {
|
||||||
r.project.center = r.project.center.Sub(r.project.screenToMapRel(e.X-r.interact.mouseDrag.X, e.Y-r.interact.mouseDrag.Y))
|
delta := r.drag.Move(Pt(e.X, e.Y))
|
||||||
|
r.project.center = r.project.center.Sub(r.project.screenToMapRel(delta.X, delta.Y))
|
||||||
r.project.update(ctx.Renderer)
|
r.project.update(ctx.Renderer)
|
||||||
r.interact.mouseDrag = PtPtr(e.X, e.Y)
|
|
||||||
}
|
}
|
||||||
case *sdl.MouseWheelEvent:
|
case *sdl.MouseWheelEvent:
|
||||||
if r.hover != nil {
|
if r.hover != nil {
|
||||||
@ -143,9 +179,10 @@ func (r *terrainRenderer) Render(ctx *Context) {
|
|||||||
|
|
||||||
toItemTexture := func(x, y int32) *Texture {
|
toItemTexture := func(x, y int32) *Texture {
|
||||||
variant := r.terrain.Variant.Value(x, y)
|
variant := r.terrain.Variant.Value(x, y)
|
||||||
_, ok := r.terrain.Flowers[Pt(x, y)]
|
flower, ok := r.terrain.Flowers[Pt(x, y)]
|
||||||
if ok {
|
if ok {
|
||||||
return variantToTexture("flower-poppy-%d", variant)
|
desc, _ := r.game.Herbarium.Find(flower.ID)
|
||||||
|
return ctx.Textures.Texture(desc.IconTemplate.Variant(variantToInt(variant)))
|
||||||
}
|
}
|
||||||
temp := r.terrain.Temp.Value(x, y)
|
temp := r.terrain.Temp.Value(x, y)
|
||||||
humid := r.terrain.Humid.Value(x, y)
|
humid := r.terrain.Humid.Value(x, y)
|
||||||
|
24
tools.go
Normal file
24
tools.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package tins2020
|
||||||
|
|
||||||
|
type Tool interface {
|
||||||
|
Type() string
|
||||||
|
ClickedTile(*Game, Point)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlantFlowerTool struct {
|
||||||
|
FlowerID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *PlantFlowerTool) Type() string { return "plant-flower" }
|
||||||
|
|
||||||
|
func (t *PlantFlowerTool) ClickedTile(game *Game, tile Point) {
|
||||||
|
game.PlantFlower(t.FlowerID, tile)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShovelTool struct{}
|
||||||
|
|
||||||
|
func (t *ShovelTool) Type() string { return "shovel" }
|
||||||
|
|
||||||
|
func (t *ShovelTool) ClickedTile(game *Game, tile Point) {
|
||||||
|
game.Dig(tile)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user