forked from go/golangs_learn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
213 lines
4.1 KiB
213 lines
4.1 KiB
package bugs
|
|
|
|
import (
|
|
"fmt"
|
|
"image/color"
|
|
|
|
"fyne.io/fyne/v2"
|
|
"fyne.io/fyne/v2/canvas"
|
|
"fyne.io/fyne/v2/container"
|
|
"fyne.io/fyne/v2/dialog"
|
|
"fyne.io/fyne/v2/theme"
|
|
"fyne.io/fyne/v2/widget"
|
|
)
|
|
|
|
var bug, code, flag *theme.ThemedResource
|
|
|
|
func init() {
|
|
bug = theme.NewThemedResource(bugIcon)
|
|
code = theme.NewThemedResource(codeIcon)
|
|
flag = theme.NewThemedResource(flagIcon)
|
|
}
|
|
|
|
type gameRenderer struct {
|
|
grid *fyne.Container
|
|
header fyne.CanvasObject
|
|
|
|
game *game
|
|
}
|
|
|
|
func (g *gameRenderer) MinSize() fyne.Size {
|
|
return g.grid.MinSize().Add(fyne.NewSize(0, g.header.MinSize().Height))
|
|
}
|
|
|
|
func (g *gameRenderer) Layout(size fyne.Size) {
|
|
headerHeight := g.header.MinSize().Height
|
|
g.header.Resize(fyne.NewSize(size.Width, headerHeight))
|
|
g.grid.Move(fyne.NewPos(0, headerHeight)) // TODO why ignored?
|
|
gridSize := size.Subtract(fyne.NewSize(0, headerHeight))
|
|
g.grid.Layout.Layout(g.grid.Objects, gridSize)
|
|
}
|
|
|
|
func (g *gameRenderer) ApplyTheme() {
|
|
}
|
|
|
|
func (g *gameRenderer) BackgroundColor() color.Color {
|
|
return theme.BackgroundColor()
|
|
}
|
|
|
|
func (g *gameRenderer) Refresh() {
|
|
canvas.Refresh(g.grid)
|
|
}
|
|
|
|
func (g *gameRenderer) Objects() []fyne.CanvasObject {
|
|
return []fyne.CanvasObject{g.grid, g.header}
|
|
}
|
|
|
|
func (g *gameRenderer) Destroy() {
|
|
}
|
|
|
|
type game struct {
|
|
widget.BaseWidget
|
|
board *board
|
|
remain *widget.Label
|
|
|
|
grid *fyne.Container
|
|
window fyne.Window
|
|
}
|
|
|
|
func (g *game) refreshSquare(x, y int) {
|
|
if x < 0 || y < 0 || x >= g.board.width || y >= g.board.height {
|
|
return
|
|
}
|
|
|
|
sq := g.board.bugs[y][x]
|
|
i := y*g.board.width + x
|
|
button := g.grid.Objects[i].(*bugButton)
|
|
|
|
if sq.flagged {
|
|
if button.icon == flag {
|
|
return
|
|
}
|
|
button.icon = flag
|
|
button.text = ""
|
|
} else if !sq.shown {
|
|
if button.icon == code {
|
|
return
|
|
}
|
|
button.icon = code
|
|
button.text = ""
|
|
} else if sq.bug {
|
|
if button.icon == bug {
|
|
return
|
|
}
|
|
button.icon = bug
|
|
button.text = ""
|
|
} else if button.icon == nil {
|
|
return
|
|
} else {
|
|
button.icon = nil
|
|
button.text = squareString(sq)
|
|
}
|
|
|
|
button.Refresh()
|
|
}
|
|
|
|
func (g *game) refreshAround(xp, yp, d int) {
|
|
x, y := xp-d, yp-d
|
|
for ; x < xp+d; x++ {
|
|
g.refreshSquare(x, y)
|
|
}
|
|
for ; y < yp+d; y++ {
|
|
g.refreshSquare(x, y)
|
|
}
|
|
for ; x > xp-d; x-- {
|
|
g.refreshSquare(x, y)
|
|
}
|
|
for ; y > yp-d; y-- {
|
|
g.refreshSquare(x, y)
|
|
}
|
|
}
|
|
|
|
func (g *game) refreshFrom(x, y int) {
|
|
g.refreshSquare(x, y)
|
|
|
|
for i := 1; i < int(fyne.Max(float32(g.board.width), float32(g.board.height))); i++ {
|
|
g.refreshAround(x, y, i)
|
|
}
|
|
}
|
|
|
|
func (g *game) CreateRenderer() fyne.WidgetRenderer {
|
|
renderer := &gameRenderer{game: g}
|
|
title := widget.NewLabel("Hunt bugs!")
|
|
g.remain = widget.NewLabel("")
|
|
g.updateRemain()
|
|
renderer.header = container.NewBorder(nil, nil, title, g.remain)
|
|
|
|
var buttons []fyne.CanvasObject
|
|
for y := 0; y < g.board.height; y++ {
|
|
for x := 0; x < g.board.width; x++ {
|
|
xx, yy := x, y
|
|
|
|
buttons = append(buttons, newButton("", code, func(reveal bool) {
|
|
if reveal {
|
|
g.squareReveal(xx, yy)
|
|
} else {
|
|
g.squareFlagged(xx, yy)
|
|
}
|
|
|
|
g.updateRemain()
|
|
}))
|
|
}
|
|
}
|
|
|
|
renderer.grid = container.NewGridWithColumns(g.board.width, buttons...)
|
|
g.grid = renderer.grid
|
|
return renderer
|
|
}
|
|
|
|
func (g *game) squareReveal(x, y int) {
|
|
if g.board.flagged(x, y) {
|
|
return
|
|
}
|
|
|
|
g.board.reveal(x, y)
|
|
g.refreshFrom(x, y)
|
|
}
|
|
|
|
func (g *game) squareFlagged(x, y int) {
|
|
g.board.flag(x, y)
|
|
g.refreshSquare(x, y)
|
|
}
|
|
|
|
func (g *game) loseCallback(yes bool) {
|
|
if !yes {
|
|
return
|
|
}
|
|
|
|
g.board.load(40)
|
|
g.updateRemain()
|
|
g.refreshFrom(g.board.width/2, g.board.height/2)
|
|
}
|
|
|
|
func (g *game) win() {
|
|
dialog.ShowInformation("You won!", "Congratulations, you found all the bugs", g.window)
|
|
}
|
|
|
|
func (g *game) lose() {
|
|
dialog.ShowConfirm("You lost!", "You hit a bug and lost the game, try again?", g.loseCallback, g.window)
|
|
}
|
|
|
|
func (g *game) updateRemain() {
|
|
g.remain.SetText(fmt.Sprintf("remaining: %d", g.board.remaining()))
|
|
}
|
|
|
|
func newGame(f *board) *game {
|
|
g := &game{board: f}
|
|
g.ExtendBaseWidget(g)
|
|
|
|
return g
|
|
}
|
|
|
|
// Show starts a new bugs game
|
|
func Show(win fyne.Window) fyne.CanvasObject {
|
|
b := newBoard(20, 14)
|
|
game := newGame(b)
|
|
|
|
b.win = game.win
|
|
b.lose = game.lose
|
|
b.load(40)
|
|
|
|
game.window = win
|
|
return game
|
|
}
|
|
|