2020-05-08 16:38:26 +00:00
|
|
|
package tins2020
|
|
|
|
|
|
|
|
import (
|
2020-05-10 18:44:20 +00:00
|
|
|
"log"
|
2020-05-08 16:38:26 +00:00
|
|
|
"math/rand"
|
2020-05-09 17:34:43 +00:00
|
|
|
"time"
|
2020-05-08 16:38:26 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Game struct {
|
2020-05-10 18:44:20 +00:00
|
|
|
Balance int
|
|
|
|
Speed GameSpeed
|
|
|
|
SpeedBeforePause GameSpeed
|
|
|
|
Herbarium Herbarium
|
|
|
|
Terrain *Map
|
|
|
|
|
2020-05-10 21:00:19 +00:00
|
|
|
tool Tool
|
|
|
|
toolChanged *Events
|
|
|
|
speedChanged *Events
|
|
|
|
simulation Animation
|
2020-05-08 16:38:26 +00:00
|
|
|
}
|
|
|
|
|
2020-05-10 15:16:18 +00:00
|
|
|
type GameSpeed string
|
|
|
|
|
|
|
|
const (
|
|
|
|
GameSpeedNormal GameSpeed = "normal"
|
|
|
|
GameSpeedFast = "fast"
|
|
|
|
GameSpeedPaused = "paused"
|
|
|
|
)
|
|
|
|
|
|
|
|
const simulationInterval = 120 * time.Millisecond
|
|
|
|
const fastSimulationInterval = 40 * time.Millisecond
|
|
|
|
|
2020-05-08 16:38:26 +00:00
|
|
|
func NewGame() *Game {
|
|
|
|
terrain := &Map{
|
2020-05-09 11:55:37 +00:00
|
|
|
Temp: NewNoiseMap(rand.Int63()),
|
|
|
|
Humid: NewNoiseMap(rand.Int63()),
|
|
|
|
Variant: NewRandomNoiseMap(rand.Int63()),
|
|
|
|
PlaceX: NewRandomNoiseMap(rand.Int63()),
|
|
|
|
PlaceY: NewRandomNoiseMap(rand.Int63()),
|
2020-05-09 17:34:43 +00:00
|
|
|
Flowers: map[Point]Flower{},
|
2020-05-08 16:38:26 +00:00
|
|
|
}
|
2020-05-10 18:44:20 +00:00
|
|
|
herbarium := NewHerbarium()
|
2020-05-10 21:00:19 +00:00
|
|
|
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,
|
2020-05-10 22:46:26 +00:00
|
|
|
Traits: NewAnemoneTraits(),
|
2020-05-10 18:44:20 +00:00
|
|
|
Unlocked: true,
|
|
|
|
})
|
2020-05-10 22:00:35 +00:00
|
|
|
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",
|
2020-05-10 22:46:26 +00:00
|
|
|
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",
|
2020-05-10 22:00:35 +00:00
|
|
|
BuyPrice: 20000,
|
|
|
|
SellPrice: 5000,
|
2020-05-10 22:46:26 +00:00
|
|
|
Traits: NewTulipTraits(),
|
2020-05-10 22:00:35 +00:00
|
|
|
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,
|
|
|
|
})
|
2020-05-08 16:38:26 +00:00
|
|
|
return &Game{
|
2020-05-10 18:44:20 +00:00
|
|
|
Speed: GameSpeedNormal,
|
|
|
|
Balance: 100,
|
|
|
|
Terrain: terrain,
|
|
|
|
Herbarium: herbarium,
|
2020-05-09 17:34:43 +00:00
|
|
|
|
2020-05-10 21:00:19 +00:00
|
|
|
speedChanged: NewEvents(),
|
|
|
|
toolChanged: NewEvents(),
|
|
|
|
simulation: NewAnimation(time.Millisecond * 10),
|
2020-05-09 17:34:43 +00:00
|
|
|
}
|
2020-05-08 16:38:26 +00:00
|
|
|
}
|
|
|
|
|
2020-05-10 18:44:20 +00:00
|
|
|
func (g *Game) selectTool(t Tool) {
|
|
|
|
g.tool = t
|
|
|
|
g.toolChanged.Notify(t)
|
|
|
|
}
|
|
|
|
|
2020-05-10 21:00:19 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-09 17:34:43 +00:00
|
|
|
func (g *Game) tick() {
|
|
|
|
randomNeighbor := func(pos Point) Point {
|
|
|
|
switch rand.Intn(4) {
|
|
|
|
case 0:
|
|
|
|
return Pt(pos.X-1, pos.Y)
|
|
|
|
case 1:
|
|
|
|
return Pt(pos.X, pos.Y-1)
|
|
|
|
case 2:
|
|
|
|
return Pt(pos.X+1, pos.Y)
|
|
|
|
case 3:
|
|
|
|
return Pt(pos.X, pos.Y+1)
|
|
|
|
}
|
|
|
|
return pos
|
|
|
|
}
|
|
|
|
flowers := map[Point]Flower{}
|
|
|
|
for pos, flower := range g.Terrain.Flowers {
|
|
|
|
if rand.Float32() < flower.Traits.Spread {
|
|
|
|
dst := randomNeighbor(pos)
|
|
|
|
if _, ok := g.Terrain.Flowers[dst]; !ok {
|
|
|
|
if _, ok := flowers[dst]; !ok {
|
2020-05-10 21:00:19 +00:00
|
|
|
flowers[dst] = g.Terrain.NewFlower(dst, flower.ID, flower.Traits)
|
2020-05-09 17:34:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-10 21:00:19 +00:00
|
|
|
if Sqr32(rand.Float32()) < flower.Traits.Life*flower.Traits.LifeModifier {
|
2020-05-09 17:34:43 +00:00
|
|
|
flowers[pos] = flower
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g.Terrain.Flowers = flowers
|
2020-05-08 16:38:26 +00:00
|
|
|
}
|
2020-05-10 15:16:18 +00:00
|
|
|
|
2020-05-10 18:44:20 +00:00
|
|
|
func (g *Game) CancelTool() {
|
|
|
|
g.selectTool(nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Game) Dig(tile Point) {
|
2020-05-10 21:00:19 +00:00
|
|
|
id := g.Terrain.DigFlower(tile)
|
|
|
|
desc, ok := g.Herbarium.Find(id)
|
|
|
|
if !ok {
|
2020-05-10 18:44:20 +00:00
|
|
|
return
|
|
|
|
}
|
2020-05-10 21:00:19 +00:00
|
|
|
g.Balance += desc.SellPrice
|
2020-05-10 15:16:18 +00:00
|
|
|
}
|
|
|
|
|
2020-05-10 21:00:19 +00:00
|
|
|
func (g *Game) Pause() { g.setSpeed(GameSpeedPaused) }
|
|
|
|
|
2020-05-10 18:44:20 +00:00
|
|
|
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
|
|
|
|
}
|
2020-05-10 21:00:19 +00:00
|
|
|
if flower.BuyPrice > g.Balance {
|
2020-05-10 18:44:20 +00:00
|
|
|
// TODO: notify user of insufficient balance?
|
|
|
|
return
|
|
|
|
}
|
2020-05-10 21:00:19 +00:00
|
|
|
g.Balance -= flower.BuyPrice
|
|
|
|
g.Terrain.AddFlower(tile, id, flower.Traits)
|
2020-05-10 18:44:20 +00:00
|
|
|
}
|
|
|
|
|
2020-05-10 21:00:19 +00:00
|
|
|
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})
|
2020-05-10 18:44:20 +00:00
|
|
|
}
|
|
|
|
|
2020-05-10 21:00:19 +00:00
|
|
|
func (g *Game) SelectShovel() {
|
|
|
|
g.selectTool(&ShovelTool{})
|
2020-05-10 15:16:18 +00:00
|
|
|
}
|
|
|
|
|
2020-05-10 21:00:19 +00:00
|
|
|
func (g *Game) SelectResearch() {
|
|
|
|
g.Pause()
|
2020-05-10 15:16:18 +00:00
|
|
|
}
|
|
|
|
|
2020-05-10 21:00:19 +00:00
|
|
|
func (g *Game) SpeedChanged() EventHandler { return g.speedChanged }
|
|
|
|
|
|
|
|
func (g *Game) TogglePause() {
|
|
|
|
if g.Speed == GameSpeedPaused {
|
|
|
|
g.Resume()
|
|
|
|
} else {
|
|
|
|
g.Pause()
|
|
|
|
}
|
2020-05-10 18:44:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *Game) Tool() Tool { return g.tool }
|
|
|
|
|
|
|
|
func (g *Game) ToolChanged() EventHandler { return g.toolChanged }
|
|
|
|
|
2020-05-10 15:16:18 +00:00
|
|
|
func (g *Game) Update() {
|
|
|
|
for g.simulation.Animate() {
|
|
|
|
g.tick()
|
|
|
|
}
|
|
|
|
}
|
2020-05-10 18:44:20 +00:00
|
|
|
|
|
|
|
func (g *Game) UserClickedTile(pos Point) {
|
|
|
|
if g.tool == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
g.tool.ClickedTile(g, pos)
|
|
|
|
}
|