Compare commits
4 Commits
3a8ad733a4
...
a72f416650
Author | SHA1 | Date | |
---|---|---|---|
a72f416650 | |||
6a7843f314 | |||
0cb57c5940 | |||
e26de92f1d |
20
buttonbar.go
20
buttonbar.go
@ -5,9 +5,10 @@ import "github.com/veandco/go-sdl2/sdl"
|
|||||||
type ButtonBar struct {
|
type ButtonBar struct {
|
||||||
Container
|
Container
|
||||||
|
|
||||||
Background sdl.Color
|
Background sdl.Color
|
||||||
Orientation Orientation
|
ButtonLength int32
|
||||||
Buttons []Control
|
Orientation Orientation
|
||||||
|
Buttons []Control
|
||||||
}
|
}
|
||||||
|
|
||||||
const buttonBarWidth = 96
|
const buttonBarWidth = 96
|
||||||
@ -21,19 +22,24 @@ func (b *ButtonBar) Init(ctx *Context) error {
|
|||||||
|
|
||||||
func (b *ButtonBar) Arrange(ctx *Context, bounds Rectangle) {
|
func (b *ButtonBar) Arrange(ctx *Context, bounds Rectangle) {
|
||||||
b.Container.Arrange(ctx, bounds)
|
b.Container.Arrange(ctx, bounds)
|
||||||
|
length := b.ButtonLength
|
||||||
switch b.Orientation {
|
switch b.Orientation {
|
||||||
case OrientationHorizontal:
|
case OrientationHorizontal:
|
||||||
length := bounds.H
|
if length == 0 {
|
||||||
|
length = bounds.H
|
||||||
|
}
|
||||||
offset := bounds.X
|
offset := bounds.X
|
||||||
for i := range b.Buttons {
|
for i := range b.Buttons {
|
||||||
b.Buttons[i].Arrange(ctx, RectSize(offset, bounds.Y, length, length))
|
b.Buttons[i].Arrange(ctx, RectSize(offset, bounds.Y, length, bounds.H))
|
||||||
offset += length
|
offset += length
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
length := bounds.W
|
if length == 0 {
|
||||||
|
length = bounds.W
|
||||||
|
}
|
||||||
offset := bounds.Y
|
offset := bounds.Y
|
||||||
for i := range b.Buttons {
|
for i := range b.Buttons {
|
||||||
b.Buttons[i].Arrange(ctx, RectSize(bounds.X, offset, length, length))
|
b.Buttons[i].Arrange(ctx, RectSize(bounds.X, offset, bounds.W, length))
|
||||||
offset += length
|
offset += length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,29 +38,39 @@ func (b *BuyFlowerButton) animate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *BuyFlowerButton) fmtTooltipText() string {
|
func (b *BuyFlowerButton) fmtTooltipText() string {
|
||||||
if b.Flower.Unlocked {
|
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, "Traits are not known yet.")
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s - %s - %s", FmtMoney(b.Flower.BuyPrice), b.Flower.Name, b.Flower.Description)
|
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) updateTexts(ctx *Context) error {
|
||||||
text := fmt.Sprintf("%s - %s - %s", FmtMoney(b.Flower.BuyPrice), b.Flower.Name, b.Flower.Description)
|
text := b.fmtTooltipText()
|
||||||
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)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if b.hoverTexture != nil {
|
||||||
|
b.hoverTexture.Destroy()
|
||||||
|
}
|
||||||
b.hoverTexture = texture
|
b.hoverTexture = texture
|
||||||
texture, err = font.Render(ctx.Renderer, FmtMoney(b.Flower.BuyPrice), color)
|
texture, err = font.Render(ctx.Renderer, FmtMoney(b.Flower.BuyPrice), color)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if b.priceTexture != nil {
|
||||||
|
b.priceTexture.Destroy()
|
||||||
|
}
|
||||||
b.priceTexture = texture
|
b.priceTexture = texture
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BuyFlowerButton) Init(ctx *Context) error {
|
||||||
|
return b.updateTexts(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *BuyFlowerButton) Handle(ctx *Context, event sdl.Event) {
|
func (b *BuyFlowerButton) Handle(ctx *Context, event sdl.Event) {
|
||||||
b.IconButton.Handle(ctx, event)
|
b.IconButton.Handle(ctx, event)
|
||||||
if b.IsMouseOver && b.hoverAnimation == nil {
|
if b.IsMouseOver && b.hoverAnimation == nil {
|
||||||
@ -76,9 +86,9 @@ func (b *BuyFlowerButton) Render(ctx *Context) {
|
|||||||
mouseOverTexture := ctx.Textures.Texture("control-hover")
|
mouseOverTexture := ctx.Textures.Texture("control-hover")
|
||||||
|
|
||||||
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-60, b.Bounds.W, 120))
|
||||||
if (b.IsMouseOver && !b.IsDisabled) || b.IsActive {
|
if (b.IsMouseOver && !b.IsDisabled) || b.IsActive {
|
||||||
mouseOverTexture.Copy(ctx.Renderer, pos)
|
mouseOverTexture.CopyResize(ctx.Renderer, b.Bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.hoverAnimation != nil {
|
if b.hoverAnimation != nil {
|
||||||
@ -86,8 +96,8 @@ func (b *BuyFlowerButton) Render(ctx *Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if b.IsMouseOver {
|
if b.IsMouseOver {
|
||||||
left := buttonBarWidth - 8 - b.hoverOffset
|
left := b.Bounds.W - 8 - b.hoverOffset
|
||||||
top := pos.Y + buttonBarWidth - 20
|
top := pos.Y + b.Bounds.H - 20
|
||||||
if left < 0 {
|
if left < 0 {
|
||||||
part := Rect(-left, 0, b.hoverTexture.Size().X, b.hoverTexture.Size().Y)
|
part := Rect(-left, 0, b.hoverTexture.Size().X, b.hoverTexture.Size().Y)
|
||||||
b.hoverTexture.CopyPart(ctx.Renderer, part, Pt(pos.X, top))
|
b.hoverTexture.CopyPart(ctx.Renderer, part, Pt(pos.X, top))
|
||||||
@ -95,6 +105,11 @@ func (b *BuyFlowerButton) Render(ctx *Context) {
|
|||||||
b.hoverTexture.Copy(ctx.Renderer, Pt(pos.X+left, top))
|
b.hoverTexture.Copy(ctx.Renderer, Pt(pos.X+left, top))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
b.priceTexture.Copy(ctx.Renderer, Pt(pos.X+buttonBarWidth-8-b.priceTexture.Size().X, pos.Y+buttonBarWidth-20))
|
b.priceTexture.Copy(ctx.Renderer, Pt(pos.X+b.Bounds.W-8-b.priceTexture.Size().X, pos.Y+b.Bounds.H-20))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BuyFlowerButton) Update(ctx *Context, desc FlowerDescriptor) {
|
||||||
|
b.Flower = desc
|
||||||
|
b.updateTexts(ctx)
|
||||||
|
}
|
||||||
|
@ -39,6 +39,10 @@ func (b *ControlBase) Handle(ctx *Context, event sdl.Event) {
|
|||||||
if b.IsMouseOver && e.Button == sdl.BUTTON_LEFT && e.Type == sdl.MOUSEBUTTONDOWN {
|
if b.IsMouseOver && e.Button == sdl.BUTTON_LEFT && e.Type == sdl.MOUSEBUTTONDOWN {
|
||||||
b.Invoke(ctx, b.OnLeftMouseButtonClick)
|
b.Invoke(ctx, b.OnLeftMouseButtonClick)
|
||||||
}
|
}
|
||||||
|
case *sdl.WindowEvent:
|
||||||
|
if e.Event == sdl.WINDOWEVENT_LEAVE {
|
||||||
|
b.IsMouseOver = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
24
drageable.go
Normal file
24
drageable.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package tins2020
|
||||||
|
|
||||||
|
type Drageable struct {
|
||||||
|
start *Point
|
||||||
|
dragged bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Drageable) Cancel() { d.start = nil }
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
136
game.go
136
game.go
@ -13,10 +13,11 @@ type Game struct {
|
|||||||
Herbarium Herbarium
|
Herbarium Herbarium
|
||||||
Terrain *Map
|
Terrain *Map
|
||||||
|
|
||||||
tool Tool
|
tool Tool
|
||||||
toolChanged *Events
|
centerChanged *Events
|
||||||
speedChanged *Events
|
toolChanged *Events
|
||||||
simulation Animation
|
speedChanged *Events
|
||||||
|
simulation Animation
|
||||||
}
|
}
|
||||||
|
|
||||||
type GameSpeed string
|
type GameSpeed string
|
||||||
@ -40,60 +41,16 @@ func NewGame() *Game {
|
|||||||
Flowers: map[Point]Flower{},
|
Flowers: map[Point]Flower{},
|
||||||
}
|
}
|
||||||
herbarium := NewHerbarium()
|
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,
|
Herbarium: herbarium,
|
||||||
|
|
||||||
speedChanged: NewEvents(),
|
centerChanged: NewEvents(),
|
||||||
toolChanged: NewEvents(),
|
speedChanged: NewEvents(),
|
||||||
simulation: NewAnimation(time.Millisecond * 10),
|
toolChanged: NewEvents(),
|
||||||
|
simulation: NewAnimation(time.Millisecond * 10),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,6 +116,8 @@ func (g *Game) CancelTool() {
|
|||||||
g.selectTool(nil)
|
g.selectTool(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Game) CenterChanged() EventHandler { return g.centerChanged }
|
||||||
|
|
||||||
func (g *Game) Dig(tile Point) {
|
func (g *Game) Dig(tile Point) {
|
||||||
id := g.Terrain.DigFlower(tile)
|
id := g.Terrain.DigFlower(tile)
|
||||||
desc, ok := g.Herbarium.Find(id)
|
desc, ok := g.Herbarium.Find(id)
|
||||||
@ -168,6 +127,44 @@ func (g *Game) Dig(tile Point) {
|
|||||||
g.Balance += desc.SellPrice
|
g.Balance += desc.SellPrice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Game) Load() {
|
||||||
|
g.CancelTool()
|
||||||
|
g.Pause()
|
||||||
|
|
||||||
|
var state GameState
|
||||||
|
err := state.Deserialize(SaveGameName())
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to load; error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Herbarium = NewHerbarium()
|
||||||
|
for _, flower := range state.Herbarium.Flowers {
|
||||||
|
g.Herbarium.Update(flower.ID, func(desc *FlowerDescriptor) {
|
||||||
|
desc.Unlocked = flower.Unlocked
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Balance = state.Balance
|
||||||
|
g.Terrain = &Map{
|
||||||
|
Temp: NewNoiseMap(state.Terrain.Temperature),
|
||||||
|
Humid: NewNoiseMap(state.Terrain.Humidity),
|
||||||
|
Variant: NewRandomNoiseMap(state.Terrain.Variant),
|
||||||
|
PlaceX: NewRandomNoiseMap(state.Terrain.PlaceX),
|
||||||
|
PlaceY: NewRandomNoiseMap(state.Terrain.PlaceY),
|
||||||
|
Flowers: map[Point]Flower{},
|
||||||
|
}
|
||||||
|
for _, flower := range state.Terrain.Flowers {
|
||||||
|
desc, _ := g.Herbarium.Find(flower.ID)
|
||||||
|
g.Terrain.AddFlower(flower.Location, flower.ID, desc.Traits)
|
||||||
|
}
|
||||||
|
g.Terrain.Center = state.View.Center
|
||||||
|
g.centerChanged.Notify(g.Terrain.Center)
|
||||||
|
|
||||||
|
g.CancelTool()
|
||||||
|
g.setSpeed(state.Speed)
|
||||||
|
}
|
||||||
|
|
||||||
func (g *Game) Pause() { g.setSpeed(GameSpeedPaused) }
|
func (g *Game) Pause() { g.setSpeed(GameSpeedPaused) }
|
||||||
|
|
||||||
func (g *Game) PlantFlower(id string, tile Point) {
|
func (g *Game) PlantFlower(id string, tile Point) {
|
||||||
@ -190,6 +187,14 @@ func (g *Game) Run() { g.setSpeed(GameSpeedNormal) }
|
|||||||
|
|
||||||
func (g *Game) RunFast() { g.setSpeed(GameSpeedFast) }
|
func (g *Game) RunFast() { g.setSpeed(GameSpeedFast) }
|
||||||
|
|
||||||
|
func (g *Game) Save() {
|
||||||
|
state := g.State()
|
||||||
|
err := state.Serialize(SaveGameName())
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to save; error:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (g *Game) SelectPlantFlowerTool(id string) {
|
func (g *Game) SelectPlantFlowerTool(id string) {
|
||||||
g.selectTool(&PlantFlowerTool{FlowerID: id})
|
g.selectTool(&PlantFlowerTool{FlowerID: id})
|
||||||
}
|
}
|
||||||
@ -204,6 +209,33 @@ func (g *Game) SelectResearch() {
|
|||||||
|
|
||||||
func (g *Game) SpeedChanged() EventHandler { return g.speedChanged }
|
func (g *Game) SpeedChanged() EventHandler { return g.speedChanged }
|
||||||
|
|
||||||
|
func (g *Game) State() GameState {
|
||||||
|
var state GameState
|
||||||
|
state.Balance = g.Balance
|
||||||
|
state.Speed = g.Speed
|
||||||
|
for _, id := range g.Herbarium.Flowers() {
|
||||||
|
flower, _ := g.Herbarium.Find(id)
|
||||||
|
state.Herbarium.Flowers = append(state.Herbarium.Flowers, HerbariumFlowerState{
|
||||||
|
ID: id,
|
||||||
|
Unlocked: flower.Unlocked,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
flowers := make([]FlowerState, 0, len(g.Terrain.Flowers))
|
||||||
|
for pos, flower := range g.Terrain.Flowers {
|
||||||
|
flowers = append(flowers, FlowerState{ID: flower.ID, Location: pos})
|
||||||
|
}
|
||||||
|
state.Terrain = TerrainState{
|
||||||
|
Temperature: g.Terrain.Temp.Seed(),
|
||||||
|
Humidity: g.Terrain.Humid.Seed(),
|
||||||
|
Variant: g.Terrain.Variant.Seed(),
|
||||||
|
PlaceX: g.Terrain.PlaceX.Seed(),
|
||||||
|
PlaceY: g.Terrain.PlaceY.Seed(),
|
||||||
|
Flowers: flowers,
|
||||||
|
}
|
||||||
|
state.View.Center = g.Terrain.Center
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
func (g *Game) TogglePause() {
|
func (g *Game) TogglePause() {
|
||||||
if g.Speed == GameSpeedPaused {
|
if g.Speed == GameSpeedPaused {
|
||||||
g.Resume()
|
g.Resume()
|
||||||
|
@ -58,6 +58,7 @@ func (c *GameControls) toolChanged(state interface{}) {
|
|||||||
for _, control := range c.flowers.Buttons {
|
for _, control := range c.flowers.Buttons {
|
||||||
button := control.(*BuyFlowerButton)
|
button := control.(*BuyFlowerButton)
|
||||||
button.IsActive = button.FlowerID == flowerID
|
button.IsActive = button.FlowerID == flowerID
|
||||||
|
button.IsDisabled = !c.game.Herbarium.IsUnlocked(button.FlowerID)
|
||||||
}
|
}
|
||||||
_, shovel := tool.(*ShovelTool)
|
_, shovel := tool.(*ShovelTool)
|
||||||
c.shovel.IsActive = shovel
|
c.shovel.IsActive = shovel
|
||||||
@ -75,7 +76,8 @@ func (c *GameControls) Init(ctx *Context) error {
|
|||||||
c.game.SpeedChanged().RegisterItf(c.speedChanged)
|
c.game.SpeedChanged().RegisterItf(c.speedChanged)
|
||||||
c.game.ToolChanged().RegisterItf(c.toolChanged)
|
c.game.ToolChanged().RegisterItf(c.toolChanged)
|
||||||
|
|
||||||
c.flowers.Background = MustHexColor("#356dad") // brown alternative? #4ac69a
|
c.flowers.Background = MustHexColor("#356dad")
|
||||||
|
c.flowers.ButtonLength = 64
|
||||||
|
|
||||||
for _, id := range c.game.Herbarium.Flowers() {
|
for _, id := range c.game.Herbarium.Flowers() {
|
||||||
c.flowers.Buttons = append(c.flowers.Buttons, c.createBuyFlowerButton(id))
|
c.flowers.Buttons = append(c.flowers.Buttons, c.createBuyFlowerButton(id))
|
||||||
@ -102,13 +104,22 @@ func (c *GameControls) Init(ctx *Context) error {
|
|||||||
|
|
||||||
c.menu.Background = MustHexColor("#356dad")
|
c.menu.Background = MustHexColor("#356dad")
|
||||||
c.menu.Buttons = []Control{
|
c.menu.Buttons = []Control{
|
||||||
NewIconButton("control-settings", EmptyEvent(func() {})),
|
NewIconButton("control-settings", func(*Context) {}),
|
||||||
NewIconButton("control-save", EmptyEvent(func() {})),
|
NewIconButton("control-save", func(*Context) { c.game.Save() }),
|
||||||
NewIconButton("control-load", EmptyEvent(func() {})),
|
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.shovel = NewIconButtonConfig("control-shovel", func(*Context) { c.game.SelectShovel() }, func(b *IconButton) { b.IconHeight = 48 })
|
c.shovel = NewIconButtonConfig("control-shovel", func(*Context) { c.game.SelectShovel() }, func(b *IconButton) { b.IconHeight = 32 })
|
||||||
c.research = NewIconButtonConfig("control-research", func(*Context) { c.game.SelectResearch() }, func(b *IconButton) { b.IconHeight = 48 })
|
c.research = NewIconButtonConfig("control-research", func(*Context) { c.game.SelectResearch() }, func(b *IconButton) { b.IconHeight = 32 })
|
||||||
c.otherTools.Buttons = []Control{c.shovel, c.research}
|
c.otherTools.Buttons = []Control{c.shovel, c.research}
|
||||||
|
|
||||||
c.Container.AddChild(&c.menu)
|
c.Container.AddChild(&c.menu)
|
||||||
|
54
gamestate.go
Normal file
54
gamestate.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package tins2020
|
||||||
|
|
||||||
|
type FlowerState struct {
|
||||||
|
ID string
|
||||||
|
Location Point
|
||||||
|
}
|
||||||
|
|
||||||
|
type GameState struct {
|
||||||
|
Balance int
|
||||||
|
Speed GameSpeed
|
||||||
|
Herbarium HerbariumState
|
||||||
|
Terrain TerrainState
|
||||||
|
View ViewState
|
||||||
|
}
|
||||||
|
|
||||||
|
type HerbariumState struct {
|
||||||
|
Flowers []HerbariumFlowerState
|
||||||
|
}
|
||||||
|
|
||||||
|
type HerbariumFlowerState struct {
|
||||||
|
ID string
|
||||||
|
Unlocked bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type TerrainState struct {
|
||||||
|
Temperature int64
|
||||||
|
Humidity int64
|
||||||
|
Variant int64
|
||||||
|
PlaceX int64
|
||||||
|
PlaceY int64
|
||||||
|
Flowers []FlowerState
|
||||||
|
}
|
||||||
|
|
||||||
|
type ViewState struct {
|
||||||
|
Center Point
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GameState) Serialize(name string) error {
|
||||||
|
path, err := UserFile(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return EncodeJSON(path, &s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GameState) Deserialize(name string) error {
|
||||||
|
path, err := UserFile(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return DecodeJSON(path, &s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveGameName() string { return "savegame.json" }
|
72
herbarium.go
72
herbarium.go
@ -11,7 +11,69 @@ type Herbarium struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewHerbarium() Herbarium {
|
func NewHerbarium() Herbarium {
|
||||||
return Herbarium{map[string]FlowerDescriptor{}, nil}
|
h := Herbarium{map[string]FlowerDescriptor{}, nil}
|
||||||
|
h.Reset()
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Herbarium) Reset() {
|
||||||
|
h.flowers = map[string]FlowerDescriptor{}
|
||||||
|
h.order = nil
|
||||||
|
|
||||||
|
h.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,
|
||||||
|
})
|
||||||
|
h.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: true,
|
||||||
|
})
|
||||||
|
h.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: true,
|
||||||
|
})
|
||||||
|
h.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: true,
|
||||||
|
})
|
||||||
|
h.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: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Herbarium) Update(id string, update func(*FlowerDescriptor)) {
|
||||||
|
flower, ok := h.flowers[id]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
update(&flower)
|
||||||
|
h.flowers[id] = flower
|
||||||
}
|
}
|
||||||
|
|
||||||
type FlowerDescriptor struct {
|
type FlowerDescriptor struct {
|
||||||
@ -44,3 +106,11 @@ func (h *Herbarium) Find(id string) (FlowerDescriptor, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Herbarium) Flowers() []string { return h.order }
|
func (h *Herbarium) Flowers() []string { return h.order }
|
||||||
|
|
||||||
|
func (h *Herbarium) IsUnlocked(id string) bool {
|
||||||
|
flower, ok := h.flowers[id]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return flower.Unlocked
|
||||||
|
}
|
||||||
|
2
io.go
2
io.go
@ -33,7 +33,7 @@ func UserDir() (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
dir := filepath.Join(config, "tins2020")
|
dir := filepath.Join(config, "tins2020_flowers_sim")
|
||||||
err = os.MkdirAll(dir, 0777)
|
err = os.MkdirAll(dir, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
1
map.go
1
map.go
@ -7,6 +7,7 @@ type Map struct {
|
|||||||
PlaceX NoiseMap // displacement map of props
|
PlaceX NoiseMap // displacement map of props
|
||||||
PlaceY NoiseMap
|
PlaceY NoiseMap
|
||||||
|
|
||||||
|
Center Point
|
||||||
Flowers map[Point]Flower
|
Flowers map[Point]Flower
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
noisemap.go
35
noisemap.go
@ -2,7 +2,18 @@ package tins2020
|
|||||||
|
|
||||||
import "opslag.de/schobers/geom/noise"
|
import "opslag.de/schobers/geom/noise"
|
||||||
|
|
||||||
|
func clipNormalized(x float64) float64 {
|
||||||
|
if x < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if x > 1 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
type NoiseMap interface {
|
type NoiseMap interface {
|
||||||
|
Seed() int64
|
||||||
Value(x, y int32) float64
|
Value(x, y int32) float64
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,32 +26,24 @@ func NewNoiseMap(seed int64) NoiseMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRandomNoiseMap(seed int64) NoiseMap {
|
|
||||||
return &randomNoiseMap{noise.NewPerlin(seed)}
|
|
||||||
}
|
|
||||||
|
|
||||||
type noiseMap struct {
|
type noiseMap struct {
|
||||||
noise *noise.Perlin
|
noise *noise.Perlin
|
||||||
alpha, beta float64
|
alpha, beta float64
|
||||||
harmonics int
|
harmonics int
|
||||||
}
|
}
|
||||||
|
|
||||||
func clipNormalized(x float64) float64 {
|
|
||||||
if x < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if x > 1 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value generates the noise value for an x/y pair.
|
// Value generates the noise value for an x/y pair.
|
||||||
func (m *noiseMap) Value(x, y int32) float64 {
|
func (m noiseMap) Value(x, y int32) float64 {
|
||||||
value := m.noise.Noise2D(float64(x)*.01, float64(y)*.01, m.alpha, m.beta, m.harmonics)*.565 + .5
|
value := m.noise.Noise2D(float64(x)*.01, float64(y)*.01, m.alpha, m.beta, m.harmonics)*.565 + .5
|
||||||
return clipNormalized(value)
|
return clipNormalized(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m noiseMap) Seed() int64 { return m.noise.Seed }
|
||||||
|
|
||||||
|
func NewRandomNoiseMap(seed int64) NoiseMap {
|
||||||
|
return &randomNoiseMap{noise.NewPerlin(seed)}
|
||||||
|
}
|
||||||
|
|
||||||
type randomNoiseMap struct {
|
type randomNoiseMap struct {
|
||||||
*noise.Perlin
|
*noise.Perlin
|
||||||
}
|
}
|
||||||
@ -50,3 +53,5 @@ func (m randomNoiseMap) Value(x, y int32) float64 {
|
|||||||
value := m.Noise2D(float64(x)*.53, float64(y)*.53, 1.01, 2, 2)*.5 + .5
|
value := m.Noise2D(float64(x)*.53, float64(y)*.53, 1.01, 2, 2)*.5 + .5
|
||||||
return clipNormalized(value)
|
return clipNormalized(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m randomNoiseMap) Seed() int64 { return m.Perlin.Seed }
|
||||||
|
@ -6,6 +6,10 @@ import (
|
|||||||
"github.com/veandco/go-sdl2/sdl"
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func mapToTile(q PointF) Point {
|
||||||
|
return Pt(int32(Round32(q.X)), int32(Round32(q.Y)))
|
||||||
|
}
|
||||||
|
|
||||||
type projection struct {
|
type projection struct {
|
||||||
center PointF
|
center PointF
|
||||||
zoom float32
|
zoom float32
|
||||||
@ -41,7 +45,7 @@ func (p *projection) screenToMap(x, y int32) PointF {
|
|||||||
|
|
||||||
func (p *projection) screenToMapInt(x, y int32) Point {
|
func (p *projection) screenToMapInt(x, y int32) Point {
|
||||||
pos := p.screenToMap(x, y)
|
pos := p.screenToMap(x, y)
|
||||||
return Pt(int32(Round32(pos.X)), int32(Round32(pos.Y)))
|
return mapToTile(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *projection) screenToMapRel(x, y int32) PointF {
|
func (p *projection) screenToMapRel(x, y int32) PointF {
|
||||||
|
@ -8,38 +8,14 @@ import (
|
|||||||
|
|
||||||
type terrainRenderer struct {
|
type terrainRenderer struct {
|
||||||
game *Game
|
game *Game
|
||||||
terrain *Map
|
|
||||||
hover *Point
|
hover *Point
|
||||||
project projection
|
project projection
|
||||||
|
|
||||||
drag Drageable
|
drag Drageable
|
||||||
}
|
}
|
||||||
|
|
||||||
type Drageable struct {
|
|
||||||
start *Point
|
|
||||||
dragged bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Drageable) Cancel() { d.start = nil }
|
|
||||||
|
|
||||||
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 {
|
func NewTerrainRenderer(game *Game) Control {
|
||||||
return &terrainRenderer{game: game, terrain: game.Terrain, project: newProjection()}
|
return &terrainRenderer{game: game, project: newProjection()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *terrainRenderer) Arrange(ctx *Context, _ Rectangle) {
|
func (r *terrainRenderer) Arrange(ctx *Context, _ Rectangle) {
|
||||||
@ -47,6 +23,11 @@ func (r *terrainRenderer) Arrange(ctx *Context, _ Rectangle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *terrainRenderer) Init(ctx *Context) error {
|
func (r *terrainRenderer) Init(ctx *Context) error {
|
||||||
|
r.game.CenterChanged().RegisterItf(func(state interface{}) {
|
||||||
|
center := state.(Point)
|
||||||
|
r.project.center = center.ToPtF()
|
||||||
|
r.project.update(ctx.Renderer)
|
||||||
|
})
|
||||||
r.project.update(ctx.Renderer)
|
r.project.update(ctx.Renderer)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -78,6 +59,7 @@ func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) {
|
|||||||
}
|
}
|
||||||
if e.Type == sdl.MOUSEBUTTONUP {
|
if e.Type == sdl.MOUSEBUTTONUP {
|
||||||
if r.drag.IsDragging() {
|
if r.drag.IsDragging() {
|
||||||
|
r.game.Terrain.Center = mapToTile(r.project.center)
|
||||||
r.drag.Cancel()
|
r.drag.Cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,12 +91,18 @@ func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) {
|
|||||||
r.project.update(ctx.Renderer)
|
r.project.update(ctx.Renderer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case *sdl.WindowEvent:
|
||||||
|
if e.Event == sdl.WINDOWEVENT_LEAVE {
|
||||||
|
r.hover = nil
|
||||||
|
r.project.update(ctx.Renderer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *terrainRenderer) Render(ctx *Context) {
|
func (r *terrainRenderer) Render(ctx *Context) {
|
||||||
|
terrain := r.game.Terrain
|
||||||
toTileTexture := func(x, y int32) *Texture {
|
toTileTexture := func(x, y int32) *Texture {
|
||||||
temp := r.terrain.Temp.Value(x, y)
|
temp := terrain.Temp.Value(x, y)
|
||||||
if temp < .35 {
|
if temp < .35 {
|
||||||
return ctx.Textures.Texture("tile-snow")
|
return ctx.Textures.Texture("tile-snow")
|
||||||
}
|
}
|
||||||
@ -178,14 +166,14 @@ 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 := terrain.Variant.Value(x, y)
|
||||||
flower, ok := r.terrain.Flowers[Pt(x, y)]
|
flower, ok := terrain.Flowers[Pt(x, y)]
|
||||||
if ok {
|
if ok {
|
||||||
desc, _ := r.game.Herbarium.Find(flower.ID)
|
desc, _ := r.game.Herbarium.Find(flower.ID)
|
||||||
return ctx.Textures.Texture(desc.IconTemplate.Variant(variantToInt(variant)))
|
return ctx.Textures.Texture(desc.IconTemplate.Variant(variantToInt(variant)))
|
||||||
}
|
}
|
||||||
temp := r.terrain.Temp.Value(x, y)
|
temp := terrain.Temp.Value(x, y)
|
||||||
humid := r.terrain.Humid.Value(x, y)
|
humid := terrain.Humid.Value(x, y)
|
||||||
return toPropTexture(temp, humid, variant)
|
return toPropTexture(temp, humid, variant)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,16 +197,8 @@ func (r *terrainRenderer) Render(ctx *Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
placeX, placeY := r.terrain.PlaceX.Value(x, y), r.terrain.PlaceY.Value(x, y)
|
placeX, placeY := terrain.PlaceX.Value(x, y), terrain.PlaceY.Value(x, y)
|
||||||
pos = r.project.mapToScreenF(float32(x)-.2+float32(.9*placeX-.45), float32(y)-.2+float32(.9*placeY-.45))
|
pos = r.project.mapToScreenF(float32(x)-.2+float32(.9*placeX-.45), float32(y)-.2+float32(.9*placeY-.45))
|
||||||
text.CopyResize(ctx.Renderer, r.project.screenToTileRect(pos))
|
text.CopyResize(ctx.Renderer, r.project.screenToTileRect(pos))
|
||||||
})
|
})
|
||||||
|
|
||||||
// gfx.RectangleColor(ctx.Renderer, r.project.windowRect.X, r.project.windowRect.Y, r.project.windowRect.X+r.project.windowRect.W, r.project.windowRect.Y+r.project.windowRect.H, sdl.Color{R: 255, A: 255})
|
|
||||||
// for y := int32(-40); y < 40; y++ {
|
|
||||||
// for x := int32(-40); x < 40; x++ {
|
|
||||||
// pos := r.project.mapToScreen(x, y)
|
|
||||||
// ctx.Fonts.Font("debug").RenderCopy(ctx.Renderer, fmt.Sprintf("(%d, %d)", x, y), pos, sdl.Color{R: 255, A: 255})
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user