Sander Schobers
9641719579
Refactored event handling to be able to "handle" events so no other controls will handle the same event again.
265 lines
5.8 KiB
Go
265 lines
5.8 KiB
Go
package tins2020
|
|
|
|
import (
|
|
"log"
|
|
"math/rand"
|
|
"time"
|
|
)
|
|
|
|
type Game struct {
|
|
Debug bool
|
|
|
|
Balance int
|
|
Speed GameSpeed
|
|
SpeedBeforePause GameSpeed
|
|
Herbarium Herbarium
|
|
Terrain *Map
|
|
|
|
tool Tool
|
|
centerChanged *Events
|
|
toolChanged *Events
|
|
speedChanged *Events
|
|
simulation Animation
|
|
}
|
|
|
|
type GameSpeed string
|
|
|
|
const (
|
|
GameSpeedNormal GameSpeed = "normal"
|
|
GameSpeedFast = "fast"
|
|
GameSpeedPaused = "paused"
|
|
)
|
|
|
|
const simulationInterval = 120 * time.Millisecond
|
|
const fastSimulationInterval = 20 * time.Millisecond
|
|
|
|
func NewGame() *Game {
|
|
terrain := &Map{
|
|
Temp: NewNoiseMap(rand.Int63()),
|
|
Humid: NewNoiseMap(rand.Int63()),
|
|
Variant: NewRandomNoiseMap(rand.Int63()),
|
|
PlaceX: NewRandomNoiseMap(rand.Int63()),
|
|
PlaceY: NewRandomNoiseMap(rand.Int63()),
|
|
Flowers: map[Point]Flower{},
|
|
}
|
|
herbarium := NewHerbarium()
|
|
return &Game{
|
|
Speed: GameSpeedNormal,
|
|
Balance: 100,
|
|
Terrain: terrain,
|
|
Herbarium: herbarium,
|
|
|
|
centerChanged: NewEvents(),
|
|
speedChanged: NewEvents(),
|
|
toolChanged: NewEvents(),
|
|
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() {
|
|
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 {
|
|
flowers[dst] = g.Terrain.NewFlower(dst, flower.ID, flower.Traits)
|
|
}
|
|
}
|
|
}
|
|
if Sqr32(rand.Float32()) < flower.Traits.Life*flower.Traits.LifeModifier {
|
|
flowers[pos] = flower
|
|
}
|
|
}
|
|
g.Terrain.Flowers = flowers
|
|
}
|
|
|
|
func (g *Game) CancelTool() {
|
|
g.selectTool(nil)
|
|
}
|
|
|
|
func (g *Game) CenterChanged() EventHandler { return g.centerChanged }
|
|
|
|
func (g *Game) Dig(tile Point) {
|
|
id := g.Terrain.DigFlower(tile)
|
|
desc, ok := g.Herbarium.Find(id)
|
|
if !ok {
|
|
return
|
|
}
|
|
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) 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.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) Save() {
|
|
state := g.State()
|
|
err := state.Serialize(SaveGameName())
|
|
if err != nil {
|
|
log.Println("failed to save; error:", err)
|
|
}
|
|
}
|
|
|
|
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) 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() {
|
|
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() {
|
|
for g.simulation.Animate() {
|
|
g.tick()
|
|
}
|
|
}
|
|
|
|
func (g *Game) UserClickedTile(pos Point) {
|
|
if g.tool == nil {
|
|
return
|
|
}
|
|
g.tool.ClickedTile(g, pos)
|
|
}
|