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.
831 lines
18 KiB
831 lines
18 KiB
package game |
|
|
|
import ( |
|
"fmt" |
|
"image" |
|
"image/color" |
|
"time" |
|
|
|
"code.rocketnine.space/tslocum/fibs" |
|
"github.com/hajimehoshi/ebiten/v2" |
|
"github.com/hajimehoshi/ebiten/v2/ebitenutil" |
|
"github.com/hajimehoshi/ebiten/v2/inpututil" |
|
"github.com/hajimehoshi/ebiten/v2/text" |
|
"github.com/llgcode/draw2d/draw2dimg" |
|
) |
|
|
|
type stateUpdate struct { |
|
from int |
|
to int |
|
v []int |
|
} |
|
|
|
type board struct { |
|
x, y int |
|
w, h int |
|
|
|
op *ebiten.DrawImageOptions |
|
|
|
backgroundImage *ebiten.Image |
|
|
|
Sprites *Sprites |
|
|
|
spaces [][]*Sprite // Space contents |
|
spaceRects [][4]int |
|
|
|
dragging *Sprite |
|
moving *Sprite // Moving automatically |
|
|
|
dragTouchId ebiten.TouchID |
|
touchIDs []ebiten.TouchID |
|
|
|
spaceWidth int |
|
barWidth int |
|
triangleOffset float64 |
|
horizontalBorderSize int |
|
verticalBorderSize int |
|
overlapSize int |
|
|
|
lastDirection int |
|
V []int |
|
|
|
moveQueue chan *stateUpdate |
|
|
|
drawFrame chan bool |
|
|
|
debug int // Print and draw debug information |
|
} |
|
|
|
func NewBoard() *board { |
|
b := &board{ |
|
barWidth: 100, |
|
triangleOffset: float64(50), |
|
horizontalBorderSize: 50, |
|
verticalBorderSize: 25, |
|
overlapSize: 97, |
|
Sprites: &Sprites{ |
|
sprites: make([]*Sprite, 30), |
|
num: 30, |
|
}, |
|
spaces: make([][]*Sprite, 26), |
|
spaceRects: make([][4]int, 26), |
|
V: make([]int, 42), |
|
moveQueue: make(chan *stateUpdate, 10), |
|
drawFrame: make(chan bool, 10), |
|
} |
|
|
|
for i := range b.Sprites.sprites { |
|
s := b.newSprite(i%2 == 1) |
|
|
|
b.Sprites.sprites[i] = s |
|
|
|
space := i |
|
if space > 25 { |
|
if space%2 == 0 { |
|
space = 0 |
|
} else { |
|
space = 25 |
|
} |
|
} |
|
|
|
b.spaces[space] = append(b.spaces[space], s) |
|
} |
|
|
|
go b.handleDraw() |
|
|
|
go b.handlePieceMoves() |
|
|
|
b.op = &ebiten.DrawImageOptions{} |
|
|
|
b.dragTouchId = -1 |
|
|
|
return b |
|
} |
|
|
|
func (b *board) handleDraw() { |
|
drawFreq := time.Second / 144 // TODO |
|
lastDraw := time.Now() |
|
for v := range b.drawFrame { |
|
if !v { |
|
return |
|
} |
|
since := time.Since(lastDraw) |
|
if since < drawFreq { |
|
t := time.NewTimer(drawFreq - since) |
|
DELAYDRAW: |
|
for { |
|
select { |
|
case <-b.drawFrame: |
|
continue DELAYDRAW |
|
case <-t.C: |
|
break DELAYDRAW |
|
} |
|
} |
|
} |
|
ebiten.ScheduleFrame() |
|
lastDraw = time.Now() |
|
} |
|
} |
|
|
|
func (b *board) newSprite(white bool) *Sprite { |
|
s := &Sprite{} |
|
s.colorWhite = white |
|
s.w, s.h = imgCheckerWhite.Size() |
|
return s |
|
} |
|
|
|
func (b *board) updateBackgroundImage() { |
|
tableColor := color.RGBA{0, 102, 51, 255} |
|
frameColor := color.RGBA{65, 40, 14, 255} |
|
borderColor := color.RGBA{0, 0, 0, 255} |
|
faceColor := color.RGBA{120, 63, 25, 255} |
|
triangleA := color.RGBA{225.0, 188, 125, 255} |
|
triangleB := color.RGBA{120.0, 17.0, 0, 255} |
|
|
|
borderSize := b.horizontalBorderSize |
|
if borderSize > b.barWidth/2 { |
|
borderSize = b.barWidth / 2 |
|
} |
|
frameW := b.w - ((b.horizontalBorderSize - borderSize) * 2) |
|
innerW := b.w - (b.horizontalBorderSize * 2) // Outer board width (including frame) |
|
|
|
// Table |
|
box := image.NewRGBA(image.Rect(0, 0, b.w, b.h)) |
|
img := ebiten.NewImageFromImage(box) |
|
img.Fill(tableColor) |
|
b.backgroundImage = ebiten.NewImageFromImage(img) |
|
|
|
// Frame |
|
box = image.NewRGBA(image.Rect(0, 0, frameW, b.h)) |
|
img = ebiten.NewImageFromImage(box) |
|
img.Fill(frameColor) |
|
b.op.GeoM.Reset() |
|
b.op.GeoM.Translate(float64(b.horizontalBorderSize-borderSize), 0) |
|
b.backgroundImage.DrawImage(img, b.op) |
|
|
|
// Face |
|
box = image.NewRGBA(image.Rect(0, 0, innerW, b.h-(b.verticalBorderSize*2))) |
|
img = ebiten.NewImageFromImage(box) |
|
img.Fill(faceColor) |
|
b.op.GeoM.Reset() |
|
b.op.GeoM.Translate(float64(b.horizontalBorderSize), float64(b.verticalBorderSize)) |
|
b.backgroundImage.DrawImage(img, b.op) |
|
|
|
// Bar |
|
box = image.NewRGBA(image.Rect(0, 0, b.barWidth, b.h)) |
|
img = ebiten.NewImageFromImage(box) |
|
img.Fill(frameColor) |
|
b.op.GeoM.Reset() |
|
b.op.GeoM.Translate(float64((b.w/2)-(b.barWidth/2)), 0) |
|
b.backgroundImage.DrawImage(img, b.op) |
|
|
|
// Draw triangles |
|
baseImg := image.NewRGBA(image.Rect(0, 0, b.w-(b.horizontalBorderSize*2), b.h-(b.verticalBorderSize*2))) |
|
gc := draw2dimg.NewGraphicContext(baseImg) |
|
for i := 0; i < 2; i++ { |
|
triangleTip := float64((b.h - (b.verticalBorderSize * 2)) / 2) |
|
if i == 0 { |
|
triangleTip -= b.triangleOffset |
|
} else { |
|
triangleTip += b.triangleOffset |
|
} |
|
for j := 0; j < 12; j++ { |
|
colorA := j%2 == 0 |
|
if i == 1 { |
|
colorA = !colorA |
|
} |
|
|
|
if colorA { |
|
gc.SetFillColor(triangleA) |
|
} else { |
|
gc.SetFillColor(triangleB) |
|
} |
|
|
|
tx := b.spaceWidth * j |
|
ty := b.h * i |
|
if j >= 6 { |
|
tx += b.barWidth |
|
} |
|
gc.MoveTo(float64(tx), float64(ty)) |
|
gc.LineTo(float64(tx+b.spaceWidth/2), triangleTip) |
|
gc.LineTo(float64(tx+b.spaceWidth), float64(ty)) |
|
gc.Close() |
|
gc.Fill() |
|
} |
|
} |
|
img = ebiten.NewImageFromImage(baseImg) |
|
b.op.GeoM.Reset() |
|
b.op.GeoM.Translate(float64(b.horizontalBorderSize), float64(b.verticalBorderSize)) |
|
b.backgroundImage.DrawImage(img, b.op) |
|
|
|
// Border |
|
borderImage := image.NewRGBA(image.Rect(0, 0, b.w, b.h)) |
|
gc = draw2dimg.NewGraphicContext(borderImage) |
|
gc.SetStrokeColor(borderColor) |
|
// - Outside left |
|
gc.SetLineWidth(2) |
|
gc.MoveTo(float64(1), float64(0)) |
|
gc.LineTo(float64(1), float64(b.h)) |
|
// - Center |
|
gc.SetLineWidth(2) |
|
gc.MoveTo(float64(frameW/2), float64(0)) |
|
gc.LineTo(float64(frameW/2), float64(b.h)) |
|
// - Outside right |
|
gc.MoveTo(float64(frameW), float64(0)) |
|
gc.LineTo(float64(frameW), float64(b.h)) |
|
gc.Close() |
|
gc.Stroke() |
|
// - Inside left |
|
gc.SetLineWidth(1) |
|
edge := float64((((innerW) - b.barWidth) / 2) + borderSize) |
|
gc.MoveTo(float64(borderSize), float64(b.verticalBorderSize)) |
|
gc.LineTo(edge, float64(b.verticalBorderSize)) |
|
gc.LineTo(edge, float64(b.h-b.verticalBorderSize)) |
|
gc.LineTo(float64(borderSize), float64(b.h-b.verticalBorderSize)) |
|
gc.LineTo(float64(borderSize), float64(b.verticalBorderSize)) |
|
gc.Close() |
|
gc.Stroke() |
|
// - Inside right |
|
edgeStart := float64((innerW / 2) + (b.barWidth / 2) + borderSize) |
|
edgeEnd := float64(innerW + borderSize) |
|
gc.MoveTo(float64(edgeStart), float64(b.verticalBorderSize)) |
|
gc.LineTo(edgeEnd, float64(b.verticalBorderSize)) |
|
gc.LineTo(edgeEnd, float64(b.h-b.verticalBorderSize)) |
|
gc.LineTo(float64(edgeStart), float64(b.h-b.verticalBorderSize)) |
|
gc.LineTo(float64(edgeStart), float64(b.verticalBorderSize)) |
|
gc.Close() |
|
gc.Stroke() |
|
img = ebiten.NewImageFromImage(borderImage) |
|
b.op.GeoM.Reset() |
|
b.op.GeoM.Translate(float64(b.horizontalBorderSize-borderSize), 0) |
|
b.backgroundImage.DrawImage(img, b.op) |
|
} |
|
|
|
func (b *board) ScheduleFrame() { |
|
b.drawFrame <- true |
|
} |
|
|
|
func (b *board) draw(screen *ebiten.Image) { |
|
b.op.GeoM.Reset() |
|
b.op.GeoM.Translate(float64(b.x), float64(b.y)) |
|
screen.DrawImage(b.backgroundImage, b.op) |
|
|
|
drawSprite := func(sprite *Sprite) { |
|
b.op.GeoM.Reset() |
|
b.op.GeoM.Translate(float64(sprite.x), float64(sprite.y)) |
|
|
|
if sprite.colorWhite { |
|
screen.DrawImage(imgCheckerWhite, b.op) |
|
} else { |
|
screen.DrawImage(imgCheckerBlack, b.op) |
|
} |
|
} |
|
|
|
b.iterateSpaces(func(space int) { |
|
var numPieces int |
|
for _, sprite := range b.spaces[space] { |
|
if sprite == b.dragging || sprite == b.moving { |
|
continue |
|
} |
|
numPieces++ |
|
|
|
drawSprite(sprite) |
|
|
|
if numPieces > 5 { |
|
label := fmt.Sprintf("%d", numPieces) |
|
labelColor := color.RGBA{255, 255, 255, 255} |
|
if sprite.colorWhite { |
|
labelColor = color.RGBA{0, 0, 0, 255} |
|
} |
|
|
|
bounds := text.BoundString(mplusNormalFont, label) |
|
overlayImage := ebiten.NewImage(bounds.Dx()*2, bounds.Dy()*2) |
|
text.Draw(overlayImage, label, mplusNormalFont, 0, bounds.Dy(), labelColor) |
|
|
|
x, y, w, h := b.stackSpaceRect(space, numPieces) |
|
x += (w / 2) - (bounds.Dx() / 2) |
|
y += (h / 2) - (bounds.Dy() / 2) |
|
x, y = b.offsetPosition(x, y) |
|
|
|
b.op.GeoM.Reset() |
|
b.op.GeoM.Translate(float64(x), float64(y)) |
|
screen.DrawImage(overlayImage, b.op) |
|
} |
|
} |
|
}) |
|
|
|
// Draw moving sprite |
|
if b.moving != nil { |
|
drawSprite(b.moving) |
|
} |
|
|
|
// Draw dragged sprite |
|
if b.dragging != nil { |
|
drawSprite(b.dragging) |
|
} |
|
|
|
if b.debug == 2 { |
|
b.iterateSpaces(func(space int) { |
|
x, y, w, h := b.spaceRect(space) |
|
spaceImage := ebiten.NewImage(w, h) |
|
if space%2 == 0 { |
|
spaceImage.Fill(color.RGBA{0, 0, 0, 150}) |
|
} else { |
|
spaceImage.Fill(color.RGBA{255, 255, 255, 150}) |
|
} |
|
|
|
br := "" |
|
if b.bottomRow(space) { |
|
br = "B" |
|
} |
|
|
|
ebitenutil.DebugPrint(spaceImage, fmt.Sprintf(" %d %s", space, br)) |
|
|
|
x, y = b.offsetPosition(x, y) |
|
|
|
b.op.GeoM.Reset() |
|
b.op.GeoM.Translate(float64(x), float64(y)) |
|
screen.DrawImage(spaceImage, b.op) |
|
}) |
|
} |
|
} |
|
|
|
func (b *board) setRect(x, y, w, h int) { |
|
if b.x == x && b.y == y && b.w == w && b.h == h { |
|
return |
|
} |
|
const stackAllowance = 0.97 // TODO configurable |
|
|
|
b.x, b.y, b.w, b.h = x, y, w, h |
|
|
|
b.horizontalBorderSize = 0 |
|
|
|
b.triangleOffset = float64(b.h-(b.verticalBorderSize*2)) / 15 |
|
|
|
for { |
|
b.verticalBorderSize = 7 // TODO configurable |
|
|
|
b.spaceWidth = (b.w - (b.horizontalBorderSize * 2)) / 13 |
|
|
|
b.barWidth = b.spaceWidth |
|
|
|
b.overlapSize = (((b.h - (b.verticalBorderSize * 2)) - (int(b.triangleOffset) * 2)) / 2) / 5 |
|
o := int(float64(b.spaceWidth) * stackAllowance) |
|
if b.overlapSize >= o { |
|
b.overlapSize = o |
|
break |
|
} |
|
|
|
b.horizontalBorderSize++ |
|
} |
|
|
|
extraSpace := b.w - (b.spaceWidth * 12) |
|
largeBarWidth := int(float64(b.spaceWidth) * 1.25) |
|
if extraSpace >= largeBarWidth { |
|
b.barWidth = largeBarWidth |
|
} |
|
|
|
b.horizontalBorderSize = ((b.w - (b.spaceWidth * 12)) - b.barWidth) / 2 |
|
if b.horizontalBorderSize < 0 { |
|
b.horizontalBorderSize = 0 |
|
} |
|
|
|
loadAssets(b.spaceWidth) |
|
|
|
for i := 0; i < b.Sprites.num; i++ { |
|
s := b.Sprites.sprites[i] |
|
s.w, s.h = imgCheckerWhite.Size() |
|
} |
|
|
|
b.setSpaceRects() |
|
b.updateBackgroundImage() |
|
b.positionCheckers() |
|
} |
|
|
|
func (b *board) offsetPosition(x, y int) (int, int) { |
|
return b.x + x + b.horizontalBorderSize, b.y + y + b.verticalBorderSize |
|
} |
|
|
|
func (b *board) positionCheckers() { |
|
for space := 0; space < 26; space++ { |
|
sprites := b.spaces[space] |
|
|
|
for i := range sprites { |
|
s := sprites[i] |
|
if b.dragging == s { |
|
continue |
|
} |
|
|
|
x, y, w, _ := b.stackSpaceRect(space, i) |
|
s.x, s.y = b.offsetPosition(x, y) |
|
// Center piece in space |
|
s.x += (w - s.w) / 2 |
|
} |
|
} |
|
} |
|
|
|
func (b *board) spriteAt(x, y int) *Sprite { |
|
space := b.spaceAt(x, y) |
|
if space == -1 { |
|
return nil |
|
} |
|
pieces := b.spaces[space] |
|
if len(pieces) == 0 { |
|
return nil |
|
} |
|
return pieces[len(pieces)-1] |
|
} |
|
|
|
func (b *board) spaceAt(x, y int) int { |
|
for i := 0; i < 26; i++ { |
|
sx, sy, sw, sh := b.spaceRect(i) |
|
sx, sy = b.offsetPosition(sx, sy) |
|
if x >= sx && x <= sx+sw && y >= sy && y <= sy+sh { |
|
return i |
|
} |
|
} |
|
return -1 |
|
} |
|
|
|
// TODO move to fibs library |
|
func (b *board) iterateSpaces(f func(space int)) { |
|
if b.V[fibs.StateDirection] == 1 { |
|
for space := 0; space <= 25; space++ { |
|
f(space) |
|
} |
|
return |
|
} |
|
for space := 25; space >= 0; space-- { |
|
f(space) |
|
} |
|
} |
|
|
|
func (b *board) translateSpace(space int) int { |
|
if b.V[fibs.StateDirection] == -1 { |
|
// Spaces range from 24 - 1. |
|
if space == 0 || space == 25 { |
|
space = 25 - space |
|
} else if space <= 12 { |
|
space = 12 + space |
|
} else { |
|
space = space - 12 |
|
} |
|
} |
|
return space |
|
} |
|
|
|
func (b *board) setSpaceRects() { |
|
var x, y, w, h int |
|
for i := 0; i < 26; i++ { |
|
trueSpace := i |
|
|
|
space := b.translateSpace(i) |
|
if !b.bottomRow(trueSpace) { |
|
y = 0 |
|
} else { |
|
y = (b.h / 2) - b.verticalBorderSize |
|
} |
|
|
|
w = b.spaceWidth |
|
|
|
var hspace int // horizontal space |
|
var add int |
|
if space == 0 { |
|
hspace = 6 |
|
w = b.barWidth |
|
} else if space == 25 { |
|
hspace = 6 |
|
w = b.barWidth |
|
} else if space <= 6 { |
|
hspace = space - 1 |
|
} else if space <= 12 { |
|
hspace = space - 1 |
|
add = b.barWidth |
|
} else if space <= 18 { |
|
hspace = 24 - space |
|
add = b.barWidth |
|
} else { |
|
hspace = 24 - space |
|
} |
|
|
|
x = (b.spaceWidth * hspace) + add |
|
|
|
h = (b.h - (b.verticalBorderSize * 2)) / 2 |
|
|
|
b.spaceRects[trueSpace] = [4]int{x, y, w, h} |
|
} |
|
} |
|
|
|
// relX, relY |
|
func (b *board) spaceRect(space int) (x, y, w, h int) { |
|
rect := b.spaceRects[space] |
|
return rect[0], rect[1], rect[2], rect[3] |
|
} |
|
|
|
func (b *board) bottomRow(space int) bool { |
|
bottomStart := 1 |
|
bottomEnd := 12 |
|
bottomBar := 25 |
|
if b.V[fibs.StateDirection] == 1 { |
|
bottomStart = 13 |
|
bottomEnd = 24 |
|
bottomBar = 0 |
|
} |
|
return space == bottomBar || (space >= bottomStart && space <= bottomEnd) |
|
} |
|
|
|
// relX, relY |
|
func (b *board) stackSpaceRect(space int, stack int) (x, y, w, h int) { |
|
x, y, w, h = b.spaceRect(space) |
|
|
|
// Stack pieces |
|
osize := float64(stack) |
|
var o int |
|
if stack > 4 { |
|
osize = 3.5 |
|
} |
|
if b.bottomRow(space) { |
|
osize += 1.0 |
|
} |
|
o = int(osize * float64(b.overlapSize)) |
|
if !b.bottomRow(space) { |
|
y += o |
|
} else { |
|
y = y + (h - o) |
|
} |
|
|
|
w, h = b.spaceWidth, b.spaceWidth |
|
if space == 0 || space == 25 { |
|
w = b.barWidth |
|
} |
|
|
|
return x, y, w, h |
|
} |
|
|
|
func (b *board) SetState(v []int) { |
|
b.moveQueue <- &stateUpdate{-1, -1, v} |
|
} |
|
|
|
func (b *board) ProcessState() { |
|
v := b.V |
|
|
|
if b.lastDirection != v[fibs.StateDirection] { |
|
b.setSpaceRects() |
|
} |
|
b.lastDirection = v[fibs.StateDirection] |
|
|
|
b.Sprites = &Sprites{} |
|
b.spaces = make([][]*Sprite, 26) |
|
for space := 0; space < 26; space++ { |
|
spaceValue := v[fibs.StateBoardSpace0+space] |
|
if spaceValue == 0 { |
|
continue |
|
} |
|
|
|
white := spaceValue > 0 |
|
// TODO reverse bar spaces - always? |
|
// TODO take direction into account |
|
|
|
abs := spaceValue |
|
if abs < 0 { |
|
abs *= -1 |
|
} |
|
|
|
for i := 0; i < abs; i++ { |
|
s := b.newSprite(white) |
|
b.spaces[space] = append(b.spaces[space], s) |
|
b.Sprites.sprites = append(b.Sprites.sprites, s) |
|
} |
|
} |
|
b.Sprites.num = len(b.Sprites.sprites) |
|
|
|
b.positionCheckers() |
|
} |
|
|
|
func (b *board) _movePiece(sprite *Sprite, from int, to int, speed int) { |
|
moveSize := 1 |
|
moveDelay := time.Duration(1/speed) * time.Millisecond |
|
|
|
space := from |
|
for { |
|
if space == to { |
|
break |
|
} else if to > space { |
|
space++ |
|
} else { |
|
space-- |
|
} |
|
|
|
// Go to bar or home immediately |
|
if from == 0 || from == 25 || to == 0 || to == 25 { |
|
space = to |
|
} |
|
|
|
stack := len(b.spaces[space]) |
|
if stack == 1 && sprite.colorWhite != b.spaces[space][0].colorWhite { |
|
stack = 0 // Hit |
|
} |
|
|
|
x, y, _, _ := b.stackSpaceRect(space, stack) |
|
x, y = b.offsetPosition(x, y) |
|
|
|
cy := y |
|
if cy > sprite.y == b.bottomRow(space) { |
|
cy = sprite.y |
|
} |
|
|
|
if sprite.x != x { |
|
// Center |
|
for { |
|
if sprite.y == cy { |
|
break |
|
} |
|
if sprite.y < cy { |
|
sprite.y += moveSize |
|
if sprite.y > cy { |
|
sprite.y = cy |
|
} |
|
} else if sprite.y > cy { |
|
sprite.y -= moveSize |
|
if sprite.y < cy { |
|
sprite.y = cy |
|
} |
|
} |
|
b.ScheduleFrame() |
|
time.Sleep(moveDelay) |
|
} |
|
for { |
|
if sprite.x == x { |
|
break |
|
} |
|
if sprite.x < x { |
|
sprite.x += moveSize |
|
if sprite.x > x { |
|
sprite.x = x |
|
} |
|
} else if sprite.x > x { |
|
sprite.x -= moveSize |
|
if sprite.x < x { |
|
sprite.x = x |
|
} |
|
} |
|
b.ScheduleFrame() |
|
time.Sleep(moveDelay / 2) |
|
} |
|
} |
|
for { |
|
if sprite.x == x && sprite.y == y { |
|
break |
|
} |
|
if sprite.x < x { |
|
sprite.x += moveSize |
|
if sprite.x > x { |
|
sprite.x = x |
|
} |
|
} else if sprite.x > x { |
|
sprite.x -= moveSize |
|
if sprite.x < x { |
|
sprite.x = x |
|
} |
|
} |
|
if sprite.y < y { |
|
sprite.y += moveSize |
|
if sprite.y > y { |
|
sprite.y = y |
|
} |
|
} else if sprite.y > y { |
|
sprite.y -= moveSize |
|
if sprite.y < y { |
|
sprite.y = y |
|
} |
|
} |
|
b.ScheduleFrame() |
|
time.Sleep(moveDelay) |
|
} |
|
} |
|
|
|
// TODO do not add bear off pieces |
|
b.spaces[to] = append(b.spaces[to], sprite) |
|
for i, s := range b.spaces[from] { |
|
if s == sprite { |
|
b.spaces[from] = append(b.spaces[from][:i], b.spaces[from][i+1:]...) |
|
break |
|
} |
|
} |
|
b.moving = nil |
|
|
|
b.ScheduleFrame() |
|
|
|
time.Sleep(time.Second) |
|
} |
|
|
|
func (b *board) handlePieceMoves() { |
|
for u := range b.moveQueue { |
|
if u.from == -1 || u.to == -1 { |
|
b.V = u.v |
|
b.ProcessState() |
|
continue |
|
} |
|
|
|
from, to := u.from, u.to |
|
|
|
pieces := b.spaces[from] |
|
if len(pieces) == 0 { |
|
continue |
|
} |
|
|
|
sprite := pieces[len(pieces)-1] |
|
|
|
var moveAfter *Sprite |
|
if len(b.spaces[to]) == 1 { |
|
if sprite.colorWhite != b.spaces[to][0].colorWhite { |
|
moveAfter = b.spaces[to][0] |
|
} |
|
} |
|
|
|
b._movePiece(sprite, from, to, 1) |
|
if moveAfter != nil { |
|
toBar := 0 |
|
if b.V[fibs.StateTurn] == b.V[fibs.StatePlayerColor] { |
|
toBar = 25 // TODO how is this determined? |
|
} |
|
if b.V[fibs.StateDirection] == -1 { |
|
toBar = 25 - toBar |
|
} |
|
b._movePiece(moveAfter, to, toBar, 2) |
|
} |
|
|
|
b.positionCheckers() |
|
} |
|
} |
|
|
|
// Do not call directly |
|
func (b *board) movePiece(from int, to int) { |
|
b.moveQueue <- &stateUpdate{from, to, nil} |
|
} |
|
|
|
func (b *board) update() { |
|
if b.dragging == nil { |
|
// TODO allow grabbing multiple pieces by grabbing further down the stack |
|
|
|
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) { |
|
if b.dragging == nil { |
|
x, y := ebiten.CursorPosition() |
|
s := b.spriteAt(x, y) |
|
if s != nil { |
|
b.dragging = s |
|
} |
|
} |
|
} |
|
|
|
b.touchIDs = inpututil.AppendJustPressedTouchIDs(b.touchIDs[:0]) |
|
for _, id := range b.touchIDs { |
|
x, y := ebiten.TouchPosition(id) |
|
s := b.spriteAt(x, y) |
|
if s != nil { |
|
b.dragging = s |
|
b.dragTouchId = id |
|
} |
|
} |
|
} |
|
|
|
x, y := ebiten.CursorPosition() |
|
if b.dragTouchId != -1 { |
|
x, y = ebiten.TouchPosition(b.dragTouchId) |
|
} |
|
|
|
var dropped *Sprite |
|
if b.dragTouchId == -1 { |
|
if inpututil.IsMouseButtonJustReleased(ebiten.MouseButtonLeft) { |
|
dropped = b.dragging |
|
b.dragging = nil |
|
} |
|
} else if inpututil.IsTouchJustReleased(b.dragTouchId) { |
|
dropped = b.dragging |
|
b.dragging = nil |
|
} |
|
if dropped != nil { |
|
// TODO allow dragging anywhere outside of board to bear off |
|
// allow dragging on to bar to bear off |
|
|
|
index := b.spaceAt(x, y) |
|
if index >= 0 { |
|
for space, pieces := range b.spaces { |
|
for stackIndex, piece := range pieces { |
|
if piece == dropped { |
|
b.spaces[space] = append(b.spaces[space][:stackIndex], b.spaces[space][stackIndex+1:]...) |
|
b.spaces[index] = append(b.spaces[index], dropped) |
|
break |
|
} |
|
} |
|
} |
|
} |
|
b.positionCheckers() |
|
} |
|
|
|
if b.dragging != nil { |
|
sprite := b.dragging |
|
sprite.x = x - (sprite.w / 2) |
|
sprite.y = y - (sprite.h / 2) |
|
} |
|
}
|
|
|