Compare commits
5 Commits
27afe594fe
...
2413c7d3d4
Author | SHA1 | Date | |
---|---|---|---|
2413c7d3d4 | |||
b32017f18a | |||
0a73529306 | |||
58aa819a4e | |||
8dd47c5ec2 |
BIN
cmd/tins2020/res/images/flower_purpleA_NE.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
cmd/tins2020/res/images/flower_purpleA_NW.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
cmd/tins2020/res/images/flower_purpleA_SE.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
cmd/tins2020/res/images/flower_purpleA_SW.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
cmd/tins2020/res/images/flower_purpleC_NE.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
cmd/tins2020/res/images/flower_purpleC_NW.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
cmd/tins2020/res/images/flower_purpleC_SE.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
cmd/tins2020/res/images/flower_purpleC_SW.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
cmd/tins2020/res/images/flower_redA_NE.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
cmd/tins2020/res/images/flower_redA_NW.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
cmd/tins2020/res/images/flower_redA_SE.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
cmd/tins2020/res/images/flower_redA_SW.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
cmd/tins2020/res/images/flower_redC_NE.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
cmd/tins2020/res/images/flower_redC_NW.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
cmd/tins2020/res/images/flower_redC_SE.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
cmd/tins2020/res/images/flower_redC_SW.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
cmd/tins2020/res/images/flower_yellowC_NE.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
cmd/tins2020/res/images/flower_yellowC_NW.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
cmd/tins2020/res/images/flower_yellowC_SE.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
cmd/tins2020/res/images/flower_yellowC_SW.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
74
cmd/tins2020/res/textures.txt
Normal 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
|
@ -47,10 +47,18 @@ func run() error {
|
|||||||
Y: sdl.WINDOWPOS_UNDEFINED,
|
Y: sdl.WINDOWPOS_UNDEFINED,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ctx.Settings.Window.Size == nil {
|
||||||
|
ctx.Settings.Window.Size = &tins2020.Point{
|
||||||
|
X: 800,
|
||||||
|
Y: 600,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sdl.SetHint(sdl.HINT_RENDER_SCALE_QUALITY, "1")
|
sdl.SetHint(sdl.HINT_RENDER_SCALE_QUALITY, "1")
|
||||||
window, err := sdl.CreateWindow("TINS 2020", ctx.Settings.Window.Location.X, ctx.Settings.Window.Location.Y,
|
window, err := sdl.CreateWindow("TINS 2020",
|
||||||
800, 600, sdl.WINDOW_SHOWN|sdl.WINDOW_RESIZABLE)
|
ctx.Settings.Window.Location.X, ctx.Settings.Window.Location.Y,
|
||||||
|
ctx.Settings.Window.Size.X, ctx.Settings.Window.Size.Y,
|
||||||
|
sdl.WINDOW_SHOWN|sdl.WINDOW_RESIZABLE)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -64,62 +72,31 @@ func run() error {
|
|||||||
|
|
||||||
ctx.Init(renderer)
|
ctx.Init(renderer)
|
||||||
|
|
||||||
err = ctx.Fonts.Load("default", "fonts/OpenSans-Regular.ttf", 12)
|
err = ctx.Fonts.LoadDesc(
|
||||||
|
tins2020.FontDescriptor{Name: "debug", Path: "fonts/OpenSans-Regular.ttf", Size: 12},
|
||||||
|
tins2020.FontDescriptor{Name: "default", Path: "fonts/FiraMono-Regular.ttf", Size: 10},
|
||||||
|
)
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
game := tins2020.NewGame()
|
game := tins2020.NewGame()
|
||||||
control := tins2020.NewTerrainRenderer(game.Terrain)
|
|
||||||
err = control.Init(ctx)
|
app := tins2020.NewContainer()
|
||||||
|
overlays := tins2020.NewContainer()
|
||||||
|
overlays.AddChild(&tins2020.FPS{})
|
||||||
|
content := tins2020.NewContainer()
|
||||||
|
app.AddChild(content)
|
||||||
|
app.AddChild(overlays)
|
||||||
|
content.AddChild(tins2020.NewTerrainRenderer(game.Terrain))
|
||||||
|
err = app.Init(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -135,6 +112,9 @@ func run() error {
|
|||||||
case sdl.WINDOWEVENT_MOVED:
|
case sdl.WINDOWEVENT_MOVED:
|
||||||
x, y := window.GetPosition()
|
x, y := window.GetPosition()
|
||||||
ctx.Settings.Window.Location = &tins2020.Point{X: x, Y: y}
|
ctx.Settings.Window.Location = &tins2020.Point{X: x, Y: y}
|
||||||
|
case sdl.WINDOWEVENT_SIZE_CHANGED:
|
||||||
|
w, h := window.GetSize()
|
||||||
|
ctx.Settings.Window.Size = &tins2020.Point{X: w, Y: h}
|
||||||
}
|
}
|
||||||
case *sdl.KeyboardEvent:
|
case *sdl.KeyboardEvent:
|
||||||
switch e.Keysym.Sym {
|
switch e.Keysym.Sym {
|
||||||
@ -142,8 +122,9 @@ func run() error {
|
|||||||
ctx.Quit()
|
ctx.Quit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
control.Handle(ctx, event)
|
app.Handle(ctx, event)
|
||||||
}
|
}
|
||||||
|
game.Update()
|
||||||
|
|
||||||
if ctx.ShouldQuit {
|
if ctx.ShouldQuit {
|
||||||
break
|
break
|
||||||
@ -151,7 +132,7 @@ func run() error {
|
|||||||
|
|
||||||
renderer.SetDrawColor(0, 0, 0, 255)
|
renderer.SetDrawColor(0, 0, 0, 255)
|
||||||
renderer.Clear()
|
renderer.Clear()
|
||||||
control.Render(ctx)
|
app.Render(ctx)
|
||||||
renderer.Present()
|
renderer.Present()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
39
container.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package tins2020
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Container struct {
|
||||||
|
Children []Control
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContainer() *Container {
|
||||||
|
return &Container{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) AddChild(child Control) {
|
||||||
|
c.Children = append(c.Children, child)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) Handle(ctx *Context, event sdl.Event) {
|
||||||
|
for _, child := range c.Children {
|
||||||
|
child.Handle(ctx, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) Render(ctx *Context) {
|
||||||
|
for _, child := range c.Children {
|
||||||
|
child.Render(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) Init(ctx *Context) error {
|
||||||
|
for _, child := range c.Children {
|
||||||
|
err := child.Init(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
18
control.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package tins2020
|
||||||
|
|
||||||
|
import "github.com/veandco/go-sdl2/sdl"
|
||||||
|
|
||||||
|
type Control interface {
|
||||||
|
Init(*Context) error
|
||||||
|
Handle(*Context, sdl.Event)
|
||||||
|
Render(*Context)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ControlBase struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ControlBase) Handle(*Context, sdl.Event) {}
|
||||||
|
|
||||||
|
func (b *ControlBase) Render(*Context) {}
|
||||||
|
|
||||||
|
func (b *ControlBase) Init(*Context) error { return nil }
|
46
flower.go
Normal 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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
54
fonts.go
@ -1,18 +1,66 @@
|
|||||||
package tins2020
|
package tins2020
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
"github.com/veandco/go-sdl2/ttf"
|
"github.com/veandco/go-sdl2/ttf"
|
||||||
"opslag.de/schobers/fs/vfs"
|
"opslag.de/schobers/fs/vfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Font struct {
|
||||||
|
*ttf.Font
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Font) Render(renderer *sdl.Renderer, text string, pos Point, color sdl.Color) (*Texture, error) {
|
||||||
|
surface, err := f.RenderUTF8Solid(text, color)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer surface.Free()
|
||||||
|
texture, err := NewTextureFromSurface(renderer, surface)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return texture, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Font) RenderCopy(renderer *sdl.Renderer, text string, pos Point, color sdl.Color) error {
|
||||||
|
texture, err := f.Render(renderer, text, pos, color)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer texture.Destroy()
|
||||||
|
texture.Copy(renderer, texture.RectOffset(pos))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Fonts struct {
|
type Fonts struct {
|
||||||
dir vfs.CopyDir
|
dir vfs.CopyDir
|
||||||
fonts map[string]*ttf.Font
|
fonts map[string]*Font
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fonts) Init(dir vfs.CopyDir) {
|
func (f *Fonts) Init(dir vfs.CopyDir) {
|
||||||
f.dir = dir
|
f.dir = dir
|
||||||
f.fonts = map[string]*ttf.Font{}
|
f.fonts = map[string]*Font{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fonts) Font(name string) *Font { return f.fonts[name] }
|
||||||
|
|
||||||
|
type FontDescriptor struct {
|
||||||
|
Name string
|
||||||
|
Path string
|
||||||
|
Size int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fonts) LoadDesc(fonts ...FontDescriptor) error {
|
||||||
|
for _, desc := range fonts {
|
||||||
|
err := f.Load(desc.Name, desc.Path, desc.Size)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error loading '%s'; error: %v", desc.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fonts) Load(name, path string, size int) error {
|
func (f *Fonts) Load(name, path string, size int) error {
|
||||||
@ -27,7 +75,7 @@ func (f *Fonts) Load(name, path string, size int) error {
|
|||||||
if font, ok := f.fonts[name]; ok {
|
if font, ok := f.fonts[name]; ok {
|
||||||
font.Close()
|
font.Close()
|
||||||
}
|
}
|
||||||
f.fonts[name] = font
|
f.fonts[name] = &Font{font}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
41
fpsrenderer.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package tins2020
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FPS struct {
|
||||||
|
ControlBase
|
||||||
|
|
||||||
|
start time.Time
|
||||||
|
stamp time.Duration
|
||||||
|
slot int
|
||||||
|
ticks []int
|
||||||
|
total int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FPS) Init(*Context) error {
|
||||||
|
f.start = time.Now()
|
||||||
|
f.stamp = 0
|
||||||
|
f.ticks = make([]int, 51)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FPS) Render(ctx *Context) {
|
||||||
|
elapsed := time.Since(f.start)
|
||||||
|
stamp := elapsed / (20 * time.Millisecond)
|
||||||
|
for f.stamp < stamp {
|
||||||
|
f.total += f.ticks[f.slot]
|
||||||
|
f.slot = (f.slot + 1) % len(f.ticks)
|
||||||
|
f.total -= f.ticks[f.slot]
|
||||||
|
f.ticks[f.slot] = 0
|
||||||
|
f.stamp++
|
||||||
|
}
|
||||||
|
f.ticks[f.slot]++
|
||||||
|
|
||||||
|
font := ctx.Fonts.Font("debug")
|
||||||
|
font.RenderCopy(ctx.Renderer, fmt.Sprintf("FPS: %d", f.total), Pt(5, 5), sdl.Color{R: 255, G: 255, B: 255, A: 255})
|
||||||
|
}
|
109
game.go
@ -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
|
||||||
}
|
}
|
||||||
|
37
math.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
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))) }
|
52
noisemap.go
Normal 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)
|
||||||
|
}
|
90
projection.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package tins2020
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type projection struct {
|
||||||
|
center PointF
|
||||||
|
zoom float32
|
||||||
|
zoomInv float32
|
||||||
|
|
||||||
|
windowRect sdl.Rect
|
||||||
|
tileScreenDelta PointF
|
||||||
|
tileScreenDeltaInv PointF
|
||||||
|
tileScreenOffset Point
|
||||||
|
tileScreenSize Point
|
||||||
|
tileFitScreenSize Point
|
||||||
|
windowCenter Point
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProjection() projection {
|
||||||
|
return projection{zoom: 1, tileScreenDelta: PtF(65, 32), tileScreenDeltaInv: PtF(1./65, 1./32)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *projection) mapToScreen(x, y int32) Point {
|
||||||
|
return p.mapToScreenF(float32(x), float32(y))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *projection) mapToScreenF(x, y float32) Point {
|
||||||
|
translated := PtF(x-p.center.X, y-p.center.Y)
|
||||||
|
return Pt(p.windowCenter.X+int32((translated.X-translated.Y)*65*p.zoomInv), p.windowCenter.Y+int32((translated.X+translated.Y)*32*p.zoomInv))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *projection) screenToMap(x, y int32) PointF {
|
||||||
|
pos := p.screenToMapRel(x-p.windowCenter.X, y-p.windowCenter.Y)
|
||||||
|
return p.center.Add(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *projection) screenToMapRel(x, y int32) PointF {
|
||||||
|
normX := p.zoom * float32(x)
|
||||||
|
normY := p.zoom * float32(y)
|
||||||
|
return PtF(.5*(p.tileScreenDeltaInv.X*normX+p.tileScreenDeltaInv.Y*normY), .5*(-p.tileScreenDeltaInv.X*normX+p.tileScreenDeltaInv.Y*normY))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *projection) screenToTileFitRect(pos Point) *sdl.Rect {
|
||||||
|
return &sdl.Rect{X: pos.X - p.tileFitScreenSize.X, Y: pos.Y - p.tileFitScreenSize.Y, W: 2 * p.tileFitScreenSize.X, H: 2 * p.tileFitScreenSize.Y}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *projection) screenToTileRect(pos Point) *sdl.Rect {
|
||||||
|
return &sdl.Rect{X: pos.X - p.tileScreenOffset.X, Y: pos.Y - p.tileScreenOffset.Y, W: p.tileScreenSize.X, H: p.tileScreenSize.Y}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *projection) update(renderer *sdl.Renderer) {
|
||||||
|
p.zoomInv = 1 / p.zoom
|
||||||
|
|
||||||
|
p.tileScreenOffset = Pt(int32(p.zoomInv*256), int32(p.zoomInv*300))
|
||||||
|
p.tileScreenSize = Pt(int32(p.zoomInv*512), int32(p.zoomInv*512))
|
||||||
|
p.tileFitScreenSize = Pt(int32(p.zoomInv*65), int32(p.zoomInv*32))
|
||||||
|
|
||||||
|
windowW, windowH, err := renderer.GetOutputSize()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
p.windowCenter = Pt(windowW/2, windowH/2)
|
||||||
|
p.windowRect = sdl.Rect{X: 0, Y: 0, W: windowW - 0, H: windowH - 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *projection) visibleTiles(action func(int32, int32, Point)) {
|
||||||
|
topLeft := p.screenToMap(p.windowRect.X, p.windowRect.Y)
|
||||||
|
topRight := p.screenToMap(p.windowRect.X+p.windowRect.W, p.windowRect.Y)
|
||||||
|
bottomLeft := p.screenToMap(p.windowRect.X, p.windowRect.Y+p.windowRect.H)
|
||||||
|
bottomRight := p.screenToMap(p.windowRect.X+p.windowRect.W, p.windowRect.Y+p.windowRect.H)
|
||||||
|
minY, maxY := int32(Floor32(topRight.Y)), int32(Ceil32(bottomLeft.Y))
|
||||||
|
minX, maxX := int32(Floor32(topLeft.X)), int32(Ceil32(bottomRight.X))
|
||||||
|
for y := minY; y <= maxY; y++ {
|
||||||
|
for x := minX; x <= maxX; x++ {
|
||||||
|
pos := p.mapToScreen(x, y)
|
||||||
|
rectFit := p.screenToTileFitRect(pos)
|
||||||
|
if rectFit.X+rectFit.W < p.windowRect.X || rectFit.Y+rectFit.H < p.windowRect.Y {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if rectFit.X > p.windowRect.X+p.windowRect.W || rectFit.Y > p.windowRect.Y+p.windowRect.H {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
action(x, y, pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +0,0 @@
|
|||||||
package tins2020
|
|
||||||
|
|
||||||
import "github.com/veandco/go-sdl2/sdl"
|
|
||||||
|
|
||||||
type Control interface {
|
|
||||||
Init(*Context) error
|
|
||||||
Handle(*Context, sdl.Event)
|
|
||||||
Render(*Context)
|
|
||||||
}
|
|
51
resourceloader.go
Normal 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
|
||||||
|
}
|
@ -31,4 +31,5 @@ func (s *Settings) Store() error {
|
|||||||
|
|
||||||
type WindowSettings struct {
|
type WindowSettings struct {
|
||||||
Location *Point
|
Location *Point
|
||||||
|
Size *Point
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,9 @@ package tins2020
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
|
|
||||||
"opslag.de/schobers/geom"
|
|
||||||
|
|
||||||
"github.com/veandco/go-sdl2/sdl"
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
)
|
)
|
||||||
|
|
||||||
type terrainRenderer struct {
|
type terrainRenderer struct {
|
||||||
@ -23,59 +21,6 @@ type interaction struct {
|
|||||||
mouseDrag *Point
|
mouseDrag *Point
|
||||||
}
|
}
|
||||||
|
|
||||||
type projection struct {
|
|
||||||
center PointF
|
|
||||||
zoom float32
|
|
||||||
zoomInv float32
|
|
||||||
|
|
||||||
tileScreenDelta PointF
|
|
||||||
tileScreenDeltaInv PointF
|
|
||||||
tileScreenOffset Point
|
|
||||||
tileScreenSize Point
|
|
||||||
windowCenter Point
|
|
||||||
}
|
|
||||||
|
|
||||||
func newProjection() projection {
|
|
||||||
return projection{zoom: 1, tileScreenDelta: PtF(65, 32), tileScreenDeltaInv: PtF(1./65, 1./32)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *projection) update(renderer *sdl.Renderer) {
|
|
||||||
p.zoomInv = 1 / p.zoom
|
|
||||||
|
|
||||||
p.tileScreenOffset = Pt(int32(p.zoomInv*256), int32(p.zoomInv*300))
|
|
||||||
p.tileScreenSize = Pt(int32(p.zoomInv*512), int32(p.zoomInv*512))
|
|
||||||
|
|
||||||
windowW, windowH, err := renderer.GetOutputSize()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
p.windowCenter = Pt(windowW/2, windowH/2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *projection) mapToScreen(x, y int32) Point {
|
|
||||||
return p.mapToScreenF(float32(x), float32(y))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *projection) mapToScreenF(x, y float32) Point {
|
|
||||||
translated := PtF(x-p.center.X, y-p.center.Y)
|
|
||||||
return Pt(p.windowCenter.X+int32((translated.X-translated.Y)*65*p.zoomInv), p.windowCenter.Y+int32((translated.X+translated.Y)*32*p.zoomInv))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *projection) screenToMapRel(x, y int32) PointF {
|
|
||||||
normX := p.zoom * float32(x)
|
|
||||||
normY := p.zoom * float32(y)
|
|
||||||
return PtF(.5*(p.tileScreenDeltaInv.X*normX+p.tileScreenDeltaInv.Y*normY), .5*(-p.tileScreenDeltaInv.X*normX+p.tileScreenDeltaInv.Y*normY))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *projection) screenToMap(x, y int32) PointF {
|
|
||||||
pos := p.screenToMapRel(x-p.windowCenter.X, y-p.windowCenter.Y)
|
|
||||||
return p.center.Add(pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *projection) screenToTileRect(pos Point) *sdl.Rect {
|
|
||||||
return &sdl.Rect{X: pos.X - p.tileScreenOffset.X, Y: pos.Y - p.tileScreenOffset.Y, W: p.tileScreenSize.X, H: p.tileScreenSize.Y}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTerrainRenderer(terrain *Map) Control {
|
func NewTerrainRenderer(terrain *Map) Control {
|
||||||
return &terrainRenderer{terrain: terrain, project: newProjection()}
|
return &terrainRenderer{terrain: terrain, project: newProjection()}
|
||||||
}
|
}
|
||||||
@ -92,25 +37,22 @@ func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) {
|
|||||||
r.interact.mouseLeftDown = e.Type == sdl.MOUSEBUTTONDOWN
|
r.interact.mouseLeftDown = e.Type == sdl.MOUSEBUTTONDOWN
|
||||||
if r.interact.mouseLeftDown && r.interact.mouseDrag == nil {
|
if r.interact.mouseLeftDown && r.interact.mouseDrag == nil {
|
||||||
r.interact.mouseDrag = &Point{e.X, e.Y}
|
r.interact.mouseDrag = &Point{e.X, e.Y}
|
||||||
fmt.Println("Drag start", r.interact.mouseDrag)
|
|
||||||
} else if !r.interact.mouseLeftDown && r.interact.mouseDrag != nil {
|
} else if !r.interact.mouseLeftDown && r.interact.mouseDrag != nil {
|
||||||
fmt.Println("Drag end", r.interact.mouseDrag)
|
|
||||||
r.interact.mouseDrag = nil
|
r.interact.mouseDrag = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *sdl.MouseMotionEvent:
|
case *sdl.MouseMotionEvent:
|
||||||
r.hover = r.project.screenToMap(e.X, e.Y)
|
r.hover = r.project.screenToMap(e.X, e.Y)
|
||||||
if r.interact.mouseDrag != nil {
|
if r.interact.mouseDrag != nil {
|
||||||
fmt.Println("Dragging...", r.project.center)
|
|
||||||
r.project.center = r.project.center.Sub(r.project.screenToMapRel(e.X-r.interact.mouseDrag.X, e.Y-r.interact.mouseDrag.Y))
|
r.project.center = r.project.center.Sub(r.project.screenToMapRel(e.X-r.interact.mouseDrag.X, e.Y-r.interact.mouseDrag.Y))
|
||||||
r.project.update(ctx.Renderer)
|
r.project.update(ctx.Renderer)
|
||||||
r.interact.mouseDrag = &Point{e.X, e.Y}
|
r.interact.mouseDrag = &Point{e.X, e.Y}
|
||||||
}
|
}
|
||||||
case *sdl.MouseWheelEvent:
|
case *sdl.MouseWheelEvent:
|
||||||
if e.Y > 0 {
|
if e.Y > 0 && r.project.zoom > .5 {
|
||||||
r.project.zoom *= .5
|
r.project.zoom *= .5
|
||||||
r.project.update(ctx.Renderer)
|
r.project.update(ctx.Renderer)
|
||||||
} else if e.Y < 0 {
|
} else if e.Y < 0 && r.project.zoom < 4 {
|
||||||
r.project.zoom *= 2
|
r.project.zoom *= 2
|
||||||
r.project.update(ctx.Renderer)
|
r.project.update(ctx.Renderer)
|
||||||
}
|
}
|
||||||
@ -185,41 +127,48 @@ 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
|
||||||
|
|
||||||
hover := Pt(int32(geom.Round32(r.hover.X)), int32(geom.Round32(r.hover.Y)))
|
hover := Pt(int32(geom.Round32(r.hover.X)), int32(geom.Round32(r.hover.Y)))
|
||||||
for y := int32(-100); y < 100; y++ {
|
r.project.visibleTiles(func(x, y int32, pos Point) {
|
||||||
for x := int32(-100); x < 100; x++ {
|
|
||||||
if x == hover.X && y == hover.Y {
|
if x == hover.X && y == hover.Y {
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
temp := r.terrain.Temp.Value(x, y)
|
temp := r.terrain.Temp.Value(x, y)
|
||||||
humid := r.terrain.Humid.Value(x, y)
|
humid := r.terrain.Humid.Value(x, y)
|
||||||
text := toTileTexture(temp, humid)
|
text := toTileTexture(temp, humid)
|
||||||
pos := r.project.mapToScreen(x, y)
|
rect := r.project.screenToTileRect(pos)
|
||||||
text.Copy(ctx.Renderer, r.project.screenToTileRect(pos))
|
text.Copy(ctx.Renderer, rect)
|
||||||
}
|
})
|
||||||
}
|
|
||||||
|
|
||||||
for y := int32(-100); y < 100; y++ {
|
r.project.visibleTiles(func(x, y int32, pos Point) {
|
||||||
for x := int32(-100); x < 100; x++ {
|
text := toItemTexture(x, y)
|
||||||
variant := r.terrain.Variant.Value(x, y)
|
|
||||||
if variant < -1 || variant > 1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
placeX, placeY := r.terrain.PlaceX.Value(x, y), r.terrain.PlaceY.Value(x, y)
|
placeX, placeY := r.terrain.PlaceX.Value(x, y), r.terrain.PlaceY.Value(x, y)
|
||||||
pos := r.project.mapToScreenF(float32(x)-.2+float32(.8*placeX-.4), float32(y)-.2+float32(.8*placeY-.4))
|
pos = r.project.mapToScreenF(float32(x)-.2+float32(.8*placeX-.4), float32(y)-.2+float32(.8*placeY-.4))
|
||||||
|
|
||||||
text.Copy(ctx.Renderer, r.project.screenToTileRect(pos))
|
text.Copy(ctx.Renderer, r.project.screenToTileRect(pos))
|
||||||
}
|
})
|
||||||
}
|
|
||||||
|
// gfx.RectangleColor(ctx.Renderer, r.project.windowRect.X, r.project.windowRect.Y, r.project.windowRect.X+r.project.windowRect.W, r.project.windowRect.Y+r.project.windowRect.H, sdl.Color{R: 255, A: 255})
|
||||||
|
// for y := int32(-40); y < 40; y++ {
|
||||||
|
// for x := int32(-40); x < 40; x++ {
|
||||||
|
// pos := r.project.mapToScreen(x, y)
|
||||||
|
// ctx.Fonts.Font("debug").RenderCopy(ctx.Renderer, fmt.Sprintf("(%d, %d)", x, y), pos, sdl.Color{R: 255, A: 255})
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,10 @@ func NewTextureFromSurface(renderer *sdl.Renderer, surface *sdl.Surface) (*Textu
|
|||||||
|
|
||||||
func (t *Texture) Rect() *sdl.Rect { return t.rect }
|
func (t *Texture) Rect() *sdl.Rect { return t.rect }
|
||||||
|
|
||||||
|
func (t *Texture) RectOffset(offset Point) *sdl.Rect {
|
||||||
|
return &sdl.Rect{X: offset.X, Y: offset.Y, W: t.rect.W, H: t.rect.H}
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Texture) Copy(renderer *sdl.Renderer, target *sdl.Rect) {
|
func (t *Texture) Copy(renderer *sdl.Renderer, target *sdl.Rect) {
|
||||||
renderer.Copy(t.texture, t.rect, target)
|
renderer.Copy(t.texture, t.rect, target)
|
||||||
}
|
}
|
||||||
|