diff --git a/README.md b/README.md index dcbe44e..8f5c3d3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,47 @@ # TINS 2020 ## Additional Rules -TBD + +### Genre requirement +**** There will be 1 genre rule + +**genre rule #130** + +Theme: Flowers - There should be different flowers in the game, the more the better! +Comments: This rule just reminds me of my favourite platformer, Super Mario World 2: Yoshi's island - it was FULL of flowers: flower enemies, flower collectables, flowers in the background, flowers that grow to form new platforms, shy guys using flowers as camouflage in the flower beds... + +### Artistic requirements +**** There will be 1 artistical rule + +**artistical rule #60** +make fun of old-fashioned things. +Comments:E.g. have a mode in which the game plays in black and white, featuring an old granny in the corner saying "I didn't pay my colour television license for this", or occasionally "I was in a war" Some more inspirations [here](https://www.youtube.com/watch?v=67OPI3cXAaE) and [here](https://twitter.com/Bill_Gross/status/920406104911233024) + +### Technical requirement +**** There will be 2 technical rules + +**technical rule #10** + +Your game must include procedural content. Bonus points if procedural generation is used to create the gameplay environment. + +**technical rule #68** + +some kind of special text scroller + +Comments: I'll leave you with some inspirational links + +- [Wikipedia on procedural generation](https://en.wikipedia.org/wiki/Procedural_generation) +- [Extra Credits video on procedural content](https://www.youtube.com/watch?v=TgbuWfGeG2o) +- [A star wars text scroller](http://blogs.sitepointstatic.com/examples/tech/css3-starwars/index.html) +- [An old fasioned text scroller from the demo scene](https://www.youtube.com/watch?v=3rcXPBJhFf4) + +### Bonus rule +**** There will be 1 bonus rule + +**bonus rule #9** + +Act of monkey - you may replace a single rule by a rule that has the opposite effect +Comments: Lots of wiggle room here. For example, you could revert the last technical rule to have a non-text scroller, non-scrolling text, or simply, no text scroller. ## Building Prerequisites: diff --git a/cmd/example/example.go b/cmd/example/example.go new file mode 100644 index 0000000..6f52ecb --- /dev/null +++ b/cmd/example/example.go @@ -0,0 +1,70 @@ +// author: Jacky Boen + +package main + +import ( + "fmt" + "os" + + "github.com/veandco/go-sdl2/img" + "github.com/veandco/go-sdl2/sdl" +) + +var winTitle string = "Go-SDL2 Texture" +var winWidth, winHeight int32 = 800, 600 +var imageName string = "../../assets/test.png" + +func run() int { + var window *sdl.Window + var renderer *sdl.Renderer + var texture *sdl.Texture + var src, dst sdl.Rect + var err error + + window, err = sdl.CreateWindow(winTitle, sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, + winWidth, winHeight, sdl.WINDOW_SHOWN) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to create window: %s\n", err) + return 1 + } + defer window.Destroy() + + renderer, err = sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to create renderer: %s\n", err) + return 2 + } + defer renderer.Destroy() + + image, err := img.Load(imageName) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to load PNG: %s\n", err) + return 3 + } + defer image.Free() + + texture, err = renderer.CreateTextureFromSurface(image) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to create texture: %s\n", err) + return 4 + } + defer texture.Destroy() + + src = sdl.Rect{0, 0, 512, 512} + dst = sdl.Rect{100, 50, 512, 512} + + renderer.Clear() + renderer.SetDrawColor(255, 0, 0, 255) + renderer.FillRect(&sdl.Rect{0, 0, int32(winWidth), int32(winHeight)}) + renderer.Copy(texture, &src, &dst) + renderer.Present() + + sdl.PollEvent() + sdl.Delay(2000) + + return 0 +} + +func main() { + os.Exit(run()) +} diff --git a/cmd/tins2020/terrainrenderer.go b/cmd/tins2020/terrainrenderer.go new file mode 100644 index 0000000..06a8344 --- /dev/null +++ b/cmd/tins2020/terrainrenderer.go @@ -0,0 +1,15 @@ +package main + +import "opslag.de/schobers/tins2020" + +type terrainRenderer struct { + terrain *tins2020.Map +} + +func newTerrainRenderer(terrain *tins2020.Map) *terrainRenderer { + return &terrainRenderer{terrain} +} + +func (r *terrainRenderer) Render(ctx *tins2020.Context) { + +} diff --git a/cmd/tins2020/tins2020.go b/cmd/tins2020/tins2020.go index 7474875..8b781f5 100644 --- a/cmd/tins2020/tins2020.go +++ b/cmd/tins2020/tins2020.go @@ -3,7 +3,10 @@ package main import ( "log" + rice "github.com/GeertJohan/go.rice" "github.com/veandco/go-sdl2/sdl" + "github.com/veandco/go-sdl2/ttf" + "opslag.de/schobers/tins2020" ) func main() { @@ -19,33 +22,66 @@ func run() error { } defer sdl.Quit() - window, err := sdl.CreateWindow("TINS 2020", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, + if err := ttf.Init(); err != nil { + return err + } + defer ttf.Quit() + + ctx, err := tins2020.NewContext(rice.MustFindBox("res")) + if err != nil { + return err + } + defer ctx.Destroy() + + if ctx.Settings.Window.Location == nil { + ctx.Settings.Window.Location = &tins2020.Point{ + X: sdl.WINDOWPOS_UNDEFINED, + Y: sdl.WINDOWPOS_UNDEFINED, + } + } + + window, err := sdl.CreateWindow("TINS 2020", ctx.Settings.Window.Location.X, ctx.Settings.Window.Location.Y, 800, 600, sdl.WINDOW_SHOWN) if err != nil { return err } defer window.Destroy() - surface, err := window.GetSurface() + renderer, err := sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED) if err != nil { return err } - surface.FillRect(nil, 0) + defer renderer.Destroy() - rect := sdl.Rect{0, 0, 200, 200} - surface.FillRect(&rect, 0xffff0000) - window.UpdateSurface() + ctx.Init(renderer) + + err = ctx.Fonts.Load("default", "fonts/OpenSans-Regular.ttf", 12) + if err != nil { + return err + } running := true for running { for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() { - switch event.(type) { + switch e := event.(type) { case *sdl.QuitEvent: println("Quit") running = false break + case *sdl.WindowEvent: + switch e.Event { + case sdl.WINDOWEVENT_MOVED: + x, y := window.GetPosition() + ctx.Settings.Window.Location = &tins2020.Point{X: x, Y: y} + } } } + + renderer.SetDrawColor(0, 0, 0, 255) + renderer.Clear() + renderer.SetDrawColor(255, 0, 0, 255) + renderer.FillRect(&sdl.Rect{0, 0, 200, 200}) + renderer.Present() } return nil } diff --git a/context.go b/context.go new file mode 100644 index 0000000..9ef6408 --- /dev/null +++ b/context.go @@ -0,0 +1,38 @@ +package tins2020 + +import ( + rice "github.com/GeertJohan/go.rice" + "github.com/veandco/go-sdl2/sdl" +) + +type Context struct { + Fonts Fonts + Resources Resources + Textures Textures + Settings Settings +} + +func NewContext(res *rice.Box) (*Context, error) { + ctx := &Context{} + err := ctx.Settings.Init() + if err != nil { + return nil, err + } + err = ctx.Resources.Open(res) + if err != nil { + return nil, err + } + return ctx, nil +} + +func (c *Context) Init(renderer *sdl.Renderer) { + c.Fonts.Init(c.Resources.Copy()) + c.Textures.Init(renderer) +} + +func (c *Context) Destroy() { + c.Fonts.Destroy() + c.Resources.Destroy() + c.Textures.Destroy() + c.Settings.Store() +} diff --git a/fonts.go b/fonts.go new file mode 100644 index 0000000..a93934e --- /dev/null +++ b/fonts.go @@ -0,0 +1,41 @@ +package tins2020 + +import ( + "github.com/veandco/go-sdl2/ttf" + "opslag.de/schobers/fs/vfs" +) + +type Fonts struct { + dir vfs.CopyDir + fonts map[string]*ttf.Font +} + +func (f *Fonts) Init(dir vfs.CopyDir) { + f.dir = dir + f.fonts = map[string]*ttf.Font{} +} + +func (f *Fonts) Load(name, path string, size int) error { + fontPath, err := f.dir.Retrieve(path) + if err != nil { + return err + } + font, err := ttf.OpenFont(fontPath, size) + if err != nil { + return err + } + if font, ok := f.fonts[name]; ok { + font.Close() + } + f.fonts[name] = font + return nil +} + +func (f *Fonts) Destroy() { + if f.fonts == nil { + return + } + for _, f := range f.fonts { + f.Close() + } +} diff --git a/game.go b/game.go new file mode 100644 index 0000000..70deb72 --- /dev/null +++ b/game.go @@ -0,0 +1,61 @@ +package tins2020 + +import ( + "math/rand" + + "opslag.de/schobers/geom/noise" +) + +type Game struct { + Terrain *Map + Flowers []Flower + Bees []Bee +} + +type Map struct { + Temp *NoiseMap + Humid *NoiseMap +} + +type NoiseMap struct { + noise *noise.Perlin + alpha, beta float64 + harmonics int +} + +func NewNoiseMap(seed int64) *NoiseMap { + return &NoiseMap{ + noise: noise.NewPerlin(seed), + alpha: 2, + beta: 2, + harmonics: 4, + } +} + +func (m *NoiseMap) Value(x, y int) float64 { + return m.noise.Noise2D(float64(x), float64(y), m.alpha, m.beta, m.harmonics) +} + +func NewGame() *Game { + terrain := &Map{ + Temp: NewNoiseMap(rand.Int63()), + Humid: NewNoiseMap(rand.Int63()), + } + return &Game{ + Terrain: terrain, + Flowers: []Flower{}, + Bees: []Bee{}, + } +} + +type PointF struct { + X, Y float32 +} + +type Flower struct { + Location PointF +} + +type Bee struct { + Location PointF +} diff --git a/io.go b/io.go new file mode 100644 index 0000000..ce3f1ff --- /dev/null +++ b/io.go @@ -0,0 +1,50 @@ +package tins2020 + +import ( + "encoding/json" + "os" + "path/filepath" +) + +func DecodeJSON(path string, v interface{}) error { + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + err = json.NewDecoder(f).Decode(v) + if err != nil { + return err + } + return nil +} + +func EncodeJSON(path string, v interface{}) error { + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + return json.NewEncoder(f).Encode(v) +} + +func UserDir() (string, error) { + config, err := os.UserConfigDir() + if err != nil { + return "", err + } + dir := filepath.Join(config, "tins2020") + err = os.MkdirAll(dir, 0777) + if err != nil { + return "", err + } + return dir, nil +} + +func UserFile(name string) (string, error) { + dir, err := UserDir() + if err != nil { + return "", err + } + return filepath.Join(dir, name), nil +} diff --git a/point.go b/point.go new file mode 100644 index 0000000..66a5f0b --- /dev/null +++ b/point.go @@ -0,0 +1,6 @@ +package tins2020 + +type Point struct { + X int32 + Y int32 +} diff --git a/resources.go b/resources.go new file mode 100644 index 0000000..54a54a4 --- /dev/null +++ b/resources.go @@ -0,0 +1,39 @@ +package tins2020 + +import ( + rice "github.com/GeertJohan/go.rice" + "github.com/spf13/afero" + "opslag.de/schobers/fs/ricefs" + "opslag.de/schobers/fs/vfs" +) + +type Resources struct { + box *rice.Box + fs afero.Fs + copy vfs.CopyDir +} + +func (r *Resources) Destroy() { + r.copy.Destroy() +} + +func (r *Resources) Fs() afero.Fs { + return r.fs +} + +func (r *Resources) Open(box *rice.Box) error { + r.box = box + r.fs = vfs.NewOsFsFallback(r.box.Name(), ricefs.NewFs(box)) + copy, err := vfs.NewCopyDir(r.fs) + if err != nil { + return err + } + r.copy = copy + return nil +} + +func (r *Resources) Copy() vfs.CopyDir { return r.copy } + +func (r *Resources) Retrieve(name string) (string, error) { + return r.copy.Retrieve(name) +} diff --git a/settings.go b/settings.go new file mode 100644 index 0000000..3db65e7 --- /dev/null +++ b/settings.go @@ -0,0 +1,34 @@ +package tins2020 + +import "os" + +type Settings struct { + Window WindowSettings +} + +func SettingsPath() (string, error) { + return UserFile("settings.json") +} + +func (s *Settings) Init() error { + path, err := SettingsPath() + if err != nil { + return err + } + if _, err := os.Stat(path); os.IsNotExist(err) { + return nil + } + return DecodeJSON(path, s) +} + +func (s *Settings) Store() error { + path, err := SettingsPath() + if err != nil { + return err + } + return EncodeJSON(path, s) +} + +type WindowSettings struct { + Location *Point +} diff --git a/textures.go b/textures.go new file mode 100644 index 0000000..1ada552 --- /dev/null +++ b/textures.go @@ -0,0 +1,39 @@ +package tins2020 + +import ( + "github.com/veandco/go-sdl2/img" + "github.com/veandco/go-sdl2/sdl" +) + +type Textures struct { + renderer *sdl.Renderer + textures map[string]*sdl.Texture +} + +func (t *Textures) Init(renderer *sdl.Renderer) { + t.renderer = renderer + t.textures = map[string]*sdl.Texture{} +} + +func (t *Textures) Load(name, path string, size int) error { + surface, err := img.Load(name) + if err != nil { + return err + } + defer surface.Free() + texture, err := t.renderer.CreateTextureFromSurface(surface) + if t.textures == nil { + t.textures = map[string]*sdl.Texture{} + } + t.textures[name] = texture + return nil +} + +func (t *Textures) Destroy() { + if t.textures == nil { + return + } + for _, t := range t.textures { + t.Destroy() + } +}