Added settings in-game.
Added video settings. Added and improved reusable controls. Separated drawing of sprites. Fixed bug that text was right aligned instead of left aligned.
This commit is contained in:
parent
27ad0453c3
commit
11ab3fca0f
@ -14,6 +14,12 @@ type Button struct {
|
|||||||
TextAlign allg5.HorizontalAlignment
|
TextAlign allg5.HorizontalAlignment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewButton(text string, onClick func()) *Button {
|
||||||
|
b := &Button{Text: text}
|
||||||
|
b.OnClick = onClick
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Button) DesiredSize(ctx *Context) geom.PointF32 {
|
func (b *Button) DesiredSize(ctx *Context) geom.PointF32 {
|
||||||
font := ctx.Fonts.Get(b.Font)
|
font := ctx.Fonts.Get(b.Font)
|
||||||
w := font.TextWidth(b.Text)
|
w := font.TextWidth(b.Text)
|
||||||
|
23
alui/center.go
Normal file
23
alui/center.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package alui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
)
|
||||||
|
|
||||||
|
type center struct {
|
||||||
|
Proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
func Center(control Control) Control {
|
||||||
|
return ¢er{Proxy: Proxy{Target: control}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *center) Layout(ctx *Context, bounds geom.RectangleF32) {
|
||||||
|
size := c.DesiredSize(ctx)
|
||||||
|
center := bounds.Center()
|
||||||
|
|
||||||
|
size.X = geom.Min32(size.X, bounds.Dx())
|
||||||
|
size.Y = geom.Min32(size.Y, bounds.Dy())
|
||||||
|
size = size.Mul(.5)
|
||||||
|
c.Proxy.Layout(ctx, geom.RectF32(center.X-size.X, center.Y-size.Y, center.X+size.X, center.Y+size.Y))
|
||||||
|
}
|
34
alui/column.go
Normal file
34
alui/column.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package alui
|
||||||
|
|
||||||
|
import "opslag.de/schobers/geom"
|
||||||
|
|
||||||
|
type Column struct {
|
||||||
|
Proxy
|
||||||
|
|
||||||
|
panel *StackPanel
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewColumn() *Column {
|
||||||
|
c := &Column{}
|
||||||
|
c.Init()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Column) AddChild(child ...Control) {
|
||||||
|
c.panel.Children = append(c.panel.Children, child...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Column) Init() {
|
||||||
|
c.panel = &StackPanel{Orientation: OrientationVertical}
|
||||||
|
c.Proxy.Target = c.panel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Column) Layout(ctx *Context, bounds geom.RectangleF32) {}
|
||||||
|
|
||||||
|
func (c *Column) Render(ctx *Context, bounds geom.RectangleF32) {
|
||||||
|
columnHeight := c.Proxy.DesiredSize(ctx).Y
|
||||||
|
width, center := bounds.Dx(), bounds.Center()
|
||||||
|
columnBounds := geom.RectF32(.25*width, center.Y-.5*columnHeight, .75*width, center.Y+.5*columnHeight)
|
||||||
|
c.Proxy.Layout(ctx, columnBounds)
|
||||||
|
c.Proxy.Render(ctx, columnBounds)
|
||||||
|
}
|
@ -11,11 +11,8 @@ type Container struct {
|
|||||||
Children []Control
|
Children []Control
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) Handle(e allg5.Event) {
|
func (c *Container) AddChild(child ...Control) {
|
||||||
c.ControlBase.Handle(e)
|
c.Children = append(c.Children, child...)
|
||||||
for _, child := range c.Children {
|
|
||||||
child.Handle(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) DesiredSize(ctx *Context) geom.PointF32 {
|
func (c *Container) DesiredSize(ctx *Context) geom.PointF32 {
|
||||||
@ -36,6 +33,13 @@ func (c *Container) DesiredSize(ctx *Context) geom.PointF32 {
|
|||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Container) Handle(e allg5.Event) {
|
||||||
|
c.ControlBase.Handle(e)
|
||||||
|
for _, child := range c.Children {
|
||||||
|
child.Handle(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Container) Layout(ctx *Context, bounds geom.RectangleF32) {
|
func (c *Container) Layout(ctx *Context, bounds geom.RectangleF32) {
|
||||||
c.ControlBase.Layout(ctx, bounds)
|
c.ControlBase.Layout(ctx, bounds)
|
||||||
for _, child := range c.Children {
|
for _, child := range c.Children {
|
||||||
|
@ -44,7 +44,7 @@ func (f *Fonts) DrawAlignFont(font *allg5.Font, left, top, right float32, color
|
|||||||
font.Draw(right, top, color, allg5.AlignRight, text)
|
font.Draw(right, top, color, allg5.AlignRight, text)
|
||||||
default:
|
default:
|
||||||
left, top = geom.Round32(left), geom.Round32(top)
|
left, top = geom.Round32(left), geom.Round32(top)
|
||||||
font.Draw(left, top, color, allg5.AlignRight, text)
|
font.Draw(left, top, color, allg5.AlignLeft, text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ type Label struct {
|
|||||||
ControlBase
|
ControlBase
|
||||||
|
|
||||||
Text string
|
Text string
|
||||||
|
TextAlign allg5.HorizontalAlignment
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Label) DesiredSize(ctx *Context) geom.PointF32 {
|
func (l *Label) DesiredSize(ctx *Context) geom.PointF32 {
|
||||||
@ -29,5 +30,5 @@ func (l *Label) Render(ctx *Context, bounds geom.RectangleF32) {
|
|||||||
if back != nil {
|
if back != nil {
|
||||||
allg5.DrawFilledRectangle(bounds.Min.X, bounds.Min.Y, bounds.Max.X, bounds.Max.Y, *back)
|
allg5.DrawFilledRectangle(bounds.Min.X, bounds.Min.Y, bounds.Max.X, bounds.Max.Y, *back)
|
||||||
}
|
}
|
||||||
ctx.Fonts.Draw(l.Font, bounds.Min.X+4, bounds.Min.Y+4, *fore, l.Text)
|
ctx.Fonts.DrawAlign(l.Font, bounds.Min.X+4, bounds.Min.Y+4, bounds.Max.X-4, *fore, l.TextAlign, l.Text)
|
||||||
}
|
}
|
||||||
|
23
alui/menu.go
23
alui/menu.go
@ -2,13 +2,11 @@ package alui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"opslag.de/schobers/allg5"
|
"opslag.de/schobers/allg5"
|
||||||
"opslag.de/schobers/geom"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Menu struct {
|
type Menu struct {
|
||||||
Proxy
|
Column
|
||||||
|
|
||||||
panel *StackPanel
|
|
||||||
active int
|
active int
|
||||||
buttons []*Button
|
buttons []*Button
|
||||||
}
|
}
|
||||||
@ -19,11 +17,6 @@ func NewMenu() *Menu {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Menu) Init() {
|
|
||||||
m.panel = &StackPanel{Orientation: OrientationVertical}
|
|
||||||
m.Proxy.Target = m.panel
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Menu) Activate(i int) {
|
func (m *Menu) Activate(i int) {
|
||||||
if len(m.buttons) == 0 || i < 0 {
|
if len(m.buttons) == 0 || i < 0 {
|
||||||
return
|
return
|
||||||
@ -42,11 +35,11 @@ func (m *Menu) Add(text string, onClick func()) {
|
|||||||
button.Over = true
|
button.Over = true
|
||||||
}
|
}
|
||||||
m.buttons = append(m.buttons, button)
|
m.buttons = append(m.buttons, button)
|
||||||
m.panel.Children = append(m.panel.Children, button)
|
m.AddChild(button)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Menu) Handle(e allg5.Event) {
|
func (m *Menu) Handle(e allg5.Event) {
|
||||||
m.Proxy.Handle(e)
|
m.Column.Handle(e)
|
||||||
|
|
||||||
if len(m.buttons) == 0 {
|
if len(m.buttons) == 0 {
|
||||||
return
|
return
|
||||||
@ -75,16 +68,6 @@ func (m *Menu) Handle(e allg5.Event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Menu) Layout(ctx *Context, bounds geom.RectangleF32) {}
|
|
||||||
|
|
||||||
func (m *Menu) Render(ctx *Context, bounds geom.RectangleF32) {
|
|
||||||
menuHeight := m.Proxy.DesiredSize(ctx).Y
|
|
||||||
width, center := bounds.Dx(), bounds.Center()
|
|
||||||
menuBounds := geom.RectF32(.25*width, center.Y-.5*menuHeight, .75*width, center.Y+.5*menuHeight)
|
|
||||||
m.Proxy.Layout(ctx, menuBounds)
|
|
||||||
m.Proxy.Render(ctx, menuBounds)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Menu) updateActiveButton(active int) {
|
func (m *Menu) updateActiveButton(active int) {
|
||||||
m.active = active
|
m.active = active
|
||||||
for i, btn := range m.buttons {
|
for i, btn := range m.buttons {
|
||||||
|
@ -13,10 +13,6 @@ type StackPanel struct {
|
|||||||
Orientation Orientation
|
Orientation Orientation
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StackPanel) Handle(e allg5.Event) {
|
|
||||||
s.Container.Handle(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StackPanel) asLength(p geom.PointF32) float32 {
|
func (s *StackPanel) asLength(p geom.PointF32) float32 {
|
||||||
switch s.Orientation {
|
switch s.Orientation {
|
||||||
case OrientationHorizontal:
|
case OrientationHorizontal:
|
||||||
@ -75,6 +71,10 @@ func (s *StackPanel) DesiredSize(ctx *Context) geom.PointF32 {
|
|||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StackPanel) Handle(e allg5.Event) {
|
||||||
|
s.Container.Handle(e)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StackPanel) Layout(ctx *Context, bounds geom.RectangleF32) {
|
func (s *StackPanel) Layout(ctx *Context, bounds geom.RectangleF32) {
|
||||||
s.Container.Layout(ctx, bounds)
|
s.Container.Layout(ctx, bounds)
|
||||||
|
|
||||||
|
317
cmd/krampus19/changesettings.go
Normal file
317
cmd/krampus19/changesettings.go
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"opslag.de/schobers/krampus19/gut"
|
||||||
|
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/krampus19/alui"
|
||||||
|
)
|
||||||
|
|
||||||
|
const margin = 8
|
||||||
|
|
||||||
|
type displayModeControl struct {
|
||||||
|
alui.StackPanel
|
||||||
|
|
||||||
|
label alui.Label
|
||||||
|
|
||||||
|
modes []string
|
||||||
|
current int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDisplayModeControl(ctx *Context) *displayModeControl {
|
||||||
|
var modes []string
|
||||||
|
fmtDisplayMode := func(width, height int) string { return fmt.Sprintf("%d x %d", width, height) }
|
||||||
|
containsDisplayMode := func(mode string) bool {
|
||||||
|
for _, m := range modes {
|
||||||
|
if m == mode {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
displayMode := ctx.Settings.Video.DisplayMode
|
||||||
|
if displayMode == "" {
|
||||||
|
displayMode = fmtDisplayMode(ctx.DisplaySize.X, ctx.DisplaySize.Y)
|
||||||
|
}
|
||||||
|
var current int
|
||||||
|
for _, m := range allg5.DisplayModes() {
|
||||||
|
mode := fmtDisplayMode(m.Width, m.Height)
|
||||||
|
if containsDisplayMode(mode) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if mode == displayMode {
|
||||||
|
current = len(modes)
|
||||||
|
}
|
||||||
|
modes = append(modes, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &displayModeControl{modes: modes, current: current}
|
||||||
|
c.Orientation = alui.OrientationHorizontal
|
||||||
|
c.label.TextAlign = allg5.AlignCenter
|
||||||
|
c.selectMode(0)
|
||||||
|
|
||||||
|
c.AddChild(newSpriteButton(ctx, "ui", "angle-left", func() { c.selectMode(-1) }))
|
||||||
|
c.AddChild(&c.label)
|
||||||
|
c.AddChild(newSpriteButton(ctx, "ui", "angle-right", func() { c.selectMode(1) }))
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *displayModeControl) selectMode(delta int) {
|
||||||
|
c.current = (c.current + delta + len(c.modes)) % len(c.modes)
|
||||||
|
c.label.Text = c.Mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *displayModeControl) Mode() string { return c.modes[c.current] }
|
||||||
|
|
||||||
|
type checkBox struct {
|
||||||
|
alui.Button
|
||||||
|
|
||||||
|
ctx *Context
|
||||||
|
|
||||||
|
Selected bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCheckBox(ctx *Context) *checkBox {
|
||||||
|
b := &checkBox{ctx: ctx}
|
||||||
|
b.OnClick = func() {
|
||||||
|
b.Selected = !b.Selected
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *checkBox) DesiredSize(ctx *alui.Context) geom.PointF32 {
|
||||||
|
return b.ctx.SpriteDrawer.Size("ui", "check-square").Add2D(2*margin, 2*margin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *checkBox) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||||
|
tint := b.ctx.Palette.Primary
|
||||||
|
if b.Over {
|
||||||
|
tint = b.ctx.Palette.Dark
|
||||||
|
ctx.Cursor = allg5.MouseCursorLink
|
||||||
|
}
|
||||||
|
var part = "square"
|
||||||
|
if b.Selected {
|
||||||
|
part = "check-square"
|
||||||
|
}
|
||||||
|
b.ctx.SpriteDrawer.Draw("ui", part, bounds.Center(), DrawSpriteOptions{Tint: &tint})
|
||||||
|
}
|
||||||
|
|
||||||
|
type spriteButton struct {
|
||||||
|
alui.Button
|
||||||
|
|
||||||
|
ctx *Context
|
||||||
|
|
||||||
|
Sprite string
|
||||||
|
Part string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSpriteButton(ctx *Context, sprite, part string, onClick func()) *spriteButton {
|
||||||
|
b := &spriteButton{ctx: ctx, Sprite: sprite, Part: part}
|
||||||
|
b.OnClick = onClick
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *spriteButton) DesiredSize(ctx *alui.Context) geom.PointF32 {
|
||||||
|
return b.ctx.SpriteDrawer.Size(b.Sprite, b.Part).Add2D(2*margin, 2*margin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *spriteButton) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||||
|
tint := b.ctx.Palette.Primary
|
||||||
|
if b.Over {
|
||||||
|
tint = b.ctx.Palette.Dark
|
||||||
|
ctx.Cursor = allg5.MouseCursorLink
|
||||||
|
}
|
||||||
|
b.ctx.SpriteDrawer.Draw(b.Sprite, b.Part, bounds.Center(), DrawSpriteOptions{Tint: &tint})
|
||||||
|
}
|
||||||
|
|
||||||
|
type selectKeyControl struct {
|
||||||
|
alui.Label
|
||||||
|
|
||||||
|
Key allg5.Key
|
||||||
|
|
||||||
|
WaitingForInput bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSelectKeyControl(key allg5.Key) *selectKeyControl {
|
||||||
|
c := &selectKeyControl{Key: key}
|
||||||
|
c.TextAlign = allg5.AlignCenter
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *selectKeyControl) Handle(e allg5.Event) {
|
||||||
|
c.Label.Handle(e)
|
||||||
|
|
||||||
|
switch e := e.(type) {
|
||||||
|
case *allg5.MouseButtonDownEvent:
|
||||||
|
if c.Over && e.Button == allg5.MouseButtonLeft {
|
||||||
|
c.WaitingForInput = true
|
||||||
|
}
|
||||||
|
case *allg5.KeyDownEvent:
|
||||||
|
if c.WaitingForInput {
|
||||||
|
if e.KeyCode != allg5.KeyEscape {
|
||||||
|
c.Key = e.KeyCode
|
||||||
|
}
|
||||||
|
c.WaitingForInput = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *selectKeyControl) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||||
|
if c.WaitingForInput {
|
||||||
|
c.Text = "_"
|
||||||
|
} else {
|
||||||
|
c.Text = gut.KeyToString(c.Key)
|
||||||
|
}
|
||||||
|
if c.Over {
|
||||||
|
ctx.Cursor = allg5.MouseCursorLink
|
||||||
|
}
|
||||||
|
c.Label.Render(ctx, bounds)
|
||||||
|
}
|
||||||
|
|
||||||
|
type settingsHeader struct {
|
||||||
|
alui.Label
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSettingsHeader(label string) alui.Control {
|
||||||
|
header := &settingsHeader{}
|
||||||
|
header.Text = label
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *settingsHeader) DesiredSize(ctx *alui.Context) geom.PointF32 {
|
||||||
|
size := h.Label.DesiredSize(ctx)
|
||||||
|
size.Y += 5 * margin
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *settingsHeader) Layout(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||||
|
var label = bounds
|
||||||
|
if label.Dy() > 5*margin {
|
||||||
|
label.Min.Y = bounds.Min.Y + 3*margin
|
||||||
|
label.Max.Y = bounds.Max.Y - 2*margin
|
||||||
|
}
|
||||||
|
h.Label.Layout(ctx, label)
|
||||||
|
}
|
||||||
|
|
||||||
|
type settingsRow struct {
|
||||||
|
alui.StackPanel
|
||||||
|
|
||||||
|
label *alui.Label
|
||||||
|
edit alui.Control
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSettingRow(label string, edit alui.Control) *settingsRow {
|
||||||
|
row := &settingsRow{}
|
||||||
|
row.Orientation = alui.OrientationHorizontal
|
||||||
|
row.label = &alui.Label{Text: label}
|
||||||
|
row.edit = edit
|
||||||
|
row.Children = append(row.Children, row.label, row.edit)
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *settingsRow) Layout(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||||
|
label := bounds
|
||||||
|
label.Max.X = label.Min.X + .5*label.Dx()
|
||||||
|
r.label.Layout(ctx, label)
|
||||||
|
|
||||||
|
width := r.edit.DesiredSize(ctx).X
|
||||||
|
edit := bounds
|
||||||
|
if geom.IsNaN32(width) || width > label.Dx() {
|
||||||
|
edit.Min.X = label.Max.X
|
||||||
|
} else {
|
||||||
|
margin := (label.Dx() - width) * .5
|
||||||
|
edit.Min.X = label.Max.X + margin
|
||||||
|
edit.Max.X -= margin
|
||||||
|
}
|
||||||
|
r.edit.Layout(ctx, edit)
|
||||||
|
}
|
||||||
|
|
||||||
|
type wideButton struct {
|
||||||
|
alui.Button
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWideButton(label string, onClick func()) *wideButton {
|
||||||
|
b := &wideButton{}
|
||||||
|
b.Text = label
|
||||||
|
b.TextAlign = allg5.AlignCenter
|
||||||
|
b.OnClick = onClick
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *wideButton) DesiredSize(ctx *alui.Context) geom.PointF32 {
|
||||||
|
size := b.Button.DesiredSize(ctx)
|
||||||
|
size.X += 2 * margin
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
type changeSettings struct {
|
||||||
|
alui.Column
|
||||||
|
|
||||||
|
ctx *Context
|
||||||
|
|
||||||
|
rows []*settingsRow
|
||||||
|
active int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *changeSettings) addRow(row ...*settingsRow) {
|
||||||
|
for _, row := range row {
|
||||||
|
s.AddChild(row)
|
||||||
|
s.rows = append(s.rows, row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *changeSettings) Enter(ctx *Context) error {
|
||||||
|
s.Init()
|
||||||
|
s.ctx = ctx
|
||||||
|
|
||||||
|
keyLeft := newSelectKeyControl(s.ctx.Settings.Controls.MoveLeft)
|
||||||
|
keyUp := newSelectKeyControl(s.ctx.Settings.Controls.MoveUp)
|
||||||
|
keyRight := newSelectKeyControl(s.ctx.Settings.Controls.MoveRight)
|
||||||
|
keyDown := newSelectKeyControl(s.ctx.Settings.Controls.MoveDown)
|
||||||
|
|
||||||
|
s.AddChild(newSettingsHeader("Controls"))
|
||||||
|
s.addRow(
|
||||||
|
newSettingRow("Key left", keyLeft),
|
||||||
|
newSettingRow("Key up", keyUp),
|
||||||
|
newSettingRow("Key right", keyRight),
|
||||||
|
newSettingRow("Key down", keyDown),
|
||||||
|
)
|
||||||
|
s.AddChild(newSettingsHeader("Video"))
|
||||||
|
|
||||||
|
displayMode := newDisplayModeControl(ctx)
|
||||||
|
windowed := newCheckBox(ctx)
|
||||||
|
windowed.Selected = s.ctx.Settings.Video.Windowed
|
||||||
|
|
||||||
|
s.addRow(
|
||||||
|
newSettingRow("Windowed", windowed),
|
||||||
|
newSettingRow("Resolution (fullscreen)", displayMode),
|
||||||
|
)
|
||||||
|
buttons := &alui.StackPanel{Orientation: alui.OrientationHorizontal}
|
||||||
|
buttons.AddChild(
|
||||||
|
newWideButton("Apply", func() {
|
||||||
|
var controls = controls{MoveLeft: keyLeft.Key, MoveUp: keyUp.Key, MoveRight: keyRight.Key, MoveDown: keyDown.Key}
|
||||||
|
var video = video{Windowed: windowed.Selected, DisplayMode: displayMode.Mode()}
|
||||||
|
s.ctx.Settings = settings{Controls: controls, Video: video}
|
||||||
|
err := s.ctx.Settings.StoreDefault()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("User settings are not stored: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Printf("Stored new settings.")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.ctx.Navigation.showMainMenu()
|
||||||
|
}),
|
||||||
|
newWideButton("Cancel", func() { s.ctx.Navigation.showMainMenu() }),
|
||||||
|
)
|
||||||
|
s.AddChild(alui.Center(buttons))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *changeSettings) Leave() {
|
||||||
|
}
|
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
"opslag.de/schobers/krampus19/alui"
|
"opslag.de/schobers/krampus19/alui"
|
||||||
|
|
||||||
"opslag.de/schobers/allg5"
|
"opslag.de/schobers/allg5"
|
||||||
@ -20,10 +21,12 @@ func newTexture(bmp *allg5.Bitmap) texture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
|
DisplaySize geom.Point
|
||||||
Resources vfs.CopyDir
|
Resources vfs.CopyDir
|
||||||
Textures map[string]texture
|
Textures map[string]texture
|
||||||
Levels map[string]level
|
Levels map[string]level
|
||||||
Sprites map[string]sprite
|
Sprites map[string]sprite
|
||||||
|
SpriteDrawer SpriteDrawer
|
||||||
Settings settings
|
Settings settings
|
||||||
Palette *alui.Palette
|
Palette *alui.Palette
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"opslag.de/schobers/allg5"
|
"opslag.de/schobers/allg5"
|
||||||
"opslag.de/schobers/fs/vfs"
|
"opslag.de/schobers/fs/vfs"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
"opslag.de/schobers/krampus19/alui"
|
"opslag.de/schobers/krampus19/alui"
|
||||||
"opslag.de/schobers/krampus19/gut"
|
"opslag.de/schobers/krampus19/gut"
|
||||||
)
|
)
|
||||||
@ -141,6 +142,8 @@ func (g *game) loadAssets() error {
|
|||||||
|
|
||||||
"tile_lava_brick.png": "lava_brick",
|
"tile_lava_brick.png": "lava_brick",
|
||||||
"tile_magma.png": "magma",
|
"tile_magma.png": "magma",
|
||||||
|
|
||||||
|
"ui.png": "ui",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -162,7 +165,7 @@ func (g *game) loadAssets() error {
|
|||||||
log.Printf("Loaded %d fonts.\n", g.ui.Fonts().Len())
|
log.Printf("Loaded %d fonts.\n", g.ui.Fonts().Len())
|
||||||
|
|
||||||
log.Println("Loading sprites")
|
log.Println("Loading sprites")
|
||||||
err = g.loadSprites("brick", "lava_brick", "magma", "main_character")
|
err = g.loadSprites("brick", "lava_brick", "magma", "main_character", "ui")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -175,9 +178,11 @@ func (g *game) Destroy() {
|
|||||||
g.ctx.Destroy()
|
g.ctx.Destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *game) Init(disp *allg5.Display, res vfs.CopyDir, cons *gut.Console, fps *gut.FPS) error {
|
func (g *game) Init(disp *allg5.Display, settings settings, res vfs.CopyDir, cons *gut.Console, fps *gut.FPS) error {
|
||||||
log.Print("Initializing game...")
|
log.Print("Initializing game...")
|
||||||
g.ctx = &Context{Resources: res, Textures: map[string]texture{}, Settings: newDefaultSettings(), Navigation: navigation{game: g}}
|
g.ctx = &Context{Resources: res, Textures: map[string]texture{}, Settings: settings, Navigation: navigation{game: g}}
|
||||||
|
g.ctx.DisplaySize = geom.Pt(disp.Width(), disp.Height())
|
||||||
|
g.ctx.SpriteDrawer.ctx = g.ctx
|
||||||
if err := g.initUI(disp, cons, fps); err != nil {
|
if err := g.initUI(disp, cons, fps); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
@ -10,6 +11,7 @@ import (
|
|||||||
"opslag.de/schobers/allg5"
|
"opslag.de/schobers/allg5"
|
||||||
"opslag.de/schobers/fs/ricefs"
|
"opslag.de/schobers/fs/ricefs"
|
||||||
"opslag.de/schobers/fs/vfs"
|
"opslag.de/schobers/fs/vfs"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
"opslag.de/schobers/krampus19/gut"
|
"opslag.de/schobers/krampus19/gut"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,14 +35,39 @@ func run() error {
|
|||||||
cons := &gut.Console{}
|
cons := &gut.Console{}
|
||||||
log.SetOutput(io.MultiWriter(log.Writer(), cons))
|
log.SetOutput(io.MultiWriter(log.Writer(), cons))
|
||||||
|
|
||||||
log.Printf("Initializing Allegro")
|
log.Printf("Initializing Allegro.")
|
||||||
err := allg5.Init(allg5.InitAll)
|
err := allg5.Init(allg5.InitAll)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Creating display")
|
settings := newDefaultSettings()
|
||||||
disp, err := allg5.NewDisplay(1440, 900, allg5.NewDisplayOptions{Maximized: false, Windowed: true, Resizable: true, Vsync: true})
|
err = settings.LoadDefault()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Unable to load settings, falling back on defaults.")
|
||||||
|
}
|
||||||
|
err = settings.StoreDefault()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Unable to store settings.")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Creating display.")
|
||||||
|
var size = geom.Pt(1280, 720)
|
||||||
|
dispOptions := allg5.NewDisplayOptions{Vsync: true}
|
||||||
|
if settings.Video.Windowed {
|
||||||
|
dispOptions.Maximized = true
|
||||||
|
dispOptions.Frameless = true
|
||||||
|
dispOptions.Windowed = true
|
||||||
|
} else {
|
||||||
|
dispOptions.Fullscreen = true
|
||||||
|
str := func(m allg5.DisplayMode) string { return fmt.Sprintf("%d x %d", m.Width, m.Height) }
|
||||||
|
for _, mode := range allg5.DisplayModes() {
|
||||||
|
if str(mode) == settings.Video.DisplayMode {
|
||||||
|
size = geom.Pt(mode.Width, mode.Height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disp, err := allg5.NewDisplay(size.X, size.Y, dispOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -66,7 +93,7 @@ func run() error {
|
|||||||
defer fps.Destroy()
|
defer fps.Destroy()
|
||||||
|
|
||||||
game := &game{}
|
game := &game{}
|
||||||
err = game.Init(disp, res, cons, fps)
|
err = game.Init(disp, settings, res, cons, fps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -94,6 +121,8 @@ func run() error {
|
|||||||
log.Printf("Stopping game loop, user pressed Alt+F4")
|
log.Printf("Stopping game loop, user pressed Alt+F4")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
case *allg5.DisplayResizeEvent:
|
||||||
|
game.ctx.DisplaySize = geom.Pt(disp.Width(), disp.Height())
|
||||||
}
|
}
|
||||||
game.Handle(e)
|
game.Handle(e)
|
||||||
e = eq.Get()
|
e = eq.Get()
|
||||||
|
@ -14,6 +14,7 @@ func (m *mainMenu) Enter(ctx *Context) error {
|
|||||||
m.ctx = ctx
|
m.ctx = ctx
|
||||||
m.Init()
|
m.Init()
|
||||||
m.Add("Play", func() { m.ctx.Navigation.playLevel("1") })
|
m.Add("Play", func() { m.ctx.Navigation.playLevel("1") })
|
||||||
|
m.Add("Settings", func() { m.ctx.Navigation.changeSettings() })
|
||||||
m.Add("Quit", func() { m.ctx.Navigation.quit() })
|
m.Add("Quit", func() { m.ctx.Navigation.quit() })
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,10 @@ type scene interface {
|
|||||||
Leave()
|
Leave()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *navigation) changeSettings() {
|
||||||
|
n.switchTo(&changeSettings{})
|
||||||
|
}
|
||||||
|
|
||||||
func (n *navigation) playLevel(l string) {
|
func (n *navigation) playLevel(l string) {
|
||||||
n.switchTo(&playLevel{name: l})
|
n.switchTo(&playLevel{name: l})
|
||||||
}
|
}
|
||||||
@ -34,6 +38,7 @@ func (n *navigation) switchTo(s scene) {
|
|||||||
}
|
}
|
||||||
n.curr = s
|
n.curr = s
|
||||||
n.game.scene.Proxy = s
|
n.game.scene.Proxy = s
|
||||||
|
n.game.scene.Visible = s != nil
|
||||||
if n.curr != nil {
|
if n.curr != nil {
|
||||||
n.curr.Enter(n.game.ctx)
|
n.curr.Enter(n.game.ctx)
|
||||||
}
|
}
|
||||||
|
@ -165,32 +165,12 @@ func (l *playLevel) Handle(e allg5.Event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *playLevel) drawSpritePart(name, partName string, pos geom.PointF32) {
|
func (l *playLevel) drawSprite(name, partName string, pos geom.PointF32) {
|
||||||
l.drawSpritePartOffset(name, partName, pos, 0)
|
l.drawSpritePart(name, partName, pos, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *playLevel) drawSpritePartOffset(name, partName string, pos geom.PointF32, z float32) {
|
func (l *playLevel) drawSpritePart(name, partName string, pos geom.PointF32, z float32) {
|
||||||
sprite, ok := l.ctx.Sprites[name]
|
l.ctx.SpriteDrawer.Draw(name, partName, l.posToScreenF32(pos, z), DrawSpriteOptions{Scale: l.scale})
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
text, ok := l.ctx.Textures[sprite.texture]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
partText, ok := text.Subs[partName]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
part := sprite.FindPartByName(partName)
|
|
||||||
scale := l.scale
|
|
||||||
if part.scale != 0 {
|
|
||||||
scale *= 1. / part.scale
|
|
||||||
}
|
|
||||||
anchor := part.sub.Min.Sub(part.anchor).ToF32().Mul(scale)
|
|
||||||
scrPos := l.posToScreenF32(pos, z).Add(anchor)
|
|
||||||
left, top := scrPos.X, scrPos.Y
|
|
||||||
partText.DrawOptions(left, top, allg5.DrawOptions{Scale: allg5.NewUniformScale(scale)})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
func (l *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||||
@ -200,15 +180,15 @@ func (l *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
|||||||
switch t {
|
switch t {
|
||||||
case tileBasic:
|
case tileBasic:
|
||||||
if l.state.IsNextToMagma(pos) {
|
if l.state.IsNextToMagma(pos) {
|
||||||
l.drawSpritePart("lava_brick", "magma", pos.ToF32())
|
l.drawSprite("lava_brick", "magma", pos.ToF32())
|
||||||
} else {
|
} else {
|
||||||
l.drawSpritePart("lava_brick", "lava_brick", pos.ToF32())
|
l.drawSprite("lava_brick", "lava_brick", pos.ToF32())
|
||||||
}
|
}
|
||||||
case tileMagma:
|
case tileMagma:
|
||||||
l.drawSpritePart("magma", "magma", pos.ToF32())
|
l.drawSprite("magma", "magma", pos.ToF32())
|
||||||
if l.state.IsFilledUp(pos) {
|
if l.state.IsFilledUp(pos) {
|
||||||
l.drawSpritePartOffset("brick", "brick", pos.ToF32(), 80)
|
l.drawSpritePart("brick", "brick", pos.ToF32(), 80)
|
||||||
l.drawSpritePart("magma", "sunken_overlay", pos.ToF32())
|
l.drawSprite("magma", "sunken_overlay", pos.ToF32())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,9 +204,9 @@ func (l *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
|||||||
for _, e := range entities {
|
for _, e := range entities {
|
||||||
switch e.typ {
|
switch e.typ {
|
||||||
case entityTypeCharacter:
|
case entityTypeCharacter:
|
||||||
l.drawSpritePart("main_character", "main_character", e.scr)
|
l.drawSprite("main_character", "main_character", e.scr)
|
||||||
case entityTypeBrick:
|
case entityTypeBrick:
|
||||||
l.drawSpritePart("brick", "brick", e.scr)
|
l.drawSprite("brick", "brick", e.scr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
32
cmd/krampus19/res/sprites/ui.txt
Normal file
32
cmd/krampus19/res/sprites/ui.txt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
sprite:
|
||||||
|
texture: ui
|
||||||
|
|
||||||
|
part:
|
||||||
|
name: square
|
||||||
|
sub_texture: 0,0,448,512
|
||||||
|
anchor: 224,256
|
||||||
|
scale: 16
|
||||||
|
:part
|
||||||
|
|
||||||
|
part:
|
||||||
|
name: check-square
|
||||||
|
sub_texture: 0,512,448,512
|
||||||
|
anchor: 224,768
|
||||||
|
scale: 16
|
||||||
|
:part
|
||||||
|
|
||||||
|
part:
|
||||||
|
name: angle-left
|
||||||
|
sub_texture: 768,0,192,512
|
||||||
|
anchor: 848,196
|
||||||
|
scale: 16
|
||||||
|
:part
|
||||||
|
|
||||||
|
part:
|
||||||
|
name: angle-right
|
||||||
|
sub_texture: 768,512,192,512
|
||||||
|
anchor: 848,720
|
||||||
|
scale: 16
|
||||||
|
:part
|
||||||
|
|
||||||
|
:sprite
|
BIN
cmd/krampus19/res/ui.png
Normal file
BIN
cmd/krampus19/res/ui.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
@ -1,10 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "opslag.de/schobers/allg5"
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
type settings struct {
|
"opslag.de/schobers/allg5"
|
||||||
Controls controls
|
)
|
||||||
}
|
|
||||||
|
|
||||||
func newDefaultSettings() settings {
|
func newDefaultSettings() settings {
|
||||||
return settings{
|
return settings{
|
||||||
@ -14,9 +16,71 @@ func newDefaultSettings() settings {
|
|||||||
MoveDown: allg5.KeyDown,
|
MoveDown: allg5.KeyDown,
|
||||||
MoveLeft: allg5.KeyLeft,
|
MoveLeft: allg5.KeyLeft,
|
||||||
},
|
},
|
||||||
|
Video: video{
|
||||||
|
Windowed: true,
|
||||||
|
DisplayMode: "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type settings struct {
|
||||||
|
Controls controls
|
||||||
|
Video video
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *settings) DefaultPath() (string, error) {
|
||||||
|
config, err := os.UserConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
dir := filepath.Join(config, "krampus19")
|
||||||
|
err = os.MkdirAll(dir, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Join(dir, "settings.json"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *settings) Load(path string) error {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
var fromFile settings
|
||||||
|
err = json.NewDecoder(f).Decode(&fromFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*s = fromFile
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *settings) LoadDefault() error {
|
||||||
|
path, err := s.DefaultPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.Load(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *settings) Store(path string) error {
|
||||||
|
f, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return json.NewEncoder(f).Encode(&s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *settings) StoreDefault() error {
|
||||||
|
path, err := s.DefaultPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.Store(path)
|
||||||
|
}
|
||||||
|
|
||||||
type controls struct {
|
type controls struct {
|
||||||
MoveUp allg5.Key
|
MoveUp allg5.Key
|
||||||
MoveRight allg5.Key
|
MoveRight allg5.Key
|
||||||
@ -24,6 +88,11 @@ type controls struct {
|
|||||||
MoveLeft allg5.Key
|
MoveLeft allg5.Key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type video struct {
|
||||||
|
Windowed bool
|
||||||
|
DisplayMode string
|
||||||
|
}
|
||||||
|
|
||||||
func (c controls) MovementKeys() []allg5.Key {
|
func (c controls) MovementKeys() []allg5.Key {
|
||||||
return []allg5.Key{c.MoveUp, c.MoveRight, c.MoveDown, c.MoveLeft}
|
return []allg5.Key{c.MoveUp, c.MoveRight, c.MoveDown, c.MoveLeft}
|
||||||
}
|
}
|
||||||
|
69
cmd/krampus19/spritedrawer.go
Normal file
69
cmd/krampus19/spritedrawer.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DrawSpriteOptions struct {
|
||||||
|
Scale float32
|
||||||
|
Tint *allg5.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
type SpriteDrawer struct {
|
||||||
|
ctx *Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d SpriteDrawer) scale(sprite, user float32) float32 {
|
||||||
|
var scale float32 = 1
|
||||||
|
if sprite != 0 {
|
||||||
|
scale *= 1 / sprite
|
||||||
|
}
|
||||||
|
if user != 0 {
|
||||||
|
scale *= user
|
||||||
|
}
|
||||||
|
return scale
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d SpriteDrawer) Draw(name, partName string, pos geom.PointF32, opts DrawSpriteOptions) bool {
|
||||||
|
sprite, ok := d.ctx.Sprites[name]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
text, ok := d.ctx.Textures[sprite.texture]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
partText, ok := text.Subs[partName]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
part := sprite.FindPartByName(partName)
|
||||||
|
scale := d.scale(part.scale, opts.Scale)
|
||||||
|
anchor := part.sub.Min.Sub(part.anchor).ToF32().Mul(scale)
|
||||||
|
scrPos := pos.Add(anchor)
|
||||||
|
left, top := scrPos.X, scrPos.Y
|
||||||
|
var drawOpts allg5.DrawOptions
|
||||||
|
if scale != 1 {
|
||||||
|
drawOpts.Scale = allg5.NewUniformScale(scale)
|
||||||
|
}
|
||||||
|
if opts.Tint != nil {
|
||||||
|
drawOpts.Tint = opts.Tint
|
||||||
|
}
|
||||||
|
partText.DrawOptions(left, top, drawOpts)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d SpriteDrawer) Size(name, partName string) geom.PointF32 {
|
||||||
|
return d.SizeScale(name, partName, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d SpriteDrawer) SizeScale(name, partName string, scale float32) geom.PointF32 {
|
||||||
|
sprite, ok := d.ctx.Sprites[name]
|
||||||
|
if !ok {
|
||||||
|
return geom.PointF32{}
|
||||||
|
}
|
||||||
|
part := sprite.FindPartByName(partName)
|
||||||
|
scale = d.scale(part.scale, scale)
|
||||||
|
return part.sub.Size().ToF32().Mul(scale)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user