Added support for changing controls.
Changed info screen slightly.
This commit is contained in:
parent
165d1fcd26
commit
000348339d
2
TODO.md
2
TODO.md
@ -1,7 +1,7 @@
|
||||
- [X] Increase difficulty.
|
||||
- [ ] Add music & sounds.
|
||||
- [ ] Keep score/difficulty level (resume & restart).
|
||||
- [ ] Explain controls on info page.
|
||||
- [X] ~~Explain controls on info page~~ add settings for controls.
|
||||
- [X] Fix usage of go/embed (and remove rice again).
|
||||
- [X] Add monster animations (~~jumping on tile &~~ towards new tile).
|
||||
- [ ] Scale icons (heart & star on right side) when playing.
|
||||
|
@ -29,7 +29,7 @@ func NewAnimatedTexture(texture NamedTexture, n int) AnimatedTexture {
|
||||
|
||||
func FitAnimatedTexture(texture NamedTexture, scale float32, n int) AnimatedTexture {
|
||||
height := float32(texture.Texture().Height())
|
||||
scale = geom.Round32(height*scale) / height // clip scale to integer width/height
|
||||
scale = ScaleRound(height, scale)
|
||||
return newAnimatedTexture(texture, texture.Scaled(scale), n)
|
||||
}
|
||||
|
||||
@ -44,3 +44,11 @@ func (t AnimatedTexture) Frames() int { return len(t.frames) }
|
||||
func (t AnimatedTexture) Scale(scale float32) AnimatedTexture {
|
||||
return FitAnimatedTexture(t.texture, scale, t.Frames())
|
||||
}
|
||||
|
||||
func FindScaleRound(length, desired float32) float32 {
|
||||
return ScaleRound(length, desired/length)
|
||||
}
|
||||
|
||||
func ScaleRound(length, scale float32) float32 {
|
||||
return geom.Round32(length*scale) / length
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ func (a *app) Init(ctx ui.Context) error {
|
||||
|
||||
ctx.Overlays().AddOnTop(fpsOverlayName, &play.FPS{Align: ui.AlignRight}, false)
|
||||
|
||||
a.context = newAppContext(ctx, func(control ui.Control) {
|
||||
a.context = newAppContext(ctx, a.settings, func(control ui.Control) {
|
||||
a.Content = control
|
||||
})
|
||||
a.context.ShowMainMenu(ctx)
|
||||
|
@ -8,7 +8,8 @@ import (
|
||||
type appContext struct {
|
||||
setView func(ui.Control)
|
||||
|
||||
Debug bool
|
||||
Settings *tins2021.Settings
|
||||
Debug bool
|
||||
|
||||
StarTexture tins2021.AnimatedTexture
|
||||
HeartTexture tins2021.AnimatedTexture
|
||||
@ -16,10 +17,11 @@ type appContext struct {
|
||||
MonsterTextures map[tins2021.MonsterType]tins2021.AnimatedTexture
|
||||
}
|
||||
|
||||
func newAppContext(ctx ui.Context, setView func(ui.Control)) *appContext {
|
||||
func newAppContext(ctx ui.Context, settings *tins2021.Settings, setView func(ui.Control)) *appContext {
|
||||
textures := textureGenerator{}
|
||||
app := &appContext{
|
||||
setView: setView,
|
||||
Settings: settings,
|
||||
StarTexture: newAnimatedTexture(ctx, "star", defaultAnimationFrames, textures.Star),
|
||||
HeartTexture: newAnimatedTexture(ctx, "heart", defaultAnimationFrames, textures.Heart),
|
||||
MonsterTextures: map[tins2021.MonsterType]tins2021.AnimatedTexture{
|
||||
@ -62,6 +64,10 @@ func (app *appContext) ShowCredits(ctx ui.Context) {
|
||||
app.setView(newCredits(app, ctx))
|
||||
}
|
||||
|
||||
func (app *appContext) ShowSettings(ctx ui.Context) {
|
||||
app.setView(newSettings(app, ctx))
|
||||
}
|
||||
|
||||
func (app *appContext) ShowInfo(ctx ui.Context) {
|
||||
app.setView(newInfo(app, ctx))
|
||||
}
|
||||
|
@ -6,14 +6,19 @@ import (
|
||||
"opslag.de/schobers/zntg/ui"
|
||||
)
|
||||
|
||||
const infoLegendIconSize = .36
|
||||
const infoLegendIconMargin = 4.
|
||||
const infoLegendSeparatorMargin = 24
|
||||
const infoText = "Qbitter is a game loosly based on the 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 is based on the works of M.C. Escher. \n\nIn the game you (represented as a gnome) have to collect stars while trying to avoid enemies (hexagons). Every level has increasing difficulty."
|
||||
|
||||
var infoLegendIconRectangle = geom.RectRelF32(0, 0, infoLegendIconSize*tins2021.TextureSize, infoLegendIconSize*tins2021.TextureSize)
|
||||
|
||||
type info struct {
|
||||
ui.StackPanel
|
||||
|
||||
app *appContext
|
||||
}
|
||||
|
||||
const infoText = "Qbitter is a game loosly based on the 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 is based on the works of M.C. Escher. \n\nIn 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
|
||||
|
||||
@ -22,44 +27,42 @@ type infoLegend struct {
|
||||
}
|
||||
|
||||
func (l *infoLegend) DesiredSize(ctx ui.Context, size geom.PointF32) geom.PointF32 {
|
||||
return geom.PtF32(geom.NaN32(), .4*tins2021.TextureSize)
|
||||
textureHeight := float32(l.Icon.Height())
|
||||
font := ctx.Fonts().Font("score")
|
||||
fontHeight := float32(font.Height())
|
||||
return geom.PtF32(textureHeight+infoLegendSeparatorMargin+font.WidthOf(l.Description), geom.Max32(textureHeight+2*infoLegendIconMargin, fontHeight))
|
||||
}
|
||||
|
||||
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})
|
||||
renderer.DrawTexturePointOptions(l.Icon, bounds.Min, 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)
|
||||
renderer.Text(font, bounds.Min.Add2D(textureHeight+infoLegendSeparatorMargin, .5*(textureHeight-fontHeight)+infoLegendIconMargin), 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),
|
||||
Icon: ctx.Textures().ScaledByName("star", infoLegendIconSize),
|
||||
Description: "Collect them to complete the level.",
|
||||
})
|
||||
p.AddChild(&infoLegend{
|
||||
Icon: ctx.Textures().ScaledByName("heart", infoLegenIconSize),
|
||||
Icon: ctx.Textures().ScaledByName("heart", infoLegendIconSize),
|
||||
Description: "Gives (back) a life.",
|
||||
})
|
||||
p.AddChild(&infoLegend{
|
||||
Icon: ctx.Textures().ScaledByName("straight-walking-monster", infoLegenIconSize),
|
||||
Icon: ctx.Textures().ScaledByName("straight-walking-monster", infoLegendIconSize),
|
||||
Description: "Monster that walks over a fixed diagonal.",
|
||||
})
|
||||
p.AddChild(&infoLegend{
|
||||
Icon: ctx.Textures().ScaledByName("random-walking-monster", infoLegenIconSize),
|
||||
Icon: ctx.Textures().ScaledByName("random-walking-monster", infoLegendIconSize),
|
||||
Description: "Monster that walks randomly.",
|
||||
})
|
||||
p.AddChild(&infoLegend{
|
||||
Icon: ctx.Textures().ScaledByName("chasing-monster", infoLegenIconSize),
|
||||
Icon: ctx.Textures().ScaledByName("chasing-monster", infoLegendIconSize),
|
||||
Description: "Monster that walks towards you.",
|
||||
})
|
||||
})
|
||||
@ -70,7 +73,7 @@ func newInfo(app *appContext, ctx ui.Context) ui.Control {
|
||||
label("", "score"), // spacing
|
||||
paragraphOpts(infoText, "score", labelOptions{TextAlignment: ui.AlignCenter}),
|
||||
label("", "score"), // spacing
|
||||
legend,
|
||||
Center(legend),
|
||||
},
|
||||
},
|
||||
Orientation: ui.OrientationVertical,
|
||||
|
@ -28,6 +28,8 @@ type levelController struct {
|
||||
MovingMonsters *tins2021.Animations
|
||||
|
||||
SmallFont *tins2021.BitmapFont
|
||||
|
||||
Controls map[ui.Key]tins2021.Direction
|
||||
}
|
||||
|
||||
func newLevelControl(app *appContext, ctx ui.Context, level *tins2021.Level) *levelController {
|
||||
@ -41,6 +43,37 @@ func newLevelControl(app *appContext, ctx ui.Context, level *tins2021.Level) *le
|
||||
panic(err)
|
||||
}
|
||||
control.SmallFont = small
|
||||
switch app.Settings.Controls.Type {
|
||||
case controlsTypeArrows:
|
||||
control.Controls = map[ui.Key]tins2021.Direction{
|
||||
ui.KeyUp: tins2021.DirectionUpLeft,
|
||||
ui.KeyLeft: tins2021.DirectionDownLeft,
|
||||
ui.KeyDown: tins2021.DirectionDownRight,
|
||||
ui.KeyRight: tins2021.DirectionUpRight,
|
||||
}
|
||||
case controlsTypeCustom:
|
||||
find := func(s string, defaultKey ui.Key) ui.Key {
|
||||
for key, setting := range supportedCustomKeys {
|
||||
if s == setting {
|
||||
return key
|
||||
}
|
||||
}
|
||||
return defaultKey
|
||||
}
|
||||
control.Controls = map[ui.Key]tins2021.Direction{
|
||||
find(app.Settings.Controls.MoveUpLeft, ui.KeyW): tins2021.DirectionUpLeft,
|
||||
find(app.Settings.Controls.MoveDownLeft, ui.KeyA): tins2021.DirectionDownLeft,
|
||||
find(app.Settings.Controls.MoveDownRight, ui.KeyS): tins2021.DirectionDownRight,
|
||||
find(app.Settings.Controls.MoveUpRight, ui.KeyD): tins2021.DirectionUpRight,
|
||||
}
|
||||
default:
|
||||
control.Controls = map[ui.Key]tins2021.Direction{
|
||||
ui.KeyW: tins2021.DirectionUpLeft,
|
||||
ui.KeyA: tins2021.DirectionDownLeft,
|
||||
ui.KeyS: tins2021.DirectionDownRight,
|
||||
ui.KeyD: tins2021.DirectionUpRight,
|
||||
}
|
||||
}
|
||||
|
||||
control.Play(level)
|
||||
|
||||
@ -77,15 +110,9 @@ func (r *levelController) Handle(ctx ui.Context, e ui.Event) bool {
|
||||
|
||||
switch e := e.(type) {
|
||||
case *ui.KeyDownEvent:
|
||||
switch e.Key {
|
||||
case ui.KeyW:
|
||||
r.Level.MovePlayer(tins2021.DirectionUpLeft)
|
||||
case ui.KeyD:
|
||||
r.Level.MovePlayer(tins2021.DirectionUpRight)
|
||||
case ui.KeyS:
|
||||
r.Level.MovePlayer(tins2021.DirectionDownRight)
|
||||
case ui.KeyA:
|
||||
r.Level.MovePlayer(tins2021.DirectionDownLeft)
|
||||
dir, ok := r.Controls[e.Key]
|
||||
if ok {
|
||||
r.Level.MovePlayer(dir)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,7 @@ func newMainMenu(app *appContext, ctx ui.Context) ui.Control {
|
||||
menu.Add("Play", func(ctx ui.Context) {
|
||||
app.Play(ctx)
|
||||
})
|
||||
menu.Add("Controls", func(ctx ui.Context) { app.ShowSettings(ctx) })
|
||||
menu.Add("Credits", func(ctx ui.Context) { app.ShowCredits(ctx) })
|
||||
menu.Add("Quit", func(ctx ui.Context) { ctx.Quit() })
|
||||
|
||||
|
347
cmd/tins2021/settings.go
Normal file
347
cmd/tins2021/settings.go
Normal file
@ -0,0 +1,347 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/golang/freetype/truetype"
|
||||
"github.com/llgcode/draw2d"
|
||||
"github.com/llgcode/draw2d/draw2dimg"
|
||||
"opslag.de/schobers/geom"
|
||||
"opslag.de/schobers/tins2021"
|
||||
"opslag.de/schobers/zntg"
|
||||
"opslag.de/schobers/zntg/ui"
|
||||
)
|
||||
|
||||
const keyboardKeyCornerRadius = .1 * keyboardKeyWidth
|
||||
const keyboardKeyHeight = .2 * keyboardLayoutTextureWidth
|
||||
const keyboardKeySkew = .15
|
||||
const keyboardKeyWidth = .25 * keyboardLayoutTextureWidth
|
||||
const keyboardLayoutTextureHeight = 256
|
||||
const keyboardLayoutTextureWidth = 2 * keyboardLayoutTextureHeight
|
||||
|
||||
func drawKey(ctx *draw2dimg.GraphicContext, font *truetype.Font, center geom.PointF, key rune, color color.Color) {
|
||||
const cornerRadius = keyboardKeyCornerRadius
|
||||
const keyHeight_5 = .5 * keyboardKeyHeight
|
||||
const keyWidth_5 = .5 * keyboardKeyWidth
|
||||
|
||||
skewed := func(x, y float64) (float64, float64) {
|
||||
x, y = skewedKeyboardCoordinates(x, y)
|
||||
return center.X + x, center.Y + y
|
||||
}
|
||||
corner := func(x, y, start float64) {
|
||||
for a := start; a <= start+.25; a += .025 {
|
||||
aa := a * 2 * geom.Pi
|
||||
ctx.LineTo(skewed(x+cornerRadius*geom.Cos(aa), y-cornerRadius*geom.Sin(aa)))
|
||||
}
|
||||
}
|
||||
|
||||
ctx.SetLineWidth(3)
|
||||
ctx.SetStrokeColor(color)
|
||||
ctx.SetFillColor(color)
|
||||
ctx.MoveTo(skewed(-keyWidth_5+cornerRadius, keyHeight_5))
|
||||
ctx.LineTo(skewed(keyWidth_5-cornerRadius, keyHeight_5))
|
||||
corner(keyWidth_5-cornerRadius, keyHeight_5-cornerRadius, .75)
|
||||
ctx.LineTo(skewed(keyWidth_5, -keyHeight_5+cornerRadius))
|
||||
corner(keyWidth_5-cornerRadius, -keyHeight_5+cornerRadius, 0)
|
||||
ctx.LineTo(skewed(-keyWidth_5+cornerRadius, -keyHeight_5))
|
||||
corner(-keyWidth_5+cornerRadius, -keyHeight_5+cornerRadius, .25)
|
||||
ctx.LineTo(skewed(-keyWidth_5, keyHeight_5-cornerRadius))
|
||||
corner(-keyWidth_5+cornerRadius, keyHeight_5-cornerRadius, .5)
|
||||
ctx.Close()
|
||||
ctx.Stroke()
|
||||
|
||||
ctx.FontCache = fontCache{font}
|
||||
ctx.SetFont(font)
|
||||
ctx.SetFontSize(keyHeight_5)
|
||||
|
||||
text := fmt.Sprintf("%c", key)
|
||||
textLeft, textTop, textRight, textBottom := ctx.GetStringBounds(text)
|
||||
textX, textY := skewed(-.5*(textRight-textLeft), .5*(textBottom-textTop))
|
||||
ctx.FillStringAt(text, textX, textY)
|
||||
}
|
||||
|
||||
type fontCache struct{ *truetype.Font }
|
||||
|
||||
func (f fontCache) Load(draw2d.FontData) (*truetype.Font, error) { return f.Font, nil }
|
||||
func (fontCache) Store(draw2d.FontData, *truetype.Font) {}
|
||||
|
||||
func generateArrowKeys(resources ui.Resources) image.Image {
|
||||
return generateKeys(resources,
|
||||
keyboardLayoutKey{Position: geom.PtF(.53, .25), Key: '↑'},
|
||||
keyboardLayoutKey{Position: geom.PtF(.2, .75), Key: '←'},
|
||||
keyboardLayoutKey{Position: geom.PtF(.5, .75), Key: '↓'},
|
||||
keyboardLayoutKey{Position: geom.PtF(.8, .75), Key: '→'},
|
||||
)
|
||||
}
|
||||
|
||||
func generateArrowKeysHighlight(resources ui.Resources, highlight [4]bool) image.Image {
|
||||
spaceOrRune := func(r rune, space bool) rune {
|
||||
if space {
|
||||
return ' '
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
return generateKeys(resources,
|
||||
keyboardLayoutKey{Position: geom.PtF(.45, .25), Key: spaceOrRune('↑', !highlight[0]), Highlight: highlight[0]},
|
||||
keyboardLayoutKey{Position: geom.PtF(.2, .75), Key: spaceOrRune('←', !highlight[1]), Highlight: highlight[1]},
|
||||
keyboardLayoutKey{Position: geom.PtF(.5, .75), Key: spaceOrRune('↓', !highlight[2]), Highlight: highlight[2]},
|
||||
keyboardLayoutKey{Position: geom.PtF(.8, .75), Key: spaceOrRune('→', !highlight[3]), Highlight: highlight[3]},
|
||||
)
|
||||
}
|
||||
|
||||
func generateCustomKeys(resources ui.Resources, keys [4]rune) image.Image {
|
||||
return generateKeys(resources,
|
||||
keyboardLayoutKey{Position: geom.PtF(.45, .25), Key: keys[0]},
|
||||
keyboardLayoutKey{Position: geom.PtF(.2, .75), Key: keys[1]},
|
||||
keyboardLayoutKey{Position: geom.PtF(.5, .75), Key: keys[2]},
|
||||
keyboardLayoutKey{Position: geom.PtF(.8, .75), Key: keys[3]},
|
||||
)
|
||||
}
|
||||
|
||||
func generateKeys(resources ui.Resources, keys ...keyboardLayoutKey) image.Image {
|
||||
im := image.NewRGBA(image.Rect(0, 0, keyboardLayoutTextureWidth, keyboardLayoutTextureHeight))
|
||||
ctx := draw2dimg.NewGraphicContext(im)
|
||||
|
||||
font, err := parseFont(resources)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
var color color.Color = color.White
|
||||
if key.Highlight {
|
||||
color = zntg.MustHexColor(tins2021.Orange)
|
||||
}
|
||||
center := geom.PtF(key.Position.X*keyboardLayoutTextureWidth, key.Position.Y*keyboardLayoutTextureHeight)
|
||||
drawKey(ctx, font, center, key.Key, color)
|
||||
}
|
||||
|
||||
return im
|
||||
}
|
||||
|
||||
func generateWASDKeys(resources ui.Resources) image.Image {
|
||||
return generateKeys(resources,
|
||||
keyboardLayoutKey{Position: geom.PtF(.45, .25), Key: 'W'},
|
||||
keyboardLayoutKey{Position: geom.PtF(.2, .75), Key: 'A'},
|
||||
keyboardLayoutKey{Position: geom.PtF(.5, .75), Key: 'S'},
|
||||
keyboardLayoutKey{Position: geom.PtF(.8, .75), Key: 'D'},
|
||||
)
|
||||
}
|
||||
|
||||
type keyboardLayoutKey struct {
|
||||
Position geom.PointF
|
||||
Key rune
|
||||
Highlight bool
|
||||
}
|
||||
|
||||
func parseFont(resources ui.Resources) (*truetype.Font, error) {
|
||||
ttf, err := resources.OpenResource("resources/fonts/FiraMono-Regular.ttf")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ttf.Close()
|
||||
data, err := ioutil.ReadAll(ttf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return truetype.Parse(data)
|
||||
}
|
||||
|
||||
type settings struct {
|
||||
ui.StackPanel
|
||||
|
||||
app *appContext
|
||||
|
||||
ActiveLayout int
|
||||
SelectedLayout int
|
||||
|
||||
SelectingCustom int
|
||||
}
|
||||
|
||||
const (
|
||||
controlsTypeWASD = "wasd"
|
||||
controlsTypeArrows = "arrows"
|
||||
controlsTypeCustom = "custom"
|
||||
)
|
||||
|
||||
func newSettings(app *appContext, ctx ui.Context) *settings {
|
||||
ctx.Textures().CreateTextureGo("layout-wasd", generateWASDKeys(ctx.Resources()), true)
|
||||
ctx.Textures().CreateTextureGo("layout-arrows", generateArrowKeys(ctx.Resources()), true)
|
||||
ctx.Textures().CreateTextureGo("layout-select-1", generateArrowKeysHighlight(ctx.Resources(), [4]bool{true, false, false, false}), true)
|
||||
ctx.Textures().CreateTextureGo("layout-select-2", generateArrowKeysHighlight(ctx.Resources(), [4]bool{false, true, false, false}), true)
|
||||
ctx.Textures().CreateTextureGo("layout-select-3", generateArrowKeysHighlight(ctx.Resources(), [4]bool{false, false, true, false}), true)
|
||||
ctx.Textures().CreateTextureGo("layout-select-4", generateArrowKeysHighlight(ctx.Resources(), [4]bool{false, false, false, true}), true)
|
||||
|
||||
var layout int
|
||||
switch app.Settings.Controls.Type {
|
||||
case controlsTypeArrows:
|
||||
layout = 1
|
||||
case controlsTypeCustom:
|
||||
layout = 2
|
||||
}
|
||||
|
||||
settings := &settings{app: app, ActiveLayout: layout, SelectedLayout: layout}
|
||||
settings.renderCustomLayout(ctx)
|
||||
return settings
|
||||
}
|
||||
|
||||
var supportedCustomKeys = map[ui.Key]string{
|
||||
ui.KeyA: "A",
|
||||
ui.KeyB: "B",
|
||||
ui.KeyC: "C",
|
||||
ui.KeyD: "D",
|
||||
ui.KeyE: "E",
|
||||
ui.KeyF: "F",
|
||||
ui.KeyG: "G",
|
||||
ui.KeyH: "H",
|
||||
ui.KeyI: "I",
|
||||
ui.KeyJ: "J",
|
||||
ui.KeyK: "K",
|
||||
ui.KeyL: "L",
|
||||
ui.KeyM: "M",
|
||||
ui.KeyN: "N",
|
||||
ui.KeyO: "O",
|
||||
ui.KeyP: "P",
|
||||
ui.KeyQ: "Q",
|
||||
ui.KeyR: "R",
|
||||
ui.KeyS: "S",
|
||||
ui.KeyT: "T",
|
||||
ui.KeyU: "U",
|
||||
ui.KeyV: "V",
|
||||
ui.KeyW: "W",
|
||||
ui.KeyX: "X",
|
||||
ui.KeyY: "Y",
|
||||
ui.KeyZ: "Z",
|
||||
}
|
||||
|
||||
func (s *settings) Handle(ctx ui.Context, e ui.Event) bool {
|
||||
if s.StackPanel.Handle(ctx, e) {
|
||||
return true
|
||||
}
|
||||
switch e := e.(type) {
|
||||
case *ui.KeyDownEvent:
|
||||
if s.SelectingCustom > 0 {
|
||||
switch e.Key {
|
||||
case ui.KeyEscape:
|
||||
s.SelectingCustom = 0
|
||||
return true
|
||||
}
|
||||
key, ok := supportedCustomKeys[e.Key]
|
||||
if ok {
|
||||
switch s.SelectingCustom {
|
||||
case 1:
|
||||
s.app.Settings.Controls.MoveUpLeft = key
|
||||
case 2:
|
||||
s.app.Settings.Controls.MoveDownLeft = key
|
||||
case 3:
|
||||
s.app.Settings.Controls.MoveDownRight = key
|
||||
case 4:
|
||||
s.app.Settings.Controls.MoveUpRight = key
|
||||
}
|
||||
s.renderCustomLayout(ctx)
|
||||
|
||||
s.SelectingCustom++
|
||||
if s.SelectingCustom == 5 {
|
||||
s.SelectingCustom = 0
|
||||
s.SelectedLayout = 2
|
||||
s.app.Settings.Controls.Type = controlsTypeCustom
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
switch e.Key {
|
||||
case ui.KeyEscape:
|
||||
s.app.ShowMainMenu(ctx)
|
||||
return true
|
||||
case ui.KeyLeft:
|
||||
s.ActiveLayout = (s.ActiveLayout + 2) % 3
|
||||
case ui.KeyRight:
|
||||
s.ActiveLayout = (s.ActiveLayout + 1) % 3
|
||||
case ui.KeyEnter:
|
||||
switch s.ActiveLayout {
|
||||
case 0:
|
||||
s.SelectedLayout = 0
|
||||
s.app.Settings.Controls.Type = controlsTypeWASD
|
||||
case 1:
|
||||
s.SelectedLayout = 1
|
||||
s.app.Settings.Controls.Type = controlsTypeArrows
|
||||
case 2:
|
||||
s.SelectingCustom = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *settings) Render(ctx ui.Context) {
|
||||
bounds := s.Bounds()
|
||||
center := bounds.Center()
|
||||
width := bounds.Dx()
|
||||
renderer := ctx.Renderer()
|
||||
|
||||
scale := tins2021.FindScaleRound(keyboardLayoutTextureWidth, .28*width)
|
||||
|
||||
font := ctx.Fonts().Font("default")
|
||||
|
||||
layouts := []string{
|
||||
"WASD",
|
||||
"ARROWS",
|
||||
"CUSTOM",
|
||||
}
|
||||
layoutTextures := []string{"layout-wasd", "layout-arrows", "layout-custom"}
|
||||
|
||||
normalColor := ctx.Style().Palette.Text
|
||||
highlightColor := ctx.Style().Palette.Primary
|
||||
|
||||
for i, layout := range layouts {
|
||||
layoutLeft := (.04 + .32*float32(i)) * width
|
||||
layoutCenter := layoutLeft + .14*width
|
||||
|
||||
textColor := normalColor
|
||||
layoutColor := normalColor
|
||||
if s.ActiveLayout == i {
|
||||
textColor = highlightColor
|
||||
}
|
||||
if s.SelectedLayout == i {
|
||||
layoutColor = highlightColor
|
||||
}
|
||||
|
||||
renderer.TextAlign(font, geom.PtF32(layoutCenter, center.Y-2*font.Height()), textColor, layout, ui.AlignCenter)
|
||||
renderer.DrawTexturePointOptions(ctx.Textures().ScaledByName(layoutTextures[i], scale), geom.PtF32(layoutLeft, center.Y), ui.DrawOptions{Tint: layoutColor})
|
||||
}
|
||||
|
||||
if s.SelectingCustom > 0 {
|
||||
renderer.FillRectangle(bounds, zntg.MustHexColor(`#000000CF`))
|
||||
|
||||
selectTexture := fmt.Sprintf("layout-select-%d", s.SelectingCustom)
|
||||
|
||||
layoutLeft := .36 * width
|
||||
layoutCenter := layoutLeft + .14*width
|
||||
renderer.TextAlign(font, geom.PtF32(layoutCenter, center.Y-2*font.Height()), normalColor, "PRESS KEY TO ASSIGN", ui.AlignCenter)
|
||||
renderer.DrawTexturePoint(ctx.Textures().ScaledByName(selectTexture, scale), geom.PtF32(layoutLeft, center.Y))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *settings) renderCustomLayout(ctx ui.Context) {
|
||||
runeOrQuestionMark := func(s string) rune {
|
||||
if len(s) == 0 {
|
||||
return '?'
|
||||
}
|
||||
return []rune(s)[0]
|
||||
}
|
||||
customKeys := [4]rune{
|
||||
runeOrQuestionMark(s.app.Settings.Controls.MoveUpLeft),
|
||||
runeOrQuestionMark(s.app.Settings.Controls.MoveDownLeft),
|
||||
runeOrQuestionMark(s.app.Settings.Controls.MoveDownRight),
|
||||
runeOrQuestionMark(s.app.Settings.Controls.MoveUpRight),
|
||||
}
|
||||
ctx.Textures().CreateTextureGo("layout-custom", generateCustomKeys(ctx.Resources(), customKeys), true)
|
||||
}
|
||||
|
||||
func skewedKeyboardCoordinates(x, y float64) (float64, float64) {
|
||||
return x - keyboardKeySkew*y, y
|
||||
}
|
11
settings.go
11
settings.go
@ -8,7 +8,8 @@ import (
|
||||
)
|
||||
|
||||
type Settings struct {
|
||||
Window WindowSettings
|
||||
Controls ControlsSettings
|
||||
Window WindowSettings
|
||||
}
|
||||
|
||||
func SettingsPath() (string, error) {
|
||||
@ -34,6 +35,14 @@ func (s *Settings) Store() error {
|
||||
return zntg.EncodeJSON(path, s)
|
||||
}
|
||||
|
||||
type ControlsSettings struct {
|
||||
Type string
|
||||
MoveDownRight string
|
||||
MoveDownLeft string
|
||||
MoveUpLeft string
|
||||
MoveUpRight string
|
||||
}
|
||||
|
||||
type WindowSettings struct {
|
||||
Location *geom.Point
|
||||
Size *geom.Point
|
||||
|
Loading…
Reference in New Issue
Block a user