From 6f9191cbf9923c44803424ab5c9ba03884319523 Mon Sep 17 00:00:00 2001 From: Sander Schobers Date: Mon, 9 Aug 2021 11:01:29 +0200 Subject: [PATCH] Updated README & added credits. --- README.md | 28 +++-- cmd/tins2021/app.go | 1 - cmd/tins2021/appcontext.go | 7 +- cmd/tins2021/credits.go | 185 ++++++++++++++++++++++++++++++++ cmd/tins2021/levelcontroller.go | 2 + cmd/tins2021/tins2021.go | 2 +- 6 files changed, 211 insertions(+), 14 deletions(-) create mode 100644 cmd/tins2021/credits.go diff --git a/README.md b/README.md index 5f34ebb..34573f7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# TINS 2020 +# TINS 2021 ## Content @@ -24,11 +24,14 @@ **Welcome to Qbitter!** -TODO: description of the game. +Qbitter is a game loosly based on a work "LW305 Kringloop" of M.C. Escher where a gnome runs down a stairs and morphs into its 2D abstract shape. The game also lends ideas from _Q*Bert_, a game from the eighties that was itself based on the works of M.C. Escher. In the game you (represented as a gnome) have to collect stars while trying to avoid enemies (hexagons). Every level has increasing difficulty. **Controls:** -TODO: fill in. +W: move up (left) cube +A: move down (left) cube +S: move down (right) cube +D: move up (right) cube ## Additional Rules @@ -36,23 +39,28 @@ TODO: fill in. **genre rule #143** -This rule has been omitted by applying the bonus rule. +This rule has been omitted (too subjective) by applying the bonus rule. There is two elements that could be considered funny though: +- The gnome (player) is an character that is absurdly placed in this world, both style (hand drawn vs geometric) and color (gray vs colorful) are opposites of each other. +- In the credits my own libraries are blatantly mentioned first (yes I know, it could be considered arrogance or is it humorous as well?). -**artistical rule #60** - -The least obvious and not finished within the time contraint: when researching you'll have to dial a phone number like you used to do with an old rotary dial (works by repeating the number on the keyboard an exact number of times). +**artistical rule #147** +The game is based on a work of M.C. Escher "LW305 Kringloop" and it shows in different ways: +- The isometric cubes of the game try to mimic the style. +- The gnome featured as player is a loose interpretation of the gnome drawn by M.C. Escher. +- Geometric figures are used as game elements (stars, hearts, enemies). + * They are all based on a formula applied on a polar coordinate system (also the heart but this one isn't repeated). **artistical rule #94** -TODO: describe plug. +In the credits some of the libraries I wrote (and used) are promoted (**Developed using** geom, zntg and allg5). To be sure that people notice this I put them on top of the list of credits. **technical rule #113** -The main grid is made of cubes (faux 3D) that are regular hexagons in 2D. +The main grid is made of cubes (faux 3D) that are regular hexagons in 2D. Additionaly the enemies of the player are made of wobbly hexagons. **bonus rule #13** -TODO: describe tests. +Some functionality of the root package (tins2021) is unit tested (coverage is at time of writing 17% of the statements). You can find the tests in the files ending on `_test.go` (e.g. `level_test.go`). ### Definition diff --git a/cmd/tins2021/app.go b/cmd/tins2021/app.go index 5fb27a0..0a63b51 100644 --- a/cmd/tins2021/app.go +++ b/cmd/tins2021/app.go @@ -34,7 +34,6 @@ func (a *app) loadFonts(ctx ui.Context, descriptors ...fontDescriptor) error { const fpsOverlayName = `fps` func (a *app) Init(ctx ui.Context) error { - if err := a.loadFonts(ctx, fontDescriptor{"debug", "fonts/FiraMono-Regular.ttf", 12}, fontDescriptor{"default", "fonts/escheresk.ttf", 48}, diff --git a/cmd/tins2021/appcontext.go b/cmd/tins2021/appcontext.go index 7816b30..4279528 100644 --- a/cmd/tins2021/appcontext.go +++ b/cmd/tins2021/appcontext.go @@ -22,8 +22,11 @@ func (app *appContext) show(control ui.Control) { app.setView(control) } -func (app *appContext) ShowCredits(ctx ui.Context) {} -func (app *appContext) ShowInfo(ctx ui.Context) {} +func (app *appContext) ShowCredits(ctx ui.Context) { + app.setView(newCredits(app, ctx)) +} + +func (app *appContext) ShowInfo(ctx ui.Context) {} func (app *appContext) ShowMainMenu(ctx ui.Context) { app.show(newMainMenu(app, ctx)) diff --git a/cmd/tins2021/credits.go b/cmd/tins2021/credits.go new file mode 100644 index 0000000..b86a05b --- /dev/null +++ b/cmd/tins2021/credits.go @@ -0,0 +1,185 @@ +package main + +import ( + "fmt" + "log" + "os/exec" + "runtime" + "strings" + "time" + + "opslag.de/schobers/geom" + "opslag.de/schobers/zntg/ui" +) + +type credits struct { + ui.ControlBase + + app *appContext + + content []string + hovering int + offset float32 + lastUpdate time.Time +} + +func newCredits(app *appContext, ctx ui.Context) *credits { + return &credits{app: app, lastUpdate: time.Now(), content: []string{ + "# QBITTER", + "", + "a game by Tharro", + "developed during the TINS 2021 game development competition", + "", + "**Developed using**", + "geom: a Go package for mathematics and geometries", + " - https://opslag.de/schobers/geom", + "zntg: an abstraction for rendering (UIs)", + " - https://opslag.de/schobers/zntg", + "allg5: an Allegro abstraction for Go", + " - https://opslag.de/schobers/allg5", + "", + "**... and also using**", + "Allegro: cross-platform development library", + " - https://liballeg.org", + "SDL: another cross-platform development library", + " - https://libsdl.org", + "go-sdl2: an SDL abstraction for Go", + " - https://github.com/veandco/go-sdl2", + "Fira Mono: monotype font", + "Escheresk: M.C. Escher themed font", + "Escher: M.C. Escher themed font", + "fauxgl: a software renderer for Go", + " - https://github.com/fogleman/fauxgl", + "draw2d: a 2D drawing library for Go", + " - https://github.com/llgcode/draw2d", + "go-colurful: a 2D drawing library for Go", + " - https://github.com/lucasb-eyer/go-colorful", + "resize: an image resizing library for Go", + " - https://github.com/nfnt/resize", + "testify: a testing library for Go", + " - https://github.com/stretchr/testify", + "", + "# THANKS", + "A big thank you to Amarillion for hosting this competition!", + " - https://tins.amarillion.org/2021", + }} +} + +func (c *credits) Handle(ctx ui.Context, e ui.Event) bool { + if c.ControlBase.Handle(ctx, e) { + return true + } + ctx.Animate() + + switch e := e.(type) { + case *ui.MouseButtonDownEvent: + if c.hovering != -1 { + s := c.content[c.hovering] + if strings.HasPrefix(s, " - https://") { + url := s[3:] + c.openBrowser(url) + } + } + case *ui.KeyDownEvent: + if e.Key == ui.KeyEscape { + c.app.ShowMainMenu(ctx) + return true + } + } + + c.hovering = -1 + mouse := ctx.MousePosition() + c.enumerateContent(ctx, func(s string, i int, top, height float32, font ui.Font) { + if !strings.HasPrefix(s, " - https://") { + return + } + if mouse.Y >= top && mouse.Y < top+height { + c.hovering = i + } + ctx.Renderer().SetMouseCursor(ui.MouseCursorPointer) + }) + + now := time.Now() + // if c.hovering == -1 { + delta := now.Sub(c.lastUpdate) + c.offset += float32(delta/time.Millisecond) / float32(20_000) + // } + c.lastUpdate = now + + return false +} + +func (c *credits) enumerateContent(ctx ui.Context, enum func(s string, i int, top, height float32, font ui.Font)) { + bounds := c.Bounds() + top := (1. - c.offset) * bounds.Max.Y + for i, font := range c.fonts(ctx) { + height := font.Height() + if top+height > 0 && top < bounds.Max.Y { + enum(c.content[i], i, top, height, font) + } + top += height + } +} + +func (c *credits) fonts(ctx ui.Context) []ui.Font { + titleFont := ctx.Fonts().Font("title") + captionFont := ctx.Fonts().Font("default") + defaultFont := ctx.Fonts().Font("score") + + font := func(s string) ui.Font { + if strings.HasPrefix(s, "# ") { + return titleFont + } + if strings.HasPrefix(s, "**") && strings.HasSuffix(s, "**") { + return captionFont + } + return defaultFont + } + + fonts := make([]ui.Font, 0, len(c.content)) + for _, line := range c.content { + font := font(line) + fonts = append(fonts, font) + } + return fonts +} + +func (c *credits) openBrowser(url string) { + var err error + switch runtime.GOOS { + case "linux": + err = exec.Command("xdg-open", url).Start() + case "windows": + err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() + case "darwin": + err = exec.Command("open", url).Start() + default: + err = fmt.Errorf("unsupported platform") + } + if err != nil { + log.Println(err) + } +} + +func (c *credits) Render(ctx ui.Context) { + renderer := ctx.Renderer() + width := c.Bounds().Dx() + defaultColor := ctx.Style().Palette.Text + c.enumerateContent(ctx, func(s string, i int, top, height float32, font ui.Font) { + color := defaultColor + if i == c.hovering { + color = ctx.Style().Palette.Secondary + } + + if strings.HasPrefix(s, "# ") { + s = s[2:] + } else if strings.HasPrefix(s, "**") && strings.HasSuffix(s, "**") { + s = s[2 : len(s)-2] + } else if strings.HasPrefix(s, " - https://") { + s = s[3:] + } + + textWidth := font.WidthOf(s) + renderer.Text(font, geom.PtF32(.5*(width-textWidth), top), color, s) + }) +} diff --git a/cmd/tins2021/levelcontroller.go b/cmd/tins2021/levelcontroller.go index ff20842..48b746c 100644 --- a/cmd/tins2021/levelcontroller.go +++ b/cmd/tins2021/levelcontroller.go @@ -100,6 +100,8 @@ func (r levelController) Handle(ctx ui.Context, e ui.Event) bool { r.Level.MovePlayer(tins2021.DirectionDownRight) case ui.KeyA: r.Level.MovePlayer(tins2021.DirectionDownLeft) + case ui.KeyEscape: + r.app.ShowMainMenu(ctx) } } for _, animations := range r.Animations { diff --git a/cmd/tins2021/tins2021.go b/cmd/tins2021/tins2021.go index a0d7645..2378dae 100644 --- a/cmd/tins2021/tins2021.go +++ b/cmd/tins2021/tins2021.go @@ -82,7 +82,7 @@ func run() error { Primary: zntg.MustHexColor(tins2021.Orange), PrimaryDark: zntg.MustHexColor(`#15569F`), PrimaryLight: zntg.MustHexColor(`#ABCAED`), - Secondary: zntg.MustHexColor(`#4AC69A`), + Secondary: zntg.MustHexColor(tins2021.Blue), SecondaryDark: zntg.MustHexColor(`#0AA36D`), SecondaryLight: zntg.MustHexColor(`#A6EED4`), Text: zntg.MustHexColor(`#EFEFEF`),