diff --git a/cmd/tins2020/res/images/flower_purpleA_NE.png b/cmd/tins2020/res/images/flower_purpleA_NE.png new file mode 100644 index 0000000..08c992b Binary files /dev/null and b/cmd/tins2020/res/images/flower_purpleA_NE.png differ diff --git a/cmd/tins2020/res/images/flower_purpleA_NW.png b/cmd/tins2020/res/images/flower_purpleA_NW.png new file mode 100644 index 0000000..25fbed6 Binary files /dev/null and b/cmd/tins2020/res/images/flower_purpleA_NW.png differ diff --git a/cmd/tins2020/res/images/flower_purpleA_SE.png b/cmd/tins2020/res/images/flower_purpleA_SE.png new file mode 100644 index 0000000..1bb75cc Binary files /dev/null and b/cmd/tins2020/res/images/flower_purpleA_SE.png differ diff --git a/cmd/tins2020/res/images/flower_purpleA_SW.png b/cmd/tins2020/res/images/flower_purpleA_SW.png new file mode 100644 index 0000000..a2547d0 Binary files /dev/null and b/cmd/tins2020/res/images/flower_purpleA_SW.png differ diff --git a/cmd/tins2020/res/images/flower_purpleC_NE.png b/cmd/tins2020/res/images/flower_purpleC_NE.png new file mode 100644 index 0000000..ddf8b1f Binary files /dev/null and b/cmd/tins2020/res/images/flower_purpleC_NE.png differ diff --git a/cmd/tins2020/res/images/flower_purpleC_NW.png b/cmd/tins2020/res/images/flower_purpleC_NW.png new file mode 100644 index 0000000..0bd8687 Binary files /dev/null and b/cmd/tins2020/res/images/flower_purpleC_NW.png differ diff --git a/cmd/tins2020/res/images/flower_purpleC_SE.png b/cmd/tins2020/res/images/flower_purpleC_SE.png new file mode 100644 index 0000000..e8f91e6 Binary files /dev/null and b/cmd/tins2020/res/images/flower_purpleC_SE.png differ diff --git a/cmd/tins2020/res/images/flower_purpleC_SW.png b/cmd/tins2020/res/images/flower_purpleC_SW.png new file mode 100644 index 0000000..2b0d6a3 Binary files /dev/null and b/cmd/tins2020/res/images/flower_purpleC_SW.png differ diff --git a/cmd/tins2020/res/images/flower_redA_NE.png b/cmd/tins2020/res/images/flower_redA_NE.png new file mode 100644 index 0000000..dfbfe92 Binary files /dev/null and b/cmd/tins2020/res/images/flower_redA_NE.png differ diff --git a/cmd/tins2020/res/images/flower_redA_NW.png b/cmd/tins2020/res/images/flower_redA_NW.png new file mode 100644 index 0000000..36992c9 Binary files /dev/null and b/cmd/tins2020/res/images/flower_redA_NW.png differ diff --git a/cmd/tins2020/res/images/flower_redA_SE.png b/cmd/tins2020/res/images/flower_redA_SE.png new file mode 100644 index 0000000..2cdf374 Binary files /dev/null and b/cmd/tins2020/res/images/flower_redA_SE.png differ diff --git a/cmd/tins2020/res/images/flower_redA_SW.png b/cmd/tins2020/res/images/flower_redA_SW.png new file mode 100644 index 0000000..0d6e6f1 Binary files /dev/null and b/cmd/tins2020/res/images/flower_redA_SW.png differ diff --git a/cmd/tins2020/res/images/flower_redC_NE.png b/cmd/tins2020/res/images/flower_redC_NE.png new file mode 100644 index 0000000..bd6c52a Binary files /dev/null and b/cmd/tins2020/res/images/flower_redC_NE.png differ diff --git a/cmd/tins2020/res/images/flower_redC_NW.png b/cmd/tins2020/res/images/flower_redC_NW.png new file mode 100644 index 0000000..5b42412 Binary files /dev/null and b/cmd/tins2020/res/images/flower_redC_NW.png differ diff --git a/cmd/tins2020/res/images/flower_redC_SE.png b/cmd/tins2020/res/images/flower_redC_SE.png new file mode 100644 index 0000000..b5ef786 Binary files /dev/null and b/cmd/tins2020/res/images/flower_redC_SE.png differ diff --git a/cmd/tins2020/res/images/flower_redC_SW.png b/cmd/tins2020/res/images/flower_redC_SW.png new file mode 100644 index 0000000..2782955 Binary files /dev/null and b/cmd/tins2020/res/images/flower_redC_SW.png differ diff --git a/cmd/tins2020/res/images/flower_yellowC_NE.png b/cmd/tins2020/res/images/flower_yellowC_NE.png new file mode 100644 index 0000000..f7342eb Binary files /dev/null and b/cmd/tins2020/res/images/flower_yellowC_NE.png differ diff --git a/cmd/tins2020/res/images/flower_yellowC_NW.png b/cmd/tins2020/res/images/flower_yellowC_NW.png new file mode 100644 index 0000000..4150213 Binary files /dev/null and b/cmd/tins2020/res/images/flower_yellowC_NW.png differ diff --git a/cmd/tins2020/res/images/flower_yellowC_SE.png b/cmd/tins2020/res/images/flower_yellowC_SE.png new file mode 100644 index 0000000..02f9ff4 Binary files /dev/null and b/cmd/tins2020/res/images/flower_yellowC_SE.png differ diff --git a/cmd/tins2020/res/images/flower_yellowC_SW.png b/cmd/tins2020/res/images/flower_yellowC_SW.png new file mode 100644 index 0000000..77e528d Binary files /dev/null and b/cmd/tins2020/res/images/flower_yellowC_SW.png differ diff --git a/cmd/tins2020/res/textures.txt b/cmd/tins2020/res/textures.txt new file mode 100644 index 0000000..af6d0d0 --- /dev/null +++ b/cmd/tins2020/res/textures.txt @@ -0,0 +1,74 @@ + +tile-dirt: images/tile_dirt.png +tile-grass: images/tile_grass.png +tile-snow: images/tile_snow.png + +cactus-small-1: images/cactus_short_NE.png +cactus-small-2: images/cactus_short_NW.png +cactus-small-3: images/cactus_short_SW.png +cactus-small-4: images/cactus_short_SE.png + +cactus-tall-1: images/cactus_tall_NE.png +cactus-tall-2: images/cactus_tall_NW.png +cactus-tall-3: images/cactus_tall_SW.png +cactus-tall-4: images/cactus_tall_SE.png + +tree-fat-1: images/tree_fat_NE.png +tree-fat-2: images/tree_fat_NW.png +tree-fat-3: images/tree_fat_SW.png +tree-fat-4: images/tree_fat_SE.png + +tree-pine-1: images/tree_pineDefaultA_NE.png +tree-pine-2: images/tree_pineDefaultA_NW.png +tree-pine-3: images/tree_pineDefaultA_SW.png +tree-pine-4: images/tree_pineDefaultA_SE.png + +grass-small-1: images/grass_NE.png +grass-small-2: images/grass_NW.png +grass-small-3: images/grass_SW.png +grass-small-4: images/grass_SE.png + +grass-leafs-1: images/grass_leafs_NE.png +grass-leafs-2: images/grass_leafs_NW.png +grass-leafs-3: images/grass_leafs_SW.png +grass-leafs-4: images/grass_leafs_SE.png + +bush-small-1: images/plant_bushSmall_NE.png +bush-small-2: images/plant_bushSmall_NW.png +bush-small-3: images/plant_bushSmall_SW.png +bush-small-4: images/plant_bushSmall_SE.png + +bush-large-1: images/plant_bushLarge_NE.png +bush-large-2: images/plant_bushLarge_NW.png +bush-large-3: images/plant_bushLarge_SW.png +bush-large-4: images/plant_bushLarge_SE.png + +bush-large-1: images/plant_bushLarge_NE.png +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-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-red-c-1: images/flower_redC_NE.png +flower-red-c-2: images/flower_redC_NW.png +flower-red-c-3: images/flower_redC_SW.png +flower-red-c-4: images/flower_redC_SE.png + +flower-red-a-1: images/flower_redA_NE.png +flower-red-a-2: images/flower_redA_NW.png +flower-red-a-3: images/flower_redA_SW.png +flower-red-a-4: images/flower_redA_SE.png + +flower-purple-a-1: images/flower_purpleA_NE.png +flower-purple-a-2: images/flower_purpleA_NW.png +flower-purple-a-3: images/flower_purpleA_SW.png +flower-purple-a-4: images/flower_purpleA_SE.png + +flower-purple-c-1: images/flower_purpleC_NE.png +flower-purple-c-2: images/flower_purpleC_NW.png +flower-purple-c-3: images/flower_purpleC_SW.png +flower-purple-c-4: images/flower_purpleC_SE.png diff --git a/cmd/tins2020/tins2020.go b/cmd/tins2020/tins2020.go index 4585e24..ea3fbff 100644 --- a/cmd/tins2020/tins2020.go +++ b/cmd/tins2020/tins2020.go @@ -79,51 +79,10 @@ func run() error { if err != nil { return err } - err = ctx.Textures.Load( - "tile-dirt", "images/tile_dirt.png", - "tile-grass", "images/tile_grass.png", - "tile-snow", "images/tile_snow.png", - - "cactus-small-1", "images/cactus_short_NE.png", - "cactus-small-2", "images/cactus_short_NW.png", - "cactus-small-3", "images/cactus_short_SW.png", - "cactus-small-4", "images/cactus_short_SE.png", - - "cactus-tall-1", "images/cactus_tall_NE.png", - "cactus-tall-2", "images/cactus_tall_NW.png", - "cactus-tall-3", "images/cactus_tall_SW.png", - "cactus-tall-4", "images/cactus_tall_SE.png", - - "tree-fat-1", "images/tree_fat_NE.png", - "tree-fat-2", "images/tree_fat_NW.png", - "tree-fat-3", "images/tree_fat_SW.png", - "tree-fat-4", "images/tree_fat_SE.png", - - "tree-pine-1", "images/tree_pineDefaultA_NE.png", - "tree-pine-2", "images/tree_pineDefaultA_NW.png", - "tree-pine-3", "images/tree_pineDefaultA_SW.png", - "tree-pine-4", "images/tree_pineDefaultA_SE.png", - - "grass-small-1", "images/grass_NE.png", - "grass-small-2", "images/grass_NW.png", - "grass-small-3", "images/grass_SW.png", - "grass-small-4", "images/grass_SE.png", - - "grass-leafs-1", "images/grass_leafs_NE.png", - "grass-leafs-2", "images/grass_leafs_NW.png", - "grass-leafs-3", "images/grass_leafs_SW.png", - "grass-leafs-4", "images/grass_leafs_SE.png", - - "bush-small-1", "images/plant_bushSmall_NE.png", - "bush-small-2", "images/plant_bushSmall_NW.png", - "bush-small-3", "images/plant_bushSmall_SW.png", - "bush-small-4", "images/plant_bushSmall_SE.png", - - "bush-large-1", "images/plant_bushLarge_NE.png", - "bush-large-2", "images/plant_bushLarge_NW.png", - "bush-large-3", "images/plant_bushLarge_SW.png", - "bush-large-4", "images/plant_bushLarge_SE.png", - ) + textureLoader := tins2020.NewResourceLoader() + err = textureLoader.LoadFromFile(&ctx.Resources, "textures.txt", func(name, content string) error { + return ctx.Textures.Load(name, content) + }) if err != nil { return err } @@ -165,6 +124,7 @@ func run() error { } app.Handle(ctx, event) } + game.Update() if ctx.ShouldQuit { break diff --git a/flower.go b/flower.go new file mode 100644 index 0000000..a26325c --- /dev/null +++ b/flower.go @@ -0,0 +1,46 @@ +package tins2020 + +type Flower struct { + Traits FlowerTraits +} + +type FlowerTraits struct { + Spread float32 + Life float32 + LifeModifier float32 + Resistance FlowerResistance +} + +func (t *FlowerTraits) UpdateModifier(temp, humid float32) { + mod := float32(1) + cold := temp * 2 + mod *= Min32(1, Sqrt32(t.Resistance.Cold*t.Resistance.Cold+cold*cold)) + hot := (1 - temp) * 2 + mod *= Min32(1, Sqrt32(t.Resistance.Hot*t.Resistance.Hot+hot*hot)) + dry := humid * 2 + mod *= Min32(1, Sqrt32(t.Resistance.Dry*t.Resistance.Dry+dry*dry)) + wet := (1 - humid) * 2 + mod *= Min32(1, Sqrt32(t.Resistance.Wet*t.Resistance.Wet+wet*wet)) + t.LifeModifier = mod +} + +type FlowerResistance struct { + Cold float32 // 0 will die almost instantly, 1 is completely resistant. + Hot float32 + Dry float32 + Wet float32 +} + +// NewPoppyTraits creates the traits of a poppy, a very generic flower that thrives in a moderate climate. +func NewPoppyTraits() FlowerTraits { + return FlowerTraits{ + Spread: 0.01, + Life: 0.9991, + Resistance: FlowerResistance{ + Cold: 0.3, + Hot: 0.3, + Dry: 0.3, + Wet: 0.3, + }, + } +} diff --git a/game.go b/game.go index 61db4d8..5802e52 100644 --- a/game.go +++ b/game.go @@ -2,14 +2,15 @@ package tins2020 import ( "math/rand" - - "opslag.de/schobers/geom/noise" + "time" ) type Game struct { + Money int Terrain *Map - Flowers []Flower - Bees []Bee + + start time.Time + lastUpdate time.Duration } type Map struct { @@ -18,55 +19,21 @@ type Map struct { Variant NoiseMap PlaceX NoiseMap // displacement map of props PlaceY NoiseMap + + Flowers map[Point]Flower } -type NoiseMap interface { - Value(x, y int32) float64 +func (m *Map) AddFlower(pos Point, traits FlowerTraits) { + m.Flowers[pos] = m.NewFlower(pos, traits) } -func NewNoiseMap(seed int64) NoiseMap { - return &noiseMap{ - noise: noise.NewPerlin(seed), - alpha: 2, - beta: 4, - harmonics: 2, +func (m *Map) NewFlower(pos Point, traits FlowerTraits) Flower { + flower := Flower{ + Traits: traits, } -} - -func NewRandomNoiseMap(seed int64) NoiseMap { - return &randomNoiseMap{noise.NewPerlin(seed)} -} - -type noiseMap struct { - noise *noise.Perlin - alpha, beta float64 - 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. The range of the output is approximately [-1.325825214,1.325825214] (for 4 harmonics). -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 - return clipNormalized(value) -} - -type randomNoiseMap struct { - *noise.Perlin -} - -// Value generates the noise value for an x/y pair. The range of the output is approximately [-1.325825214,1.325825214] (for 4 harmonics). -func (m randomNoiseMap) Value(x, y int32) float64 { - value := m.Noise2D(float64(x)*.53, float64(y)*.53, 1.01, 2, 2)*.5 + .5 - return clipNormalized(value) + 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 } func NewGame() *Game { @@ -76,18 +43,52 @@ func NewGame() *Game { Variant: NewRandomNoiseMap(rand.Int63()), PlaceX: NewRandomNoiseMap(rand.Int63()), PlaceY: NewRandomNoiseMap(rand.Int63()), + Flowers: map[Point]Flower{}, } + terrain.AddFlower(Pt(0, 0), NewPoppyTraits()) return &Game{ + Money: 100, Terrain: terrain, - Flowers: []Flower{}, - Bees: []Bee{}, + + start: time.Now(), } } -type Flower struct { - Location PointF +func (g *Game) Update() { + update := time.Since(g.start) + for g.lastUpdate < update { + g.tick() + g.lastUpdate += time.Millisecond * 10 + } } -type Bee struct { - Location PointF +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.Traits) + } + } + } + if rand.Float32() < flower.Traits.Life*flower.Traits.LifeModifier { + flowers[pos] = flower + } + } + g.Terrain.Flowers = flowers } diff --git a/math.go b/math.go index 949dafd..4f221eb 100644 --- a/math.go +++ b/math.go @@ -2,6 +2,36 @@ package tins2020 import "math" +func Abs32(x float32) float32 { + if x < 0 { + return -x + } + return x +} + +func AbsSub32(a, b float32) float32 { + if a > b { + return a - b + } + return b - a +} + func Ceil32(x float32) float32 { return float32(math.Ceil(float64(x))) } func Floor32(x float32) float32 { return float32(math.Floor(float64(x))) } + +func Max32(a, b float32) float32 { + if a > b { + return a + } + return b +} + +func Min32(a, b float32) float32 { + if a < b { + return a + } + return b +} + +func Sqrt32(x float32) float32 { return float32(math.Sqrt(float64(x))) } diff --git a/noisemap.go b/noisemap.go new file mode 100644 index 0000000..ed5060c --- /dev/null +++ b/noisemap.go @@ -0,0 +1,52 @@ +package tins2020 + +import "opslag.de/schobers/geom/noise" + +type NoiseMap interface { + Value(x, y int32) float64 +} + +func NewNoiseMap(seed int64) NoiseMap { + return &noiseMap{ + noise: noise.NewPerlin(seed), + alpha: 2, + beta: 4, + harmonics: 2, + } +} + +func NewRandomNoiseMap(seed int64) NoiseMap { + return &randomNoiseMap{noise.NewPerlin(seed)} +} + +type noiseMap struct { + noise *noise.Perlin + alpha, beta float64 + 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. The range of the output is approximately [-1.325825214,1.325825214] (for 4 harmonics). +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 + return clipNormalized(value) +} + +type randomNoiseMap struct { + *noise.Perlin +} + +// Value generates the noise value for an x/y pair. The range of the output is approximately [-1.325825214,1.325825214] (for 4 harmonics). +func (m randomNoiseMap) Value(x, y int32) float64 { + value := m.Noise2D(float64(x)*.53, float64(y)*.53, 1.01, 2, 2)*.5 + .5 + return clipNormalized(value) +} diff --git a/projection.go b/projection.go index f7dc56a..a7cbc81 100644 --- a/projection.go +++ b/projection.go @@ -64,7 +64,7 @@ func (p *projection) update(renderer *sdl.Renderer) { log.Fatal(err) } p.windowCenter = Pt(windowW/2, windowH/2) - p.windowRect = sdl.Rect{X: 200, Y: 200, W: windowW - 400, H: windowH - 400} + p.windowRect = sdl.Rect{X: 0, Y: 0, W: windowW - 0, H: windowH - 0} } func (p *projection) visibleTiles(action func(int32, int32, Point)) { diff --git a/resourceloader.go b/resourceloader.go new file mode 100644 index 0000000..58048d4 --- /dev/null +++ b/resourceloader.go @@ -0,0 +1,51 @@ +package tins2020 + +import ( + "bufio" + "fmt" + "strings" +) + +type ResourceLoader struct { + Resources map[string]string +} + +func NewResourceLoader() *ResourceLoader { + return &ResourceLoader{} +} + +func (l *ResourceLoader) parseResourcesFile(res *Resources, name string) error { + f, err := res.Fs().Open(name) + if err != nil { + return err + } + defer f.Close() + + l.Resources = map[string]string{} + scanner := bufio.NewScanner(f) + for scanner.Scan() { + definition := scanner.Text() + sep := strings.Index(definition, ":") + if sep == -1 { + continue + } + name := strings.TrimSpace(definition[:sep]) + content := strings.TrimSpace(definition[sep+1:]) + l.Resources[name] = content + } + return nil +} + +func (l *ResourceLoader) LoadFromFile(res *Resources, name string, action func(string, string) error) error { + err := l.parseResourcesFile(res, name) + if err != nil { + return err + } + for name, content := range l.Resources { + err = action(name, content) + if err != nil { + return fmt.Errorf("cannot load resource '%s'; error: %v", name, err) + } + } + return nil +} diff --git a/terrainrenderer.go b/terrainrenderer.go index b2b273f..69d9ca2 100644 --- a/terrainrenderer.go +++ b/terrainrenderer.go @@ -127,6 +127,17 @@ func (r *terrainRenderer) Render(ctx *Context) { return variantToTexture("bush-large-%d", stretch(variant, .8, 1)*multiplier) } + toItemTexture := func(x, y int32) *Texture { + variant := r.terrain.Variant.Value(x, y) + _, ok := r.terrain.Flowers[Pt(x, y)] + if ok { + return variantToTexture("flower-poppy-%d", variant) + } + temp := r.terrain.Temp.Value(x, y) + humid := r.terrain.Humid.Value(x, y) + return toPropTexture(temp, humid, variant) + } + // horizontal: [191, 321) = 130 // vertical: [267,332) = 65 @@ -143,14 +154,7 @@ func (r *terrainRenderer) Render(ctx *Context) { }) r.project.visibleTiles(func(x, y int32, pos Point) { - variant := r.terrain.Variant.Value(x, y) - if variant < -1 || variant > 1 { - return - } - - temp := r.terrain.Temp.Value(x, y) - humid := r.terrain.Humid.Value(x, y) - text := toPropTexture(temp, humid, variant) + text := toItemTexture(x, y) if text == nil { return }