diff --git a/common/control/v1/mouse.go b/common/control/v1/mouse.go new file mode 100644 index 0000000..73e5121 --- /dev/null +++ b/common/control/v1/mouse.go @@ -0,0 +1,8 @@ +package control + +const ( + MouseLeft MouseButton = iota + MouseRight +) + +type MouseButton int diff --git a/common/elements/v1/blocks/block.go b/common/elements/v1/blocks/block.go new file mode 100644 index 0000000..ade548e --- /dev/null +++ b/common/elements/v1/blocks/block.go @@ -0,0 +1,58 @@ +package blocks + +import ( + "image/color" + + "git.vezzani.net/ben/games/common/elements/v1" + "git.vezzani.net/ben/games/common/elements/v1/mouse" + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/vector" +) + +func New(ops ...Option) elements.ElementFunc { + return func(d elements.Bounds) elements.Element { + b := Block{ + ContainerBounds: d, + } + for i := range ops { + ops[i](&b) + } + return &b + } +} + +type Block struct { + ContainerBounds elements.Bounds + backgroundColor *color.Color + mouse.NopHandler + width float64 + height float64 + name string +} + +func (b *Block) Size() (w, h float64) { + w, h = b.ContainerBounds.Width, b.ContainerBounds.Height + if b.width != 0 { + w = b.width + } + if b.height != 0 { + h = b.height + } + return +} + +func (b *Block) Draw(image *ebiten.Image) (w, h float64) { + w, h = b.Size() + if b.backgroundColor != nil { + vector.DrawFilledRect( + image, + float32(b.ContainerBounds.Min.X), + float32(b.ContainerBounds.Min.Y), + float32(w), + float32(h), + *b.backgroundColor, + true, + ) + } + return +} diff --git a/common/elements/v1/blocks/options.go b/common/elements/v1/blocks/options.go new file mode 100644 index 0000000..352353c --- /dev/null +++ b/common/elements/v1/blocks/options.go @@ -0,0 +1,23 @@ +package blocks + +import "image/color" + +type Option func(*Block) + +func Size(w, h float64) Option { + return func(b *Block) { + b.width, b.height = w, h + } +} + +func BackgroundColor(c color.Color) Option { + return func(b *Block) { + b.backgroundColor = &c + } +} + +func Name(n string) Option { + return func(b *Block) { + b.name = n + } +} diff --git a/common/elements/v1/button.go b/common/elements/v1/button.go new file mode 100644 index 0000000..e59a809 --- /dev/null +++ b/common/elements/v1/button.go @@ -0,0 +1,70 @@ +package elements + +// +//import ( +// "image/color" +//) +// +//type xAlign int +//type yAlign int +// +//const ( +// AlignCente xAlign = iota +// AlignLeft +// AlignRight +//) +// +//const ( +// AlignCenter yAlign = iota +// AlignTop +// AlignBottom +//) +// +//type Button struct { +// mouseHandler +// block +// Label string +// OnClick func() error +// OnRightClick func() error +// Style struct { +// MouseDownColor *color.Color +// } +//} +// +//func (b *Button) HandleClick(_ MouseState) error { +// if b.OnClick == nil { +// return nil +// } +// return b.OnClick() +//} +// +// +//func (b *Button) getFont() font.Face { +// if b.block.Font == nil { +// return ux.FontFace +// } +// +// return *b.block.block.Font +//} +// +//func (b *Button) backgroundColor() color.Color { +// var c *color.Color +// if b.block.block.BackgroundColor != nil { +// c = b.block.block.BackgroundColor +// } else { +// c = &ux.BackgroundColor +// } +// +// if (b.mouseState.RightDown || b.mouseState.LeftDown) && b.Style.MouseDownColor != nil { +// c = b.Style.MouseDownColor +// } +// +// return *c +//} +// +//func (b *Button) Draw(ctx context.Context, image *ebiten.Image) error { +// +// vector.StrokeRect(image, xz, yz, w, h, 1, b.backgroundColor(), true) +// +// return nil +//} diff --git a/common/elements/v1/element.go b/common/elements/v1/element.go new file mode 100644 index 0000000..278fd90 --- /dev/null +++ b/common/elements/v1/element.go @@ -0,0 +1,29 @@ +package elements + +import ( + "git.vezzani.net/ben/games/common/elements/v1/mouse" + "github.com/hajimehoshi/ebiten/v2" +) + +type ElementFunc func(Bounds) Element + +type Clickable interface { +} + +type Mouseable interface { +} + +type Element interface { + HandleMouseEvent(s mouse.State) bool + + Draw(image *ebiten.Image) (w, h float64) +} + +type Point struct { + X, Y float64 +} + +type Bounds struct { + Min Point + Width, Height float64 +} diff --git a/common/elements/v1/menu.go b/common/elements/v1/menu.go new file mode 100644 index 0000000..8a64ab6 --- /dev/null +++ b/common/elements/v1/menu.go @@ -0,0 +1 @@ +package elements diff --git a/common/elements/v1/mouse/mouse.go b/common/elements/v1/mouse/mouse.go new file mode 100644 index 0000000..4a61d1d --- /dev/null +++ b/common/elements/v1/mouse/mouse.go @@ -0,0 +1,64 @@ +package mouse + +import ( + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/inpututil" +) + +type NopHandler struct{} + +func (n NopHandler) HandleMouseEvent(s State) bool { + return false +} + +type State struct { + X, Y int + LeftDown, RightDown bool + LeftChanged, RightChanged bool + LeftClicked, RightClicked bool + Clicked bool +} + +//func ClickHandler(e elements.Element) func() bool { +// newState := StateBuilder() +// return func() bool { +// _ = newState() +// if +// } +//} + +func StateBuilder() func() State { + prevState := State{} + state := State{} + return func() State { + state = prevState + state.X, state.Y = ebiten.CursorPosition() + + if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) { + state.LeftDown = true + } + if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonRight) { + state.RightDown = true + } + if inpututil.IsMouseButtonJustReleased(ebiten.MouseButtonLeft) { + state.LeftDown = false + if !state.RightDown { + state.Clicked = true + } + } + if inpututil.IsMouseButtonJustReleased(ebiten.MouseButtonRight) { + state.RightDown = false + if !state.LeftDown { + state.Clicked = true + } + } + + state.Clicked = state.Clicked && !prevState.Clicked + + state.LeftChanged = state.LeftDown != prevState.LeftDown + state.RightChanged = state.RightDown != prevState.RightDown + + prevState = state + return state + } +} diff --git a/common/elements/v1/stacks/options.go b/common/elements/v1/stacks/options.go new file mode 100644 index 0000000..8ed006b --- /dev/null +++ b/common/elements/v1/stacks/options.go @@ -0,0 +1,26 @@ +package stacks + +import ( + "git.vezzani.net/ben/games/common/elements/v1" + "git.vezzani.net/ben/games/common/elements/v1/blocks" +) + +type Option func(*Stack) + +func BlockOpt(o blocks.Option) Option { + return func(s *Stack) { + o(&s.Block) + } +} + +func Children(children ...elements.ElementFunc) Option { + return func(s *Stack) { + s.children = children + } +} + +func Horizontal() Option { + return func(s *Stack) { + s.horizontal = true + } +} diff --git a/common/elements/v1/stacks/stack.go b/common/elements/v1/stacks/stack.go new file mode 100644 index 0000000..012ffe4 --- /dev/null +++ b/common/elements/v1/stacks/stack.go @@ -0,0 +1,51 @@ +package stacks + +import ( + "git.vezzani.net/ben/games/common/elements/v1" + "git.vezzani.net/ben/games/common/elements/v1/blocks" + "git.vezzani.net/ben/games/common/elements/v1/mouse" + + "github.com/hajimehoshi/ebiten/v2" +) + +func New(ops ...Option) elements.ElementFunc { + return func(d elements.Bounds) elements.Element { + s := Stack{ + Block: blocks.Block{ContainerBounds: d}, + } + for op := range ops { + ops[op](&s) + } + return &s + } +} + +type Stack struct { + blocks.Block + mouse.NopHandler + horizontal bool + children []elements.ElementFunc +} + +func (s *Stack) Draw(image *ebiten.Image) (w, h float64) { + s.Block.Draw(image) + + d := s.ContainerBounds + d.Width, d.Height = s.Block.Size() + if s.horizontal { + d.Width /= float64(len(s.children)) + } else { + d.Height /= float64(len(s.children)) + } + + var offsetX, offsetY float64 + for i := range s.children { + offsetX, offsetY = s.children[i](d).Draw(image) + if s.horizontal { + d.Min.X += offsetX + } else { + d.Min.Y += offsetY + } + } + return s.Block.Size() +} diff --git a/common/elements/v1/table.go b/common/elements/v1/table.go new file mode 100644 index 0000000..4250cef --- /dev/null +++ b/common/elements/v1/table.go @@ -0,0 +1,93 @@ +package elements + +// +//import ( +// "context" +// "fmt" +// +// "github.com/hajimehoshi/ebiten/v2" +//) +// +//type Table struct { +// mouseHandler +// block +// +// ColumnCount int +// RowCount int +// +// Cells []Bounds +// +// Style struct { +// } +// +// mouseOverCell int +//} +// +//func (t *Table) HandleClick(ctx context.Context, s MouseState) error { +// address := t.getAddressUnderMouse() +// if address < 0 || address >= len(t.Cells) { +// return fmt.Errorf("cell address under mouse is out of bounds") +// } +// +// if clickable, ok := t.Cells[address].(Clickable); ok { +// return clickable.HandleClick(s) +// } +// +// return nil +//} +// +//func (t *Table) HandleMouseEnter(ctx context.Context, s MouseState) error { +// _ = t.mouseHandler.HandleMouseEnter(s) +// address := t.getAddressUnderMouse() +// if address < 0 || address >= len(t.Cells) { +// return fmt.Errorf("cell address under mouse is out of bounds") +// } +// +// if mouseable, ok := t.Cells[address].(Mouseable); ok { +// s.X -= +// return mouseable.HandleMouseEnter(s) +// } +// +// return nil +//} +// +//func (t *Table) HandleMouseLeave(ctx context.Context, s MouseState) error { +// _ = t.mouseHandler.HandleMouseLeave(s) +// return nil +//} +// +//func (t *Table) HandleMouseMove(ctx context.Context, s MouseState) error { +// _ = t.mouseHandler.HandleMouseMove(s) +// return nil +//} +// +//func (t *Table) HandleMouseDown(ctx context.Context, s MouseState) error { +// _ = t.mouseHandler.HandleMouseDown(s) +// return nil +//} +// +//func (t *Table) HandleMouseUp(ctx context.Context, s MouseState) error { +// _ = t.mouseHandler.HandleMouseUp(s) +// return nil +//} +// +//func (t *Table) CellSize() (w, h float32) { +// w, h = t. +// return w / float32(t.ColumnCount), h / float32(t.RowCount) +//} +// +//func (t *Table) CellZero(ctx context.Context, addr int) (x, y float32) { +// x, y = GetZero(ctx) +// w, h := t.CellSize(ctx) +// +// col, row := addr%t.ColumnCount, addr/t.ColumnCount +// return x + float32(col)*w, y + float32(row) + h +//} +// +//func (t *Table) Draw(screen *ebiten.Image, zx, zy float64) error { +// return nil +//} +// +//func (t *Table) getAddressUnderMouse() int { +// return int(t.mouseState.Y)*t.RowCount + int(t.mouseState.X) +//} diff --git a/common/sprites/v1/sprite.go b/common/sprites/v1/sprite.go new file mode 100644 index 0000000..9d32149 --- /dev/null +++ b/common/sprites/v1/sprite.go @@ -0,0 +1,147 @@ +package sprites + +import ( + "image" + + "git.vezzani.net/ben/games/common/ux/v1" + "github.com/hajimehoshi/ebiten/v2" +) + +var ( + updateCount int +) + +func Update() { + updateCount++ +} + +type animation struct { + RowNumber uint8 + TickCount uint8 + Scale float32 +} + +type subImager interface { + image.Image + SubImage(r image.Rectangle) image.Image +} + +type Sprite struct { + width, height int + imgData subImager + animations map[string]animation + + baseAnim *animation + currentAnim *animation + + animationStartedAt int + animationStopAt int + animateOnce bool +} + +func (s *Sprite) baseAnimation() animation { + if s.baseAnim != nil { + return *s.baseAnim + } + + for i := range s.animations { + if s.animations[i].RowNumber == 0 { + a := s.animations[i] + s.baseAnim = &a + return a + } + } + + return animation{} +} + +func (s *Sprite) getAnimation() animation { + if s.currentAnim != nil { + return *s.currentAnim + } + return s.baseAnimation() +} + +func (s *Sprite) Image(ops imageOptions) image.Image { + anim := s.getAnimation() + + xOffset := updateCount % int(anim.TickCount) * s.width + yOffset := int(anim.RowNumber) * s.height + + r := image.Rect(xOffset, yOffset, s.width, s.height).Intersect(s.imgData.Bounds()) + + return s.imgData.SubImage(r) +} + +func (s *Sprite) Draw(screen *ebiten.Image, options ...ImageOption) { + if s.getAnimation().RowNumber == 0 { + } + + ops := imageOptions{ + scaleX: ux.Scale, + scaleY: ux.Scale, + } + + for _, o := range options { + o(&ops) + } + + geom := ebiten.GeoM{} + geom.Translate(ops.x, ops.y) + geom.Scale(ops.scaleX, ops.scaleY) + geom.Rotate(ops.rotateTheta) + + screen.DrawImage( + ebiten.NewImageFromImage( + s.Image(ops), + ), + &ebiten.DrawImageOptions{ + GeoM: geom, + }, + ) +} + +func (s *Sprite) StartAnimation(id string, once bool) { + if a, ok := s.animations[id]; ok { + s.currentAnim = &a + } + if once { + s.animateOnce = true + s.animationStopAt = updateCount + int(s.currentAnim.TickCount) + } + s.animationStartedAt = updateCount +} + +func (s *Sprite) StopAnimation() { + s.currentAnim = nil + s.animationStartedAt = 0 + s.animationStopAt = 0 + s.animateOnce = false +} + +type imageOptions struct { + x, y float64 + scaleX, scaleY float64 + rotateTheta float64 +} + +type ImageOption func(options *imageOptions) + +func ToScale(x, y float64) ImageOption { + return func(o *imageOptions) { + o.scaleX *= x + o.scaleY *= y + } +} + +func AtPosition(x, y float64) ImageOption { + return func(o *imageOptions) { + o.x, o.y = x, y + } +} + +func Rotate(theta float64) ImageOption { + return func(o *imageOptions) { + o.rotateTheta = theta + } +} diff --git a/common/ux/v1/ux.go b/common/ux/v1/ux.go new file mode 100644 index 0000000..af6688a --- /dev/null +++ b/common/ux/v1/ux.go @@ -0,0 +1,14 @@ +package ux + +import ( + "image/color" + + "golang.org/x/image/font" + "golang.org/x/image/font/basicfont" +) + +var ( + FontFace font.Face = basicfont.Face7x13 + BackgroundColor color.Color = color.White + Scale float64 = 1.0 +) diff --git a/games/minesweeper/game/game.go b/games/minesweeper/v1/game/game.go similarity index 100% rename from games/minesweeper/game/game.go rename to games/minesweeper/v1/game/game.go diff --git a/games/minesweeper/main.go b/games/minesweeper/v1/main.go similarity index 74% rename from games/minesweeper/main.go rename to games/minesweeper/v1/main.go index b23f639..8a6b190 100644 --- a/games/minesweeper/main.go +++ b/games/minesweeper/v1/main.go @@ -3,12 +3,12 @@ package main import ( "log" - "git.vezzani.net/ben/games/games/minesweeper/game" + "git.vezzani.net/ben/games/games/minesweeper/v1/game" "github.com/hajimehoshi/ebiten/v2" ) func main() { - g := game.New(10, 10, 50) + g := game.New(10, 10, 15) w, h := g.Layout(0, 0) ebiten.SetWindowSize(w, h) ebiten.SetWindowTitle("Minesweeper") diff --git a/go.mod b/go.mod index da0c2bb..298678f 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,22 @@ module git.vezzani.net/ben/games -go 1.24 +go 1.25 require ( + github.com/ebitenui/ebitenui v0.7.1 github.com/hajimehoshi/ebiten/v2 v2.8.8 - golang.org/x/image v0.20.0 + golang.org/x/image v0.25.0 ) require ( - github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 // indirect + github.com/ebitengine/gomobile v0.0.0-20250209143333-6071a2a2351c // indirect github.com/ebitengine/hideconsole v1.0.0 // indirect - github.com/ebitengine/purego v0.8.0 // indirect + github.com/ebitengine/purego v0.8.2 // indirect + github.com/frustra/bbcode v0.0.0-20201127003707-6ef347fbe1c8 // indirect + github.com/go-text/typesetting v0.3.0 // indirect github.com/jezek/xgb v1.1.1 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.25.0 // indirect + golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect ) diff --git a/go.sum b/go.sum index 58e3580..5b25968 100644 --- a/go.sum +++ b/go.sum @@ -1,22 +1,44 @@ -github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 h1:Gk1XUEttOk0/hb6Tq3WkmutWa0ZLhNn/6fc6XZpM7tM= -github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325/go.mod h1:ulhSQcbPioQrallSuIzF8l1NKQoD7xmMZc5NxzibUMY= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/ebitengine/gomobile v0.0.0-20250209143333-6071a2a2351c h1:nCxkoQoJMcVLc5aoMp3ULbfyEMcQjxopBKgNQVBQFXE= +github.com/ebitengine/gomobile v0.0.0-20250209143333-6071a2a2351c/go.mod h1:yMh1VvLL71zDgHlVlIXXJIGmv36QcJ9ZD2gtIGYAp3I= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= -github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE= -github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= +github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitenui/ebitenui v0.7.1 h1:5ZOaonRs4EsO9LtVVJr1zXNBXadUA9ktaZQLg9QgWao= +github.com/ebitenui/ebitenui v0.7.1/go.mod h1:QiJoDflkWoBv4V/LKErS3cgzTZHrXDQyqajef7IA8vM= +github.com/frustra/bbcode v0.0.0-20201127003707-6ef347fbe1c8 h1:sdIsYe6Vv7KIWZWp8KqSeTl+XlF17d+wHCC4lbxFcYs= +github.com/frustra/bbcode v0.0.0-20201127003707-6ef347fbe1c8/go.mod h1:0QBxkXxN+o4FyZgLI9FHY/oUizheze3+bNY/kgCKL+4= +github.com/go-text/typesetting v0.3.0 h1:OWCgYpp8njoxSRpwrdd1bQOxdjOXDj9Rqart9ML4iF4= +github.com/go-text/typesetting v0.3.0/go.mod h1:qjZLkhRgOEYMhU9eHBr3AR4sfnGJvOXNLt8yRAySFuY= +github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0= +github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= github.com/hajimehoshi/bitmapfont/v3 v3.2.0 h1:0DISQM/rseKIJhdF29AkhvdzIULqNIIlXAGWit4ez1Q= github.com/hajimehoshi/bitmapfont/v3 v3.2.0/go.mod h1:8gLqGatKVu0pwcNCJguW3Igg9WQqVXF0zg/RvrGQWyg= github.com/hajimehoshi/ebiten/v2 v2.8.8 h1:xyMxOAn52T1tQ+j3vdieZ7auDBOXmvjUprSrxaIbsi8= github.com/hajimehoshi/ebiten/v2 v2.8.8/go.mod h1:durJ05+OYnio9b8q0sEtOgaNeBEQG7Yr7lRviAciYbs= github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= +github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ= +github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw= -golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= +golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= +golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tools/component_test/editor.go b/tools/component_test/editor.go new file mode 100644 index 0000000..eea5bb5 --- /dev/null +++ b/tools/component_test/editor.go @@ -0,0 +1,68 @@ +package main + +import ( + "image/color" + + "git.vezzani.net/ben/games/common/elements/v1" + "git.vezzani.net/ben/games/common/elements/v1/blocks" + "git.vezzani.net/ben/games/common/elements/v1/stacks" + "git.vezzani.net/ben/games/common/sprites/v1" + "github.com/hajimehoshi/ebiten/v2" + "golang.org/x/image/colornames" +) + +var root = stacks.New( + stacks.Horizontal(), + stacks.BlockOpt(blocks.BackgroundColor(color.White)), + stacks.BlockOpt(blocks.Size(200, 200)), + stacks.BlockOpt(blocks.Name("parent")), + stacks.Children( + stacks.New( + stacks.BlockOpt(blocks.Name("left")), + stacks.Children( + blocks.New(blocks.BackgroundColor(colornames.Green)), + blocks.New(blocks.BackgroundColor(colornames.Yellow)), + )), + stacks.New( + stacks.BlockOpt(blocks.Name("right")), + stacks.Children( + blocks.New(blocks.BackgroundColor(colornames.Blue)), + blocks.New(blocks.BackgroundColor(colornames.Red)), + ), + ), + ), +) + +func newEditor() *editor { + return &editor{} +} + +type editor struct { + root elements.Element + bounds elements.Bounds +} + +//var handleClick = mouse.ClickHandler(root) + +func (e *editor) Update() error { + sprites.Update() + //handleClick() + return nil +} + +func (e *editor) Draw(screen *ebiten.Image) { + e.root.Draw(screen) +} + +func (e *editor) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { + if e.bounds.Width != float64(outsideWidth) || e.bounds.Height != float64(outsideHeight) { + e.bounds = elements.Bounds{ + Min: elements.Point{}, + Width: float64(outsideWidth), + Height: float64(outsideHeight), + } + e.root = root(e.bounds) + } + + return outsideWidth, outsideHeight +} diff --git a/tools/component_test/main.go b/tools/component_test/main.go new file mode 100644 index 0000000..a7d70bb --- /dev/null +++ b/tools/component_test/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/hajimehoshi/ebiten/v2" +) + +func main() { + ebiten.SetWindowSize(480, 320) + ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled) + ebiten.SetWindowTitle("Component/Layout Test") + e := newEditor() + if err := ebiten.RunGame(e); err != nil { + panic(err) + } +} diff --git a/tools/spritedit/editor.go b/tools/spritedit/editor.go new file mode 100644 index 0000000..2dd65e1 --- /dev/null +++ b/tools/spritedit/editor.go @@ -0,0 +1,48 @@ +package main + +import ( + "image/color" + + "git.vezzani.net/ben/games/common/elements/v1" + "git.vezzani.net/ben/games/common/elements/v1/blocks" + "git.vezzani.net/ben/games/common/elements/v1/stacks" + "git.vezzani.net/ben/games/common/sprites/v1" + "github.com/hajimehoshi/ebiten/v2" + "golang.org/x/image/colornames" +) + +var menu = stacks.New( + stacks.BlockOpt(blocks.BackgroundColor(color.White)), + stacks.BlockOpt(blocks.Size(100, 200)), + stacks.Children( + blocks.New(blocks.BackgroundColor(colornames.Green)), + blocks.New(), + ), +) + +func newEditor() *editor { + return &editor{} +} + +type editor struct { +} + +func (e *editor) Update() error { + sprites.Update() + + return nil +} + +func (e *editor) Draw(screen *ebiten.Image) { + b := screen.Bounds() + menu(elements.Bounds{ + ZX: float64(b.Min.X), + ZY: float64(b.Min.Y), + WX: float64(b.Max.X), + WY: float64(b.Max.Y), + }).Draw(screen) +} + +func (e *editor) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { + return outsideWidth, outsideHeight +} diff --git a/tools/spritedit/main.go b/tools/spritedit/main.go new file mode 100644 index 0000000..530162d --- /dev/null +++ b/tools/spritedit/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/hajimehoshi/ebiten/v2" +) + +func main() { + ebiten.SetWindowSize(480, 320) + ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled) + ebiten.SetWindowTitle("Sprite Editor") + e := newEditor() + if err := ebiten.RunGame(e); err != nil { + panic(err) + } +}