From 157ec6d42dda72713fbe51ef949254f30a2167ff Mon Sep 17 00:00:00 2001 From: Sander Schobers Date: Mon, 9 Aug 2021 13:04:31 +0200 Subject: [PATCH] Moved initialization of textures to main (appContext). Added info screen. --- cmd/tins2021/app.go | 8 ++- cmd/tins2021/appcontext.go | 36 ++++++++++++- cmd/tins2021/info.go | 89 +++++++++++++++++++++++++++++++++ cmd/tins2021/levelcontroller.go | 39 ++------------- cmd/tins2021/mainmenu.go | 5 +- cmd/tins2021/ui.go | 7 +++ 6 files changed, 143 insertions(+), 41 deletions(-) create mode 100644 cmd/tins2021/info.go diff --git a/cmd/tins2021/app.go b/cmd/tins2021/app.go index 0a63b51..91e9484 100644 --- a/cmd/tins2021/app.go +++ b/cmd/tins2021/app.go @@ -55,11 +55,9 @@ func (a *app) Init(ctx ui.Context) error { ctx.Overlays().AddOnTop(fpsOverlayName, &play.FPS{Align: ui.AlignRight}, false) - a.context = &appContext{ - setView: func(control ui.Control) { - a.Content = control - }, - } + a.context = newAppContext(ctx, func(control ui.Control) { + a.Content = control + }) a.context.ShowMainMenu(ctx) return nil diff --git a/cmd/tins2021/appcontext.go b/cmd/tins2021/appcontext.go index 6a52174..26689cf 100644 --- a/cmd/tins2021/appcontext.go +++ b/cmd/tins2021/appcontext.go @@ -1,6 +1,8 @@ package main import ( + "image" + "opslag.de/schobers/tins2021" "opslag.de/schobers/zntg/ui" ) @@ -9,6 +11,36 @@ type appContext struct { setView func(ui.Control) Debug bool + + MonsterTextureNames map[tins2021.MonsterType]string +} + +func newAppContext(ctx ui.Context, setView func(ui.Control)) *appContext { + newAnimatedTexture(ctx, "star", "resources/images/star.png", func() image.Image { + return tins2021.AnimatePolygon(tins2021.CreateStar(5), tins2021.Yellow, tins2021.NewRotateAnimation(defaultAnimationFrames)) + }) + newAnimatedTexture(ctx, "heart", "resources/images/heart.png", func() image.Image { + return tins2021.AnimatePolygon(tins2021.CreateHeart(), tins2021.Red, tins2021.NewRotateAnimation(defaultAnimationFrames)) + }) + + app := &appContext{ + setView: setView, + MonsterTextureNames: map[tins2021.MonsterType]string{ + tins2021.MonsterTypeStraight: "straight-walking-monster", + tins2021.MonsterTypeRandom: "random-walking-monster", + tins2021.MonsterTypeChaser: "chasing-monster", + }, + } + newAnimatedTexture(ctx, app.MonsterTextureNames[tins2021.MonsterTypeStraight], "resources/images/monster-straight.png", func() image.Image { + return tins2021.AnimatePolygon(tins2021.CreateHexagon(), tins2021.Green, tins2021.NewWobbleAnimation(defaultAnimationFrames, 30)) + }) + newAnimatedTexture(ctx, app.MonsterTextureNames[tins2021.MonsterTypeRandom], "resources/images/monster-random.png", func() image.Image { + return tins2021.AnimatePolygon(tins2021.CreateHexagon(), tins2021.Blue, tins2021.NewWobbleAnimation(defaultAnimationFrames, 30)) + }) + newAnimatedTexture(ctx, app.MonsterTextureNames[tins2021.MonsterTypeChaser], "resources/images/monster-chaser.png", func() image.Image { + return tins2021.AnimatePolygon(tins2021.CreateHexagon(), tins2021.Purple, tins2021.NewWobbleAnimation(defaultAnimationFrames, 30)) + }) + return app } const numberOfStars = 5 @@ -41,7 +73,9 @@ func (app *appContext) ShowCredits(ctx ui.Context) { app.setView(newCredits(app, ctx)) } -func (app *appContext) ShowInfo(ctx ui.Context) {} +func (app *appContext) ShowInfo(ctx ui.Context) { + app.setView(newInfo(app, ctx)) +} func (app *appContext) ShowMainMenu(ctx ui.Context) { app.show(newMainMenu(app, ctx)) diff --git a/cmd/tins2021/info.go b/cmd/tins2021/info.go new file mode 100644 index 0000000..a548351 --- /dev/null +++ b/cmd/tins2021/info.go @@ -0,0 +1,89 @@ +package main + +import ( + "opslag.de/schobers/geom" + "opslag.de/schobers/tins2021" + "opslag.de/schobers/zntg/ui" +) + +type info struct { + ui.StackPanel + + app *appContext + legend *ui.StackPanel +} + +const infoText = "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." + +type infoLegend struct { + ui.ControlBase + + Icon ui.Texture + Description string +} + +func (l *infoLegend) DesiredSize(ctx ui.Context, size geom.PointF32) geom.PointF32 { + return geom.PtF32(geom.NaN32(), .4*tins2021.TextureSize) +} + +const infoLegenIconSize = .36 + +var infoLegendIconRectangle = geom.RectRelF32(0, 0, infoLegenIconSize*tins2021.TextureSize, infoLegenIconSize*tins2021.TextureSize) + +func (l *infoLegend) Render(ctx ui.Context) { + bounds := l.Bounds() + separator := .3 * bounds.Dx() + textureHeight := float32(l.Icon.Height()) + renderer := ctx.Renderer() + renderer.DrawTexturePointOptions(l.Icon, bounds.Min.Add2D(separator-textureHeight, 0), ui.DrawOptions{Source: &infoLegendIconRectangle}) + font := ctx.Fonts().Font("score") + fontHeight := float32(font.Height()) + renderer.Text(font, bounds.Min.Add2D(separator+12, .5*(textureHeight-fontHeight)), ctx.Style().Palette.Text, l.Description) +} + +func newInfo(app *appContext, ctx ui.Context) ui.Control { + legend := ui.BuildStackPanel(ui.OrientationVertical, func(p *ui.StackPanel) { + p.AddChild(&infoLegend{ + Icon: ctx.Textures().ScaledByName("star", infoLegenIconSize), + Description: "Collect them to complete the level.", + }) + p.AddChild(&infoLegend{ + Icon: ctx.Textures().ScaledByName("heart", infoLegenIconSize), + Description: "Gives (back) a life.", + }) + p.AddChild(&infoLegend{ + Icon: ctx.Textures().ScaledByName("straight-walking-monster", infoLegenIconSize), + Description: "Monster that walks over a fixed diagonal.", + }) + p.AddChild(&infoLegend{ + Icon: ctx.Textures().ScaledByName("random-walking-monster", infoLegenIconSize), + Description: "Monster that walks randomly.", + }) + p.AddChild(&infoLegend{ + Icon: ctx.Textures().ScaledByName("chasing-monster", infoLegenIconSize), + Description: "Monster that walks towards you.", + }) + }) + return Center(&info{StackPanel: ui.StackPanel{ + ContainerBase: ui.ContainerBase{ + Children: []ui.Control{ + labelOpts("QBITTER", "title", labelOptions{TextAlignment: ui.AlignCenter}), + label("", "score"), // spacing + paragraphOpts(infoText, "score", labelOptions{TextAlignment: ui.AlignCenter}), + label("", "score"), // spacing + legend, + }, + }, + Orientation: ui.OrientationVertical, + }, app: app}) +} + +func (i *info) Handle(ctx ui.Context, e ui.Event) bool { + switch e := e.(type) { + case *ui.KeyDownEvent: + if e.Key == ui.KeyEscape || e.Key == ui.KeyEnter { + i.app.ShowMainMenu(ctx) + } + } + return i.ControlBase.Handle(ctx, e) +} diff --git a/cmd/tins2021/levelcontroller.go b/cmd/tins2021/levelcontroller.go index fdb8781..caecce3 100644 --- a/cmd/tins2021/levelcontroller.go +++ b/cmd/tins2021/levelcontroller.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "image" "image/color" "math/rand" "strconv" @@ -24,50 +23,22 @@ type levelController struct { Cubes cubeTexture Animations map[string]*tins2021.Animations - MonsterTextureNames map[tins2021.MonsterType]string - IdleMonsters *tins2021.Animations - MovingMonsters *tins2021.Animations + IdleMonsters *tins2021.Animations + MovingMonsters *tins2021.Animations SmallFont *tins2021.BitmapFont - Font *tins2021.BitmapFont } func newLevelControl(app *appContext, ctx ui.Context, level *tins2021.Level) *levelController { control := &levelController{app: app} textures := ctx.Textures() control.Cubes = newCubeTexture(textures, tins2021.Orange) - newAnimatedTexture(ctx, "star", "resources/images/star.png", func() image.Image { - return tins2021.AnimatePolygon(tins2021.CreateStar(5), tins2021.Yellow, tins2021.NewRotateAnimation(defaultAnimationFrames)) - }) - newAnimatedTexture(ctx, "heart", "resources/images/heart.png", func() image.Image { - return tins2021.AnimatePolygon(tins2021.CreateHeart(), tins2021.Red, tins2021.NewRotateAnimation(defaultAnimationFrames)) - }) - - control.MonsterTextureNames = map[tins2021.MonsterType]string{ - tins2021.MonsterTypeStraight: "straight-walking-monster", - tins2021.MonsterTypeRandom: "random-walking-monster", - tins2021.MonsterTypeChaser: "chasing-monster", - } - newAnimatedTexture(ctx, control.MonsterTextureNames[tins2021.MonsterTypeStraight], "resources/images/monster-straight.png", func() image.Image { - return tins2021.AnimatePolygon(tins2021.CreateHexagon(), tins2021.Green, tins2021.NewWobbleAnimation(defaultAnimationFrames, 30)) - }) - newAnimatedTexture(ctx, control.MonsterTextureNames[tins2021.MonsterTypeRandom], "resources/images/monster-random.png", func() image.Image { - return tins2021.AnimatePolygon(tins2021.CreateHexagon(), tins2021.Blue, tins2021.NewWobbleAnimation(defaultAnimationFrames, 30)) - }) - newAnimatedTexture(ctx, control.MonsterTextureNames[tins2021.MonsterTypeChaser], "resources/images/monster-chaser.png", func() image.Image { - return tins2021.AnimatePolygon(tins2021.CreateHexagon(), tins2021.Purple, tins2021.NewWobbleAnimation(defaultAnimationFrames, 30)) - }) small, err := tins2021.NewBitmapFont(ctx.Renderer(), ctx.Fonts().Font("small"), tins2021.NumericCharacters...) if err != nil { panic(err) } control.SmallFont = small - font, err := tins2021.NewBitmapFont(ctx.Renderer(), ctx.Fonts().Font("default"), tins2021.AllCharacters...) - if err != nil { - panic(err) - } - control.Font = font control.Play(level) @@ -183,7 +154,7 @@ func (r *levelController) Play(level *tins2021.Level) { for monster := range level.Monsters { r.IdleMonsters.Frame(monster) } - for _, monster := range r.MonsterTextureNames { + for _, monster := range r.app.MonsterTextureNames { r.Animations[monster] = tins2021.NewAnimations(80*time.Millisecond, defaultAnimationFrames, true, true) } } @@ -228,7 +199,7 @@ func (r levelController) Render(ctx ui.Context) { star := tins2021.NewAnimatedTexture(ctx.Textures().ScaledByName("star", scale*.4), defaultAnimationFrames) heart := tins2021.NewAnimatedTexture(ctx.Textures().ScaledByName("heart", scale*.4), defaultAnimationFrames) monsterTextures := map[tins2021.MonsterType]tins2021.AnimatedTexture{} - for typ, name := range r.MonsterTextureNames { + for typ, name := range r.app.MonsterTextureNames { monsterTextures[typ] = tins2021.NewAnimatedTexture(ctx.Textures().ScaledByName(name, scale*.4), defaultAnimationFrames) } propOffset := geom.PtF32(-.5*float32(star.Texture.Height()), -.8*float32(star.Texture.Height())) @@ -283,7 +254,7 @@ func (r levelController) Render(ctx ui.Context) { continue } _, platformPos := positionOfTile(pos, tile) - name := r.MonsterTextureNames[monsterType.Type()] + name := r.app.MonsterTextureNames[monsterType.Type()] monsterTextures[monsterType.Type()].Draw(renderer, platformPos.Add(propOffset), r.Animations[name].Frame(pos)) } diff --git a/cmd/tins2021/mainmenu.go b/cmd/tins2021/mainmenu.go index bd0bd57..2f377cd 100644 --- a/cmd/tins2021/mainmenu.go +++ b/cmd/tins2021/mainmenu.go @@ -33,18 +33,21 @@ type mainMenu struct { func newMainMenu(app *appContext, ctx ui.Context) ui.Control { menu := NewMenu() - // menu.Add("Info", func(ctx ui.Context) { app.ShowInfo(ctx) }) + menu.Add("Info", func(ctx ui.Context) { app.ShowInfo(ctx) }) menu.Add("Play", func(ctx ui.Context) { app.Play(ctx) }) menu.Add("Credits", func(ctx ui.Context) { app.ShowCredits(ctx) }) menu.Add("Quit", func(ctx ui.Context) { ctx.Quit() }) + menu.Activate(1) // play + return Center(&mainMenu{ StackPanel: ui.StackPanel{ ContainerBase: ui.ContainerBase{ Children: []ui.Control{ label("QBITTER", "title"), + label("", "default"), // spacing menu, }, }, diff --git a/cmd/tins2021/ui.go b/cmd/tins2021/ui.go index ab9d1a3..2c33465 100644 --- a/cmd/tins2021/ui.go +++ b/cmd/tins2021/ui.go @@ -24,3 +24,10 @@ func paragraph(text, font string) *ui.Paragraph { l.Font.Name = font }) } + +func paragraphOpts(text, font string, opts labelOptions) *ui.Paragraph { + return ui.BuildParagraph(text, func(l *ui.Paragraph) { + l.Font.Name = font + l.TextAlignment = opts.TextAlignment + }) +}