From 1c208152e8b7432288919861c802bf67f19d3d03 Mon Sep 17 00:00:00 2001 From: Sander Schobers Date: Sun, 10 May 2020 23:00:19 +0200 Subject: [PATCH] Implemented digging of flowers. Renamed poppy to anemone. --- buyflowerbutton.go | 31 +++-- .../res/images/{basket.png => import.png} | Bin 15216 -> 15090 bytes cmd/tins2020/res/textures.txt | 12 +- flower.go | 10 +- game.go | 131 +++++++++--------- gamecontrols.go | 80 ++++++++--- herbarium.go | 8 +- iconbutton.go | 6 +- map.go | 34 +++++ math.go | 2 + terrainrenderer.go | 2 +- 11 files changed, 201 insertions(+), 115 deletions(-) rename cmd/tins2020/res/images/{basket.png => import.png} (92%) create mode 100644 map.go diff --git a/buyflowerbutton.go b/buyflowerbutton.go index aec86a5..76b8fad 100644 --- a/buyflowerbutton.go +++ b/buyflowerbutton.go @@ -10,12 +10,8 @@ import ( type BuyFlowerButton struct { IconButton - FlowerID string - Name string - Price int - Description string - - IsActive bool + FlowerID string + Flower FlowerDescriptor hoverAnimation *Animation hoverOffset int32 @@ -23,28 +19,33 @@ type BuyFlowerButton struct { priceTexture *Texture } -func NewBuyFlowerButton(icon, iconDisabled, flowerID, name string, price int, description string, isDisabled bool, onClick EventContextFn) *BuyFlowerButton { +func NewBuyFlowerButton(icon, iconDisabled, flowerID string, flower FlowerDescriptor, onClick EventContextFn) *BuyFlowerButton { return &BuyFlowerButton{ IconButton: *NewIconButtonConfig(icon, onClick, func(b *IconButton) { b.IconDisabled = iconDisabled - b.IsDisabled = isDisabled + b.IsDisabled = !flower.Unlocked }), - FlowerID: flowerID, - Name: name, - Price: price, - Description: description, + FlowerID: flowerID, + Flower: flower, } } func (b *BuyFlowerButton) animate() { b.hoverOffset++ 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 { - 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") color := MustHexColor("#ffffff") texture, err := font.Render(ctx.Renderer, text, color) @@ -52,7 +53,7 @@ func (b *BuyFlowerButton) Init(ctx *Context) error { return err } 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 { return err } diff --git a/cmd/tins2020/res/images/basket.png b/cmd/tins2020/res/images/import.png similarity index 92% rename from cmd/tins2020/res/images/basket.png rename to cmd/tins2020/res/images/import.png index 3750941da7c33d5ce69a5dbddd8e8891636d4f6a..a38af7cfccc02abc4505fb352764fa6d5d3a51a2 100644 GIT binary patch delta 415 zcmexR_NjEk0cJ+C$p@L887(HWvbX|CZx&mX6oW+5!TGq!YbHa47G%^Ct>8UdLKlfSZtBNQo2 zE@Hb%oZ;E*E{qnNH?S)iv1DbQT{rofrCvS54Nn)xkcwMxZam~|RuE`Q%(%09bIIbo zg1ZL-V-5=5dGPDF$=^ioFPX3LJHj!YZ^3JwhnJB5N5?^*iR zDb1yUfsv`DRAocnlvn9)EGHgJ2w$GQKW*;rRWpts4tcAs7}%qEJIp!Da9Q5=ZIABW zecsG>Hvjitj$o%Cl=6|M}09s=p_1zwYTo?f=KN+cn3Z zJAaBVWy5Cj?|DT!RHT$O< TK2z@lc?=Amu6{1-oD!Mv(yw56GIb2T{BZlOI=GNAj=}zz)065 z$tX27B`wX;z}R5&8Wu}73qv;xBd5u)S-RNFT}<7KT_#tvhCrCEK&HavudLw+MGBLP z*sc<1cs9EWqtWIK>`F!~`XMhoCSSAEt7i=Jba4!+xbzay|Q**owVT3i+YAd`*&~pA63rx zN#+#CN|SE~ygMZdgW}h@U98x??rHu8_x}M3dBKf;Ak4J&hiF z-T&OV^Xj4Ft}ca(*-NiPnKJLwl~k@(oe-?!;&<`Ia}jw?ENp2P7DPz|+;wWt~$(697q|+Z+G@ diff --git a/cmd/tins2020/res/textures.txt b/cmd/tins2020/res/textures.txt index da7e3bc..fb34beb 100644 --- a/cmd/tins2020/res/textures.txt +++ b/cmd/tins2020/res/textures.txt @@ -4,7 +4,7 @@ control-research: images/genericItem_color_111.png control-settings: images/gear.png control-save: images/save.png -control-load: images/basket.png +control-load: images/import.png control-quit: images/power.png control-pause: images/pause.png @@ -64,11 +64,11 @@ bush-large-2: images/plant_bushLarge_NW.png bush-large-3: images/plant_bushLarge_SW.png bush-large-4: images/plant_bushLarge_SE.png -flower-poppy-disabled: images/flower_yellowC_NE_disabled.png -flower-poppy-1: images/flower_yellowC_NE.png -flower-poppy-2: images/flower_yellowC_NW.png -flower-poppy-3: images/flower_yellowC_SW.png -flower-poppy-4: images/flower_yellowC_SE.png +flower-anemone-disabled: images/flower_yellowC_NE_disabled.png +flower-anemone-1: images/flower_yellowC_NE.png +flower-anemone-2: images/flower_yellowC_NW.png +flower-anemone-3: images/flower_yellowC_SW.png +flower-anemone-4: images/flower_yellowC_SE.png flower-red-c-disabled: images/flower_redC_NE_disabled.png flower-red-c-1: images/flower_redC_NE.png diff --git a/flower.go b/flower.go index e37edca..057f065 100644 --- a/flower.go +++ b/flower.go @@ -1,6 +1,7 @@ package tins2020 type Flower struct { + ID string Traits FlowerTraits } @@ -31,17 +32,16 @@ type FlowerResistance struct { Wet float32 } -// NewPoppyTraits creates the traits of a poppy, a very generic flower that thrives in a moderate climate. +// NewPoppyTraits creates the traits of a anemone, a very generic flower that thrives in a moderate climate. func NewPoppyTraits() FlowerTraits { return FlowerTraits{ - // Spread: 0.0011, - Spread: 0.0011, + Spread: 0.0004, Life: 0.99993, Resistance: FlowerResistance{ Cold: 0.5, - Hot: 0.5, + Hot: 0.7, Dry: 0.5, - Wet: 0.5, + Wet: 0.7, }, } } diff --git a/game.go b/game.go index 8487b07..f58bbb7 100644 --- a/game.go +++ b/game.go @@ -13,9 +13,10 @@ type Game struct { Herbarium Herbarium Terrain *Map - tool Tool - toolChanged *Events - simulation Animation + tool Tool + toolChanged *Events + speedChanged *Events + simulation Animation } type GameSpeed string @@ -26,29 +27,6 @@ const ( 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 fastSimulationInterval = 40 * time.Millisecond @@ -62,11 +40,12 @@ func NewGame() *Game { Flowers: map[Point]Flower{}, } herbarium := NewHerbarium() - herbarium.Add("poppy", FlowerDescriptor{ - Name: "Poppy", - Description: "A very generic flower that thrives in a moderate climate.", - IconTemplate: "flower-poppy-%s", - Price: 10, + 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: NewPoppyTraits(), Unlocked: true, }) @@ -76,8 +55,9 @@ func NewGame() *Game { Terrain: terrain, Herbarium: herbarium, - toolChanged: NewEvents(), - simulation: NewAnimation(time.Millisecond * 10), + speedChanged: NewEvents(), + toolChanged: NewEvents(), + simulation: NewAnimation(time.Millisecond * 10), } } @@ -86,6 +66,28 @@ func (g *Game) selectTool(t Tool) { 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() { randomNeighbor := func(pos Point) Point { switch rand.Intn(4) { @@ -106,11 +108,11 @@ func (g *Game) tick() { dst := randomNeighbor(pos) if _, ok := g.Terrain.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 } } @@ -122,57 +124,58 @@ func (g *Game) CancelTool() { } func (g *Game) Dig(tile Point) { - // TODO: implement -} - -func (g *Game) Pause() { - if g.Speed == GameSpeedPaused { + id := g.Terrain.DigFlower(tile) + desc, ok := g.Herbarium.Find(id) + if !ok { return } - g.SpeedBeforePause = g.Speed - g.Speed = GameSpeedPaused - g.simulation.Pause() + g.Balance += desc.SellPrice } +func (g *Game) Pause() { g.setSpeed(GameSpeedPaused) } + func (g *Game) PlantFlower(id string, tile Point) { flower, ok := g.Herbarium.Find(id) if !ok { log.Println("user was able to plant a flower that doesn't exist") return } - if flower.Price > g.Balance { + if flower.BuyPrice > g.Balance { // TODO: notify user of insufficient balance? return } - g.Balance -= flower.Price - g.Terrain.AddFlower(tile, flower.Traits) + g.Balance -= flower.BuyPrice + g.Terrain.AddFlower(tile, id, flower.Traits) } -func (g *Game) Resume() { - switch g.SpeedBeforePause { - case GameSpeedNormal: - g.Run() - case GameSpeedFast: - g.RunFast() - } -} +func (g *Game) Resume() { g.setSpeed(g.SpeedBeforePause) } -func (g *Game) Run() { - g.Speed = GameSpeedNormal - g.simulation.SetInterval(simulationInterval) - g.simulation.Run() -} +func (g *Game) Run() { g.setSpeed(GameSpeedNormal) } -func (g *Game) RunFast() { - g.Speed = GameSpeedFast - g.simulation.SetInterval(fastSimulationInterval) - g.simulation.Run() -} +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 } diff --git a/gamecontrols.go b/gamecontrols.go index 5675171..6c7d29f 100644 --- a/gamecontrols.go +++ b/gamecontrols.go @@ -1,17 +1,25 @@ package tins2020 +import ( + "github.com/veandco/go-sdl2/sdl" +) + type GameControls struct { Container game *Game - menu ButtonBar - top ButtonBar - flowers ButtonBar + menu ButtonBar + top ButtonBar + flowers ButtonBar + otherTools ButtonBar pause *IconButton run *IconButton runFast *IconButton + + shovel *IconButton + research *IconButton } func NewGameControls(game *Game) *GameControls { @@ -24,16 +32,23 @@ func (c *GameControls) createBuyFlowerButton(id string) *BuyFlowerButton { flower.IconTemplate.Variant(1), flower.IconTemplate.Disabled(), id, - flower.Name, - flower.Price, - flower.Description, - !flower.Unlocked, + 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.run, GameSpeedNormal) + disable(c.runFast, GameSpeedFast) +} + func (c *GameControls) toolChanged(state interface{}) { tool, _ := state.(Tool) var flowerID string @@ -44,15 +59,8 @@ func (c *GameControls) toolChanged(state interface{}) { button := control.(*BuyFlowerButton) button.IsActive = button.FlowerID == flowerID } -} - -func (c *GameControls) updateSpeedControls() { - disable := func(b *IconButton, speed GameSpeed) { - b.IsDisabled = speed == c.game.Speed - } - disable(c.pause, GameSpeedPaused) - disable(c.run, GameSpeedNormal) - disable(c.runFast, GameSpeedFast) + _, shovel := tool.(*ShovelTool) + c.shovel.IsActive = shovel } func (c *GameControls) Arrange(ctx *Context, bounds Rectangle) { @@ -60,9 +68,11 @@ func (c *GameControls) Arrange(ctx *Context, bounds Rectangle) { 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.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) Init(ctx *Context) error { + c.game.SpeedChanged().RegisterItf(c.speedChanged) c.game.ToolChanged().RegisterItf(c.toolChanged) c.flowers.Background = MustHexColor("#356dad") // brown alternative? #4ac69a @@ -74,23 +84,20 @@ func (c *GameControls) Init(ctx *Context) error { c.top.Orientation = OrientationHorizontal c.pause = NewIconButtonConfig("control-pause", EmptyEvent(func() { c.game.Pause() - c.updateSpeedControls() }), func(b *IconButton) { b.IconDisabled = "control-pause-disabled" }) c.run = NewIconButtonConfig("control-run", EmptyEvent(func() { c.game.Run() - c.updateSpeedControls() }), func(b *IconButton) { b.IconDisabled = "control-run-disabled" }) c.runFast = NewIconButtonConfig("control-run-fast", EmptyEvent(func() { c.game.RunFast() - c.updateSpeedControls() }), func(b *IconButton) { b.IconDisabled = "control-run-fast-disabled" }) - c.updateSpeedControls() + c.speedChanged(c.game.Speed) c.top.Buttons = []Control{c.pause, c.run, c.runFast} c.menu.Background = MustHexColor("#356dad") @@ -100,12 +107,45 @@ func (c *GameControls) Init(ctx *Context) error { 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.top) c.Container.AddChild(&c.flowers) + c.Container.AddChild(&c.otherTools) return c.Container.Init(ctx) } +func (c *GameControls) Handle(ctx *Context, event sdl.Event) { + c.Container.Handle(ctx, event) + + 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) { topBar := MustHexColor("#0000007f") ctx.Renderer.SetDrawColor(topBar.R, topBar.G, topBar.B, topBar.A) diff --git a/herbarium.go b/herbarium.go index 271ea22..f2807b2 100644 --- a/herbarium.go +++ b/herbarium.go @@ -15,9 +15,11 @@ func NewHerbarium() Herbarium { } type FlowerDescriptor struct { - Name string - Description string - Price int + Name string + Description string + BuyPrice int + SellPrice int + Unlocked bool IconTemplate IconTemplate Traits FlowerTraits diff --git a/iconbutton.go b/iconbutton.go index d6eea12..4c20bfd 100644 --- a/iconbutton.go +++ b/iconbutton.go @@ -5,9 +5,11 @@ type IconButton struct { Icon string IconDisabled string + IconHeight int32 IconScale Scale IconWidth int32 + IsActive bool IsDisabled bool } @@ -51,12 +53,14 @@ func (b *IconButton) Render(ctx *Context) { size := iconTexture.Size() if b.IconWidth != 0 { 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)) } else { iconTexture.CopyResize(ctx.Renderer, b.Bounds) } - if b.IsMouseOver && !b.IsDisabled { + if (b.IsMouseOver && !b.IsDisabled) || b.IsActive { mouseOverTexture.CopyResize(ctx.Renderer, b.Bounds) } } diff --git a/map.go b/map.go new file mode 100644 index 0000000..95cc068 --- /dev/null +++ b/map.go @@ -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 +} diff --git a/math.go b/math.go index fb059ee..d2a3c9d 100644 --- a/math.go +++ b/math.go @@ -50,4 +50,6 @@ func Min32(a, b float32) float32 { 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))) } diff --git a/terrainrenderer.go b/terrainrenderer.go index b232380..f5cd3ce 100644 --- a/terrainrenderer.go +++ b/terrainrenderer.go @@ -172,7 +172,7 @@ func (r *terrainRenderer) Render(ctx *Context) { variant := r.terrain.Variant.Value(x, y) _, ok := r.terrain.Flowers[Pt(x, y)] if ok { - return variantToTexture("flower-poppy-%d", variant) + return variantToTexture("flower-anemone-%d", variant) } temp := r.terrain.Temp.Value(x, y) humid := r.terrain.Humid.Value(x, y)