Added flowers (+ basic simulation).

Added resource loader (load list of textures).
This commit is contained in:
Sander Schobers 2020-05-09 19:34:43 +02:00
parent b32017f18a
commit 2413c7d3d4
29 changed files with 326 additions and 108 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -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

View File

@ -79,51 +79,10 @@ func run() error {
if err != nil { if err != nil {
return err return err
} }
err = ctx.Textures.Load( textureLoader := tins2020.NewResourceLoader()
"tile-dirt", "images/tile_dirt.png", err = textureLoader.LoadFromFile(&ctx.Resources, "textures.txt", func(name, content string) error {
"tile-grass", "images/tile_grass.png", return ctx.Textures.Load(name, content)
"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",
)
if err != nil { if err != nil {
return err return err
} }
@ -165,6 +124,7 @@ func run() error {
} }
app.Handle(ctx, event) app.Handle(ctx, event)
} }
game.Update()
if ctx.ShouldQuit { if ctx.ShouldQuit {
break break

46
flower.go Normal file
View File

@ -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,
},
}
}

109
game.go
View File

@ -2,14 +2,15 @@ package tins2020
import ( import (
"math/rand" "math/rand"
"time"
"opslag.de/schobers/geom/noise"
) )
type Game struct { type Game struct {
Money int
Terrain *Map Terrain *Map
Flowers []Flower
Bees []Bee start time.Time
lastUpdate time.Duration
} }
type Map struct { type Map struct {
@ -18,55 +19,21 @@ type Map struct {
Variant NoiseMap Variant NoiseMap
PlaceX NoiseMap // displacement map of props PlaceX NoiseMap // displacement map of props
PlaceY NoiseMap PlaceY NoiseMap
Flowers map[Point]Flower
} }
type NoiseMap interface { func (m *Map) AddFlower(pos Point, traits FlowerTraits) {
Value(x, y int32) float64 m.Flowers[pos] = m.NewFlower(pos, traits)
} }
func NewNoiseMap(seed int64) NoiseMap { func (m *Map) NewFlower(pos Point, traits FlowerTraits) Flower {
return &noiseMap{ flower := Flower{
noise: noise.NewPerlin(seed), Traits: traits,
alpha: 2,
beta: 4,
harmonics: 2,
} }
} temp, humid := float32(m.Temp.Value(pos.X, pos.Y)), float32(m.Humid.Value(pos.X, pos.Y))
flower.Traits.UpdateModifier(temp, humid)
func NewRandomNoiseMap(seed int64) NoiseMap { return flower
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)
} }
func NewGame() *Game { func NewGame() *Game {
@ -76,18 +43,52 @@ func NewGame() *Game {
Variant: NewRandomNoiseMap(rand.Int63()), Variant: NewRandomNoiseMap(rand.Int63()),
PlaceX: NewRandomNoiseMap(rand.Int63()), PlaceX: NewRandomNoiseMap(rand.Int63()),
PlaceY: NewRandomNoiseMap(rand.Int63()), PlaceY: NewRandomNoiseMap(rand.Int63()),
Flowers: map[Point]Flower{},
} }
terrain.AddFlower(Pt(0, 0), NewPoppyTraits())
return &Game{ return &Game{
Money: 100,
Terrain: terrain, Terrain: terrain,
Flowers: []Flower{},
Bees: []Bee{}, start: time.Now(),
} }
} }
type Flower struct { func (g *Game) Update() {
Location PointF update := time.Since(g.start)
for g.lastUpdate < update {
g.tick()
g.lastUpdate += time.Millisecond * 10
}
} }
type Bee struct { func (g *Game) tick() {
Location PointF 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
} }

30
math.go
View File

@ -2,6 +2,36 @@ package tins2020
import "math" 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 Ceil32(x float32) float32 { return float32(math.Ceil(float64(x))) }
func Floor32(x float32) float32 { return float32(math.Floor(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))) }

52
noisemap.go Normal file
View File

@ -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)
}

View File

@ -64,7 +64,7 @@ func (p *projection) update(renderer *sdl.Renderer) {
log.Fatal(err) log.Fatal(err)
} }
p.windowCenter = Pt(windowW/2, windowH/2) 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)) { func (p *projection) visibleTiles(action func(int32, int32, Point)) {

51
resourceloader.go Normal file
View File

@ -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
}

View File

@ -127,6 +127,17 @@ func (r *terrainRenderer) Render(ctx *Context) {
return variantToTexture("bush-large-%d", stretch(variant, .8, 1)*multiplier) 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 // horizontal: [191, 321) = 130
// vertical: [267,332) = 65 // vertical: [267,332) = 65
@ -143,14 +154,7 @@ func (r *terrainRenderer) Render(ctx *Context) {
}) })
r.project.visibleTiles(func(x, y int32, pos Point) { r.project.visibleTiles(func(x, y int32, pos Point) {
variant := r.terrain.Variant.Value(x, y) text := toItemTexture(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)
if text == nil { if text == nil {
return return
} }