Merge "croupier: Add Resume Game button"
diff --git a/go/src/hearts/assets/AcrossArrowBlue.png b/go/src/hearts/assets/AcrossArrowBlue.png
new file mode 100644
index 0000000..f569f18
--- /dev/null
+++ b/go/src/hearts/assets/AcrossArrowBlue.png
Binary files differ
diff --git a/go/src/hearts/assets/AcrossArrowGray.png b/go/src/hearts/assets/AcrossArrowGray.png
new file mode 100644
index 0000000..de97530
--- /dev/null
+++ b/go/src/hearts/assets/AcrossArrowGray.png
Binary files differ
diff --git a/go/src/hearts/assets/LeftArrowBlue.png b/go/src/hearts/assets/LeftArrowBlue.png
new file mode 100644
index 0000000..f949609
--- /dev/null
+++ b/go/src/hearts/assets/LeftArrowBlue.png
Binary files differ
diff --git a/go/src/hearts/assets/LeftArrowGray.png b/go/src/hearts/assets/LeftArrowGray.png
new file mode 100644
index 0000000..dddac1a
--- /dev/null
+++ b/go/src/hearts/assets/LeftArrowGray.png
Binary files differ
diff --git a/go/src/hearts/assets/PassPressed.png b/go/src/hearts/assets/PassPressed.png
new file mode 100644
index 0000000..c5a5a28
--- /dev/null
+++ b/go/src/hearts/assets/PassPressed.png
Binary files differ
diff --git a/go/src/hearts/assets/PassUnpressed.png b/go/src/hearts/assets/PassUnpressed.png
new file mode 100644
index 0000000..a90664e
--- /dev/null
+++ b/go/src/hearts/assets/PassUnpressed.png
Binary files differ
diff --git a/go/src/hearts/assets/QuitPressed.png b/go/src/hearts/assets/QuitPressed.png
index f42be9b..2f45344 100644
--- a/go/src/hearts/assets/QuitPressed.png
+++ b/go/src/hearts/assets/QuitPressed.png
Binary files differ
diff --git a/go/src/hearts/assets/QuitUnpressed.png b/go/src/hearts/assets/QuitUnpressed.png
index 47deba6..6d62380 100644
--- a/go/src/hearts/assets/QuitUnpressed.png
+++ b/go/src/hearts/assets/QuitUnpressed.png
Binary files differ
diff --git a/go/src/hearts/assets/RightArrowBlue.png b/go/src/hearts/assets/RightArrowBlue.png
new file mode 100644
index 0000000..b147d7e
--- /dev/null
+++ b/go/src/hearts/assets/RightArrowBlue.png
Binary files differ
diff --git a/go/src/hearts/assets/RightArrowGray.png b/go/src/hearts/assets/RightArrowGray.png
new file mode 100644
index 0000000..59866a0
--- /dev/null
+++ b/go/src/hearts/assets/RightArrowGray.png
Binary files differ
diff --git a/go/src/hearts/assets/SitSpotPressed.png b/go/src/hearts/assets/SitSpotPressed.png
index b4ada5f..c11470e 100644
--- a/go/src/hearts/assets/SitSpotPressed.png
+++ b/go/src/hearts/assets/SitSpotPressed.png
Binary files differ
diff --git a/go/src/hearts/assets/SitSpotUnpressed.png b/go/src/hearts/assets/SitSpotUnpressed.png
index 0991f49..8943bb6 100644
--- a/go/src/hearts/assets/SitSpotUnpressed.png
+++ b/go/src/hearts/assets/SitSpotUnpressed.png
Binary files differ
diff --git a/go/src/hearts/assets/StartGray.png b/go/src/hearts/assets/StartGray.png
index aced4e1..89e1dee 100644
--- a/go/src/hearts/assets/StartGray.png
+++ b/go/src/hearts/assets/StartGray.png
Binary files differ
diff --git a/go/src/hearts/assets/WatchSpotPressed.png b/go/src/hearts/assets/WatchSpotPressed.png
index 6060786..c51cc49 100644
--- a/go/src/hearts/assets/WatchSpotPressed.png
+++ b/go/src/hearts/assets/WatchSpotPressed.png
Binary files differ
diff --git a/go/src/hearts/assets/WatchSpotUnpressed.png b/go/src/hearts/assets/WatchSpotUnpressed.png
index 37b446c..67413e5 100644
--- a/go/src/hearts/assets/WatchSpotUnpressed.png
+++ b/go/src/hearts/assets/WatchSpotUnpressed.png
Binary files differ
diff --git a/go/src/hearts/img/reposition/reposition.go b/go/src/hearts/img/reposition/reposition.go
index 4f32f36..0e7a6e5 100644
--- a/go/src/hearts/img/reposition/reposition.go
+++ b/go/src/hearts/img/reposition/reposition.go
@@ -179,19 +179,14 @@
}
// Animation for the 'pass' action, when app is in the hand view
-func AnimateHandCardPass(ch chan bool, animImages []*staticimg.StaticImg, animCards []*card.Card, u *uistate.UIState) {
- for _, i := range animImages {
+func AnimateHandCardPass(ch chan bool, animImages []*staticimg.StaticImg, u *uistate.UIState) {
+ for counter, i := range animImages {
dims := i.GetDimensions()
to := coords.MakeVec(i.GetCurrent().X, i.GetCurrent().Y-u.WindowSize.Y)
- AnimateImageNoChannel(i, to, dims, u)
- }
- for i, c := range animCards {
- dims := c.GetDimensions()
- to := coords.MakeVec(c.GetCurrent().X, c.GetCurrent().Y-u.WindowSize.Y)
- if i < len(animCards)-1 {
- animateCardNoChannel(c, to, dims, u)
+ if counter < len(animImages)-1 {
+ AnimateImageNoChannel(i, to, dims, u)
} else {
- animateCardMovement(ch, c, to, dims, u)
+ animateImageMovement(ch, i, to, dims, u)
}
}
}
@@ -211,18 +206,21 @@
// Animation to bring in the take slot
func AnimateInTake(u *uistate.UIState) {
imgs := append(u.Other, u.DropTargets...)
- passedCards := u.CurTable.GetPlayers()[u.CurPlayerIndex].GetPassedTo()
for _, i := range imgs {
dims := i.GetDimensions()
to := coords.MakeVec(i.GetCurrent().X, i.GetCurrent().Y+u.WindowSize.Y)
AnimateImageNoChannel(i, to, dims, u)
}
- if !u.SequentialPhases || u.CurTable.AllDonePassing() {
- for _, c := range passedCards {
- dims := c.GetDimensions()
- to := coords.MakeVec(c.GetCurrent().X, c.GetCurrent().Y+u.WindowSize.Y)
- animateCardNoChannel(c, to, dims, u)
- }
+}
+
+// Animation to bring in the pass slot
+func AnimateInPass(u *uistate.UIState) {
+ imgs := append(u.Other, u.DropTargets...)
+ imgs = append(imgs, u.Buttons["pass"])
+ for _, i := range imgs {
+ dims := i.GetDimensions()
+ to := coords.MakeVec(i.GetCurrent().X, i.GetCurrent().Y+u.WindowSize.Y)
+ AnimateImageNoChannel(i, to, dims, u)
}
}
@@ -238,13 +236,16 @@
BringNodeToFront(img.GetNode(), u)
}
imgs := []*staticimg.StaticImg{u.BackgroundImgs[0], u.DropTargets[0]}
- for _, i := range imgs {
+ for counter, i := range imgs {
dims := i.GetDimensions()
to := coords.MakeVec(i.GetCurrent().X, i.GetCurrent().Y-u.WindowSize.Y)
- AnimateImageNoChannel(i, to, dims, u)
+ if counter == len(imgs)-1 {
+ animateImageMovement(ch, i, to, dims, u)
+ } else {
+ AnimateImageNoChannel(i, to, dims, u)
+ }
+
}
- to := coords.MakeVec(animCard.GetCurrent().X, animCard.GetCurrent().Y-u.WindowSize.Y)
- animateCardMovement(ch, animCard, to, animCard.GetDimensions(), u)
}
// Animation to bring in the play slot when app is in the hand view and it is the player's turn
@@ -281,17 +282,10 @@
topOfBanner := u.WindowSize.Y - 4*u.CardDim.Y - 5*u.Padding - u.BottomPadding - 40
tableImgs := make([]*staticimg.StaticImg, 0)
bannerImgs := make([]*staticimg.StaticImg, 0)
- cards := make([]*card.Card, 0)
bannerImgs = append(bannerImgs, u.Other...)
bannerImgs = append(bannerImgs, u.Buttons["toggleSplit"])
tableImgs = append(tableImgs, u.DropTargets...)
tableImgs = append(tableImgs, u.BackgroundImgs[:u.NumPlayers]...)
- cards = append(cards, u.TableCards...)
- for _, card := range cards {
- from := card.GetCurrent()
- to := coords.MakeVec(from.X, from.Y+topOfBanner)
- animateCardNoChannel(card, to, card.GetDimensions(), u)
- }
for _, img := range tableImgs {
from := img.GetCurrent()
to := coords.MakeVec(from.X, from.Y+topOfBanner)
@@ -326,17 +320,10 @@
topOfBanner := u.WindowSize.Y - 4*u.CardDim.Y - 5*u.Padding - u.BottomPadding - 40
tableImgs := make([]*staticimg.StaticImg, 0)
bannerImgs := make([]*staticimg.StaticImg, 0)
- cards := make([]*card.Card, 0)
bannerImgs = append(bannerImgs, u.Other...)
bannerImgs = append(bannerImgs, u.Buttons["toggleSplit"])
tableImgs = append(tableImgs, u.DropTargets...)
tableImgs = append(tableImgs, u.BackgroundImgs[:u.NumPlayers]...)
- cards = append(cards, u.TableCards...)
- for _, card := range cards {
- from := card.GetCurrent()
- to := coords.MakeVec(from.X, from.Y-topOfBanner)
- animateCardNoChannel(card, to, card.GetDimensions(), u)
- }
for _, img := range tableImgs {
from := img.GetCurrent()
to := coords.MakeVec(from.X, from.Y-topOfBanner)
@@ -417,8 +404,16 @@
newVec := curXY.PlusVec(XYStep)
dimVec := curDim.PlusVec(dimStep)
animImage.Move(newVec, dimVec, eng)
+ card := animImage.GetCardHere()
+ if card != nil {
+ card.Move(newVec, dimVec, eng)
+ }
} else if iteration == animationFrameCount {
animImage.Move(endPos, endDim, eng)
+ card := animImage.GetCardHere()
+ if card != nil {
+ card.Move(endPos, endDim, eng)
+ }
c <- true
}
})
@@ -439,8 +434,16 @@
newVec := curXY.PlusVec(XYStep)
dimVec := curDim.PlusVec(dimStep)
animImage.Move(newVec, dimVec, eng)
+ card := animImage.GetCardHere()
+ if card != nil {
+ card.Move(newVec, dimVec, eng)
+ }
} else if iteration == animationFrameCount {
animImage.Move(endPos, endDim, eng)
+ card := animImage.GetCardHere()
+ if card != nil {
+ card.Move(endPos, endDim, eng)
+ }
}
})
}
diff --git a/go/src/hearts/img/texture/texture.go b/go/src/hearts/img/texture/texture.go
index fd96e76..aacf862 100644
--- a/go/src/hearts/img/texture/texture.go
+++ b/go/src/hearts/img/texture/texture.go
@@ -275,6 +275,8 @@
"NewRoundPressed.png", "NewRoundUnpressed.png", "JoinGamePressed.png", "JoinGameUnpressed.png", "Period.png",
"SitSpotPressed.png", "SitSpotUnpressed.png", "WatchSpotPressed.png", "WatchSpotUnpressed.png", "StartBlue.png", "StartGray.png",
"StartBluePressed.png", "Restart.png", "Visibility.png", "VisibilityOff.png", "QuitPressed.png", "QuitUnpressed.png",
+ "PassPressed.png", "PassUnpressed.png", "RightArrowBlue.png", "LeftArrowBlue.png", "AcrossArrowBlue.png", "RightArrowGray.png",
+ "LeftArrowGray.png", "AcrossArrowGray.png",
}
for _, f := range boundedImgs {
a, err := asset.Open(f)
diff --git a/go/src/hearts/img/uistate/uistate.go b/go/src/hearts/img/uistate/uistate.go
index bb5c898..cbdb649 100644
--- a/go/src/hearts/img/uistate/uistate.go
+++ b/go/src/hearts/img/uistate/uistate.go
@@ -134,7 +134,7 @@
Padding: float32(5),
CurView: None,
Done: false,
- Debug: false,
+ Debug: true,
SequentialPhases: true,
SwitchingViews: false,
UserData: make(map[int]map[string]interface{}),
diff --git a/go/src/hearts/img/view/view.go b/go/src/hearts/img/view/view.go
index 29d26bd..fcd4814 100644
--- a/go/src/hearts/img/view/view.go
+++ b/go/src/hearts/img/view/view.go
@@ -28,11 +28,13 @@
// Arrange view: For seating players
func LoadArrangeView(u *uistate.UIState) {
- u.CurView = uistate.Arrange
- <-time.After(1 * time.Second)
- reposition.ResetAnims(u)
+ if len(u.AnimChans) > 0 {
+ reposition.ResetAnims(u)
+ <-time.After(time.Second)
+ }
resetImgs(u)
resetScene(u)
+ u.CurView = uistate.Arrange
addHeader(u)
watchImg := u.Texs["WatchSpotUnpressed.png"]
watchAlt := u.Texs["WatchSpotPressed.png"]
@@ -58,19 +60,26 @@
quitPos := coords.MakeVec(u.Padding, u.TopPadding+10)
u.Buttons["exit"] = texture.MakeImgWithAlt(quitImg, quitAlt, quitPos, quitDim, true, u)
if u.IsOwner {
- startImg := u.Texs["StartGray.png"]
- startAlt := u.Texs["StartBlue.png"]
+ startImg := u.Texs["StartBlue.png"]
+ startAlt := u.Texs["StartBluePressed.png"]
startDim := coords.MakeVec(2*u.CardDim.X, u.CardDim.Y)
startPos := u.WindowSize.MinusVec(startDim).Minus(u.BottomPadding)
- display := !u.CurTable.AllReadyForNewRound()
- u.Buttons["start"] = texture.MakeImgWithAlt(startImg, startAlt, startPos, startDim, display, u)
+ display := u.CurTable.AllReadyForNewRound()
+ u.Buttons["start"] = texture.MakeImgWithAlt(startImg, startAlt, startPos, startDim, true, u)
+ var emptyTex sprite.SubTex
+ if !display {
+ u.Eng.SetSubTex(u.Buttons["start"].GetNode(), emptyTex)
+ }
}
}
// Waiting view: Displays the word "Waiting". To be displayed when players are waiting for a new round to be dealt
// TODO(emshack): Integrate this with Arrange view and Score view so that a separate screen is not necessary
func LoadWaitingView(u *uistate.UIState) {
- reposition.ResetAnims(u)
+ if len(u.AnimChans) > 0 {
+ reposition.ResetAnims(u)
+ <-time.After(time.Second)
+ }
resetImgs(u)
resetScene(u)
center := u.WindowSize.DividedBy(2)
@@ -84,10 +93,13 @@
// Discovery view: Displays a menu of possible games to join
func LoadDiscoveryView(u *uistate.UIState) {
- u.CurView = uistate.Discovery
- reposition.ResetAnims(u)
+ if len(u.AnimChans) > 0 {
+ reposition.ResetAnims(u)
+ <-time.After(time.Second)
+ }
resetImgs(u)
resetScene(u)
+ u.CurView = uistate.Discovery
newGameImg := u.Texs["NewGameUnpressed.png"]
newGameAlt := u.Texs["NewGamePressed.png"]
newGameDim := coords.MakeVec(2*u.CardDim.X, u.CardDim.Y)
@@ -128,10 +140,13 @@
// Table View: Displays the table. Intended for public devices
func LoadTableView(u *uistate.UIState) {
- u.CurView = uistate.Table
- reposition.ResetAnims(u)
+ if len(u.AnimChans) > 0 {
+ reposition.ResetAnims(u)
+ <-time.After(time.Second)
+ }
resetImgs(u)
resetScene(u)
+ u.CurView = uistate.Table
scaler := float32(6)
maxWidth := 4 * u.TableCardDim.X
// adding four drop targets for trick
@@ -354,7 +369,6 @@
}
// Decides which view of the player's hand to load based on what steps of the round they have completed
-// Likely just for debugging
func LoadPassOrTakeOrPlay(u *uistate.UIState) {
p := u.CurTable.GetPlayers()[u.CurPlayerIndex]
if p.GetDoneTaking() || u.CurTable.GetDir() == direction.None {
@@ -368,10 +382,13 @@
// Score View: Shows current player standings at the end of every round, including the end of the game
func LoadScoreView(roundScores, winners []int, u *uistate.UIState) {
- u.CurView = uistate.Score
- reposition.ResetAnims(u)
+ if len(u.AnimChans) > 0 {
+ reposition.ResetAnims(u)
+ <-time.After(time.Second)
+ }
resetImgs(u)
resetScene(u)
+ u.CurView = uistate.Score
addHeader(u)
addScoreViewHeaderText(u)
addPlayerScores(roundScores, u)
@@ -380,25 +397,32 @@
// Pass View: Shows player's hand and allows them to pass cards
func LoadPassView(u *uistate.UIState) {
- u.CurView = uistate.Pass
- reposition.ResetAnims(u)
+ if len(u.AnimChans) > 0 {
+ reposition.ResetAnims(u)
+ <-time.After(time.Second)
+ }
resetImgs(u)
resetScene(u)
+ u.CurView = uistate.Pass
addHeader(u)
addGrayPassBar(u)
- addPassDrops(u)
+ //addPassDrops(u)
addHand(u)
if u.Debug {
addDebugBar(u)
}
+ reposition.AnimateInPass(u)
}
// Take View: Shows player's hand and allows them to take the cards that have been passed to them
func LoadTakeView(u *uistate.UIState) {
- u.CurView = uistate.Take
- reposition.ResetAnims(u)
+ if len(u.AnimChans) > 0 {
+ reposition.ResetAnims(u)
+ <-time.After(time.Second)
+ }
resetImgs(u)
resetScene(u)
+ u.CurView = uistate.Take
addHeader(u)
addGrayTakeBar(u)
addHand(u)
@@ -412,11 +436,13 @@
// Play View: Shows player's hand and allows them to play cards
func LoadPlayView(u *uistate.UIState) {
- u.CurView = uistate.Play
- fmt.Println("In play view")
- reposition.ResetAnims(u)
+ if len(u.AnimChans) > 0 {
+ reposition.ResetAnims(u)
+ <-time.After(time.Second)
+ }
resetImgs(u)
resetScene(u)
+ u.CurView = uistate.Play
addPlaySlot(u)
addHand(u)
addPlayHeader(getTurnText(u), false, u)
@@ -437,11 +463,13 @@
}
func LoadSplitView(reloading bool, u *uistate.UIState) {
- u.CurView = uistate.Split
- fmt.Println("In split view")
- reposition.ResetAnims(u)
+ if len(u.AnimChans) > 0 {
+ reposition.ResetAnims(u)
+ <-time.After(time.Second)
+ }
resetImgs(u)
resetScene(u)
+ u.CurView = uistate.Split
addPlayHeader(getTurnText(u), !reloading, u)
addSplitViewPlayerIcons(!reloading, u)
SetNumTricksHand(u)
@@ -536,8 +564,8 @@
dropTargetY -= topOfBanner
}
dropTargetPos := coords.MakeVec(dropTargetX, dropTargetY)
- u.DropTargets = append(u.DropTargets,
- texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u))
+ d := texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u)
+ u.DropTargets = append(u.DropTargets, d)
// first player icon
playerIconImage := uistate.GetAvatar(u.CurPlayerIndex, u)
u.BackgroundImgs = append(u.BackgroundImgs,
@@ -548,6 +576,7 @@
texture.PopulateCardImage(dropCard, u)
dropCard.SetInitial(dropTargetPos)
dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
+ d.SetCardHere(dropCard)
u.TableCards = append(u.TableCards, dropCard)
}
// second drop target
@@ -557,8 +586,8 @@
}
dropTargetX = splitWindowSize.X/2 - 3*u.CardDim.X/2 - u.Padding
dropTargetPos = coords.MakeVec(dropTargetX, dropTargetY)
- u.DropTargets = append(u.DropTargets,
- texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u))
+ d = texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u)
+ u.DropTargets = append(u.DropTargets, d)
// second player icon
playerIconImage = uistate.GetAvatar((u.CurPlayerIndex+1)%u.NumPlayers, u)
u.BackgroundImgs = append(u.BackgroundImgs,
@@ -569,6 +598,7 @@
texture.PopulateCardImage(dropCard, u)
dropCard.SetInitial(dropTargetPos)
dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
+ d.SetCardHere(dropCard)
u.TableCards = append(u.TableCards, dropCard)
}
// third drop target
@@ -578,8 +608,8 @@
dropTargetY -= topOfBanner
}
dropTargetPos = coords.MakeVec(dropTargetX, dropTargetY)
- u.DropTargets = append(u.DropTargets,
- texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u))
+ d = texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u)
+ u.DropTargets = append(u.DropTargets, d)
// third player icon
playerIconImage = uistate.GetAvatar((u.CurPlayerIndex+2)%u.NumPlayers, u)
u.BackgroundImgs = append(u.BackgroundImgs,
@@ -590,6 +620,7 @@
texture.PopulateCardImage(dropCard, u)
dropCard.SetInitial(dropTargetPos)
dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
+ d.SetCardHere(dropCard)
u.TableCards = append(u.TableCards, dropCard)
}
// fourth drop target
@@ -599,8 +630,8 @@
}
dropTargetX = splitWindowSize.X/2 + u.CardDim.X/2 + u.Padding
dropTargetPos = coords.MakeVec(dropTargetX, dropTargetY)
- u.DropTargets = append(u.DropTargets,
- texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u))
+ d = texture.MakeImgWithAlt(dropTargetImage, dropTargetAlt, dropTargetPos, dropTargetDimensions, true, u)
+ u.DropTargets = append(u.DropTargets, d)
// fourth player icon
playerIconImage = uistate.GetAvatar((u.CurPlayerIndex+3)%u.NumPlayers, u)
u.BackgroundImgs = append(u.BackgroundImgs,
@@ -611,6 +642,7 @@
texture.PopulateCardImage(dropCard, u)
dropCard.SetInitial(dropTargetPos)
dropCard.Move(dropTargetPos, dropTargetDimensions, u.Eng)
+ d.SetCardHere(dropCard)
u.TableCards = append(u.TableCards, dropCard)
}
}
@@ -688,28 +720,61 @@
// adding gray bar
grayBarImg := u.Texs["RoundedRectangle-Gray.png"]
blueBarImg := u.Texs["RoundedRectangle-LBlue.png"]
- grayBarDim := u.WindowSize.Minus(4 * u.BottomPadding)
- grayBarPos := coords.MakeVec(2*u.BottomPadding, 40-grayBarDim.Y+u.TopPadding)
+ topOfHand := u.WindowSize.Y - 5*(u.CardDim.Y+u.Padding) - (2 * u.Padding / 5) - u.BottomPadding
+ grayBarDim := coords.MakeVec(u.WindowSize.X-4*u.BottomPadding, topOfHand+u.CardDim.Y)
+ grayBarPos := coords.MakeVec(2*u.BottomPadding, -u.WindowSize.Y-20)
u.Other = append(u.Other,
texture.MakeImgWithAlt(grayBarImg, blueBarImg, grayBarPos, grayBarDim, true, u))
// adding name
var receivingPlayer int
+ var arrowImg sprite.SubTex
+ var arrowAlt sprite.SubTex
switch u.CurTable.GetDir() {
case direction.Right:
receivingPlayer = (u.CurPlayerIndex + 3) % u.NumPlayers
+ arrowImg = u.Texs["RightArrowGray.png"]
+ arrowAlt = u.Texs["RightArrowBlue.png"]
case direction.Left:
receivingPlayer = (u.CurPlayerIndex + 1) % u.NumPlayers
+ arrowImg = u.Texs["LeftArrowGray.png"]
+ arrowAlt = u.Texs["LeftArrowBlue.png"]
case direction.Across:
receivingPlayer = (u.CurPlayerIndex + 2) % u.NumPlayers
+ arrowImg = u.Texs["AcrossArrowGray.png"]
+ arrowAlt = u.Texs["AcrossArrowBlue.png"]
}
+ arrowDim := u.CardDim
name := uistate.GetName(receivingPlayer, u)
color := "Gray"
altColor := "LBlue"
- center := coords.MakeVec(u.WindowSize.X/2, u.TopPadding+5)
+ center := coords.MakeVec(u.WindowSize.X/2-arrowDim.X/2, 20-u.WindowSize.Y)
scaler := float32(3)
- maxWidth := grayBarDim.X - 2*u.Padding
- nameImgs := texture.MakeStringImgCenterAlign(name, color, altColor, true, center, scaler, maxWidth, u)
+ maxWidth := grayBarDim.X - 3*u.Padding - arrowDim.X
+ nameImgs := texture.MakeStringImgCenterAlign(fmt.Sprintf("Pass to %s", name), color, altColor, true, center, scaler, maxWidth, u)
u.Other = append(u.Other, nameImgs...)
+ imgBeforeArrow := u.Other[len(u.Other)-1]
+ ibaDim := imgBeforeArrow.GetDimensions()
+ ibaPos := imgBeforeArrow.GetCurrent()
+ arrowPos := coords.MakeVec(ibaPos.X+ibaDim.X+u.Padding, ibaPos.Y+ibaDim.Y/2-arrowDim.Y/2)
+ u.Other = append(u.Other,
+ texture.MakeImgWithAlt(arrowImg, arrowAlt, arrowPos, arrowDim, true, u))
+ numDrops := 3
+ dropXStart := (u.WindowSize.X - (float32(numDrops)*u.CardDim.X + (float32(numDrops)-1)*u.Padding)) / 2
+ dropImg := u.Texs["trickDrop.png"]
+ for i := 0; i < numDrops; i++ {
+ dropX := dropXStart + float32(i)*(u.Padding+u.CardDim.X)
+ dropPos := coords.MakeVec(dropX, topOfHand-u.Padding-u.WindowSize.Y-20)
+ d := texture.MakeImgWithoutAlt(dropImg, dropPos, u.CardDim, u)
+ u.DropTargets = append(u.DropTargets, d)
+ }
+ passImg := u.Texs["PassUnpressed.png"]
+ passAlt := u.Texs["PassPressed.png"]
+ passDim := coords.MakeVec(2*u.CardDim.X, 2*u.CardDim.Y/3)
+ passPos := coords.MakeVec((u.WindowSize.X-passDim.X)/2, topOfHand-2*u.Padding-u.WindowSize.Y-20-passDim.Y)
+ b := texture.MakeImgWithAlt(passImg, passAlt, passPos, passDim, true, u)
+ var emptyTex sprite.SubTex
+ u.Eng.SetSubTex(b.GetNode(), emptyTex)
+ u.Buttons["pass"] = b
}
func addGrayTakeBar(u *uistate.UIState) {
diff --git a/go/src/hearts/sync/client.go b/go/src/hearts/sync/client.go
index 63f57fe..63d6442 100644
--- a/go/src/hearts/sync/client.go
+++ b/go/src/hearts/sync/client.go
@@ -95,7 +95,7 @@
}
// Joins gamelog syncgroup
-func JoinLogSyncgroup(ch chan bool, logName string, u *uistate.UIState) {
+func JoinLogSyncgroup(logName string, u *uistate.UIState) bool {
fmt.Println("Joining gamelog syncgroup")
u.IsOwner = false
app := u.Service.App(AppName)
@@ -105,13 +105,13 @@
_, err := logSg.Join(u.Ctx, myInfoJoiner)
if err != nil {
fmt.Println("SYNCGROUP JOIN ERROR: ", err)
- ch <- false
+ return false
} else {
fmt.Println("Syncgroup joined")
if u.LogSG != logName {
resetGame(logName, false, u)
}
- ch <- true
+ return true
}
}
diff --git a/go/src/hearts/sync/server.go b/go/src/hearts/sync/server.go
index 03887be..38ed5f4 100644
--- a/go/src/hearts/sync/server.go
+++ b/go/src/hearts/sync/server.go
@@ -116,7 +116,7 @@
}
// Creates a new gamelog syncgroup
-func CreateLogSyncgroup(ch chan string, u *uistate.UIState) {
+func CreateLogSyncgroup(u *uistate.UIState) (string, string) {
fmt.Println("Creating Log Syncgroup")
u.IsOwner = true
// Generate random gameID information to advertise this game
@@ -131,7 +131,6 @@
if err != nil {
fmt.Println("WE HAVE A HUGE PROBLEM:", err)
}
- ch <- string(value)
// Create gamelog syncgroup
logSGName := fmt.Sprintf("%s/croupier/%s/%%%%sync/gaming-%d", MountPoint, SBName, gameID)
allAccess := access.AccessList{In: []security.BlessingPattern{"..."}}
@@ -163,21 +162,21 @@
_, err2 := logSG.Join(u.Ctx, myInfoCreator)
if err2 != nil {
fmt.Println("SYNCGROUP JOIN ERROR: ", err2)
- ch <- ""
+ return string(value), ""
} else {
- ch <- logSGName
+ return string(value), logSGName
}
} else {
fmt.Println("Syncgroup created")
if logSGName != u.LogSG {
resetGame(logSGName, true, u)
}
- ch <- logSGName
+ return string(value), logSGName
}
}
// Creates a new user settings syncgroup
-func CreateSettingsSyncgroup(ch chan string, u *uistate.UIState) {
+func CreateSettingsSyncgroup(u *uistate.UIState) string {
fmt.Println("Creating Settings Syncgroup")
allAccess := access.AccessList{In: []security.BlessingPattern{"..."}}
permissions := access.Permissions{
@@ -209,13 +208,13 @@
_, err2 := settingsSG.Join(u.Ctx, myInfoCreator)
if err2 != nil {
fmt.Println("SYNCGROUP JOIN ERROR: ", err2)
- ch <- ""
+ return ""
} else {
- ch <- settingsSGName
+ return settingsSGName
}
} else {
fmt.Println("Syncgroup created")
- ch <- settingsSGName
+ return settingsSGName
}
}
diff --git a/go/src/hearts/sync/util.go b/go/src/hearts/sync/util.go
index 8c6b586..b1da39f 100644
--- a/go/src/hearts/sync/util.go
+++ b/go/src/hearts/sync/util.go
@@ -8,12 +8,12 @@
const (
// switch back to my mountpoint with the following code:
- MountPoint = "users/emshack@google.com"
- //MountPoint = "/192.168.86.254:8101"
- UserID = 2222
+ //MountPoint = "users/emshack@google.com"
+ MountPoint = "/192.168.86.254:8101"
+ UserID = 1111
UserColor = 16777215
- UserAvatar = "player1.jpeg"
- UserName = "Bob"
+ UserAvatar = "player0.jpeg"
+ UserName = "Alice"
SBName = "syncbase"
AppName = "app"
DbName = "db"
diff --git a/go/src/hearts/sync/watch.go b/go/src/hearts/sync/watch.go
index 5e489f7..e458cf4 100644
--- a/go/src/hearts/sync/watch.go
+++ b/go/src/hearts/sync/watch.go
@@ -362,12 +362,8 @@
if u.CurTable.AllReadyForNewRound() && u.IsOwner {
if u.CurView == uistate.Arrange {
b := u.Buttons["start"]
- if !b.GetDisplayingImage() {
- b.SetImage(b.GetAlt())
- b.SetAlt(u.Texs["StartBluePressed.png"])
- u.Eng.SetSubTex(b.GetNode(), b.GetImage())
- b.SetDisplayingImage(true)
- }
+ u.Eng.SetSubTex(b.GetNode(), b.GetImage())
+ b.SetDisplayingImage(true)
if u.SGChan != nil {
u.SGChan <- true
u.SGChan = nil
diff --git a/go/src/hearts/touchhandler/touchhandler.go b/go/src/hearts/touchhandler/touchhandler.go
index f2cc3aa..f66ae14 100644
--- a/go/src/hearts/touchhandler/touchhandler.go
+++ b/go/src/hearts/touchhandler/touchhandler.go
@@ -14,7 +14,6 @@
"golang.org/x/mobile/event/touch"
"golang.org/x/mobile/exp/sprite"
- "hearts/img/coords"
"hearts/img/reposition"
"hearts/img/staticimg"
"hearts/img/uistate"
@@ -51,9 +50,7 @@
case uistate.Table:
switch t.Type {
case touch.TypeBegin:
- if u.Debug {
- beginClickTable(t, u)
- }
+ beginClickTable(t, u)
}
case uistate.Pass:
switch t.Type {
@@ -138,13 +135,8 @@
pressed := unpressButtons(u)
for _, button := range pressed {
if button == u.Buttons["newGame"] {
- logCh := make(chan string)
- settingsCh := make(chan string)
- go sync.CreateLogSyncgroup(logCh, u)
- go sync.CreateSettingsSyncgroup(settingsCh, u)
- gameStartData := <-logCh
- logName := <-logCh
- settingsName := <-settingsCh
+ gameStartData, logName := sync.CreateLogSyncgroup(u)
+ settingsName := sync.CreateSettingsSyncgroup(u)
if logName != "" && settingsName != "" {
sync.LogSettingsName(settingsName, u)
u.ScanChan <- true
@@ -156,13 +148,10 @@
} else {
for _, b := range u.Buttons {
if button == b {
- joinLogDone := make(chan bool)
logAddr := b.GetInfo()
- go sync.JoinLogSyncgroup(joinLogDone, logAddr, u)
- if success := <-joinLogDone; success {
- settingsCh := make(chan string)
- go sync.CreateSettingsSyncgroup(settingsCh, u)
- sgName := <-settingsCh
+ success := sync.JoinLogSyncgroup(logAddr, u)
+ if success {
+ sgName := sync.CreateSettingsSyncgroup(u)
if sgName != "" {
sync.LogSettingsName(sgName, u)
}
@@ -187,7 +176,7 @@
if u.CurTable.AllReadyForNewRound() {
pressButton(b, u)
}
- } else if u.CurPlayerIndex < 0 {
+ } else if u.CurPlayerIndex < 0 || u.Debug {
for _, button := range u.Buttons {
if b == button {
pressButton(b, u)
@@ -232,17 +221,15 @@
}
} else {
for key, button := range u.Buttons {
- if b == button && u.CurPlayerIndex < 0 {
+ if b == button && (u.CurPlayerIndex < 0 || u.Debug) {
if key == "joinTable" {
u.CurPlayerIndex = 4
sync.LogPlayerNum(u)
} else {
playerNum := strings.Split(key, "-")[1]
- if u.CurPlayerIndex < 0 {
- u.CurPlayerIndex, _ = strconv.Atoi(playerNum)
- sync.LogReady(u)
- sync.LogPlayerNum(u)
- }
+ u.CurPlayerIndex, _ = strconv.Atoi(playerNum)
+ sync.LogReady(u)
+ sync.LogPlayerNum(u)
}
}
}
@@ -268,51 +255,21 @@
view.LoadTableView(u)
} else if b == u.Buttons["hand"] {
view.LoadPassOrTakeOrPlay(u)
- } else if b == u.Buttons["dragPass"] {
- if b.GetDisplayingImage() {
- u.CurImg = b
- for _, img := range u.Other {
- u.Eng.SetSubTex(img.GetNode(), img.GetAlt())
- img.SetDisplayingImage(false)
- }
- blueBanner := u.Other[0]
- reposition.BringNodeToFront(u.BackgroundImgs[1].GetNode(), u)
- reposition.BringNodeToFront(b.GetNode(), u)
- for _, d := range u.DropTargets {
- reposition.BringNodeToFront(d.GetCardHere().GetNode(), u)
- }
- if blueBanner.GetNode().Arranger == nil {
- finalX := blueBanner.GetInitial().X
- finalY := b.GetInitial().Y + b.GetDimensions().Y - blueBanner.GetDimensions().Y
- finalPos := coords.MakeVec(finalX, finalY)
- reposition.AnimateImageNoChannel(blueBanner, finalPos, blueBanner.GetDimensions(), u)
- }
- }
+ } else if b == u.Buttons["pass"] {
+ pressButton(b, u)
}
}
}
func moveClickPass(t touch.Event, u *uistate.UIState) {
- if u.CurImg != nil {
- imgs := make([]*staticimg.StaticImg, 0)
- cards := make([]*card.Card, 0)
- pullTab := u.Buttons["dragPass"]
- blueBanner := u.BackgroundImgs[1]
- imgs = append(imgs, pullTab)
- imgs = append(imgs, blueBanner)
- for _, d := range u.DropTargets {
- imgs = append(imgs, d)
- cards = append(cards, d.GetCardHere())
- }
- for i := 2; i < 7; i++ {
- text := u.BackgroundImgs[i]
- u.Eng.SetSubTex(text.GetNode(), text.GetAlt())
- text.SetDisplayingImage(false)
- }
- reposition.DragImgs(t, cards, imgs, u)
- } else if u.CurCard != nil {
+ if u.CurCard != nil {
reposition.DragCard(t, u)
}
+ curPressed := findClickedButton(t, u)
+ alreadyPressed := getPressed(u)
+ if len(alreadyPressed) > 0 && len(curPressed) == 0 {
+ unpressButtons(u)
+ }
}
func endClickPass(t touch.Event, u *uistate.UIState) {
@@ -331,32 +288,50 @@
readyToPass = false
}
}
- pullTab := u.Buttons["dragPass"]
+ passButton := u.Buttons["pass"]
if readyToPass {
- u.Eng.SetSubTex(pullTab.GetNode(), pullTab.GetImage())
- pullTab.SetDisplayingImage(true)
- } else {
- u.Eng.SetSubTex(pullTab.GetNode(), pullTab.GetAlt())
- pullTab.SetDisplayingImage(false)
- }
- } else if u.CurImg != nil && touchingStaticImg(t, u.Other[0], u) {
- ch := make(chan bool)
- success := passCards(ch, u.CurPlayerIndex, u)
- quit := make(chan bool)
- u.AnimChans = append(u.AnimChans, quit)
- go func() {
- onDone := func() {
- if !success {
- fmt.Println("Invalid pass")
- } else {
- view.LoadTakeView(u)
+ if passButton.GetDisplayingImage() {
+ u.Eng.SetSubTex(passButton.GetNode(), passButton.GetImage())
+ passButton.SetDisplayingImage(true)
+ }
+ for _, img := range u.Other {
+ if img.GetDisplayingImage() {
+ u.Eng.SetSubTex(img.GetNode(), img.GetAlt())
+ img.SetDisplayingImage(false)
}
}
- reposition.SwitchOnChan(ch, quit, onDone, u)
- }()
+ } else {
+ var emptyTex sprite.SubTex
+ u.Eng.SetSubTex(passButton.GetNode(), emptyTex)
+ passButton.SetDisplayingImage(true)
+ for _, img := range u.Other {
+ if !img.GetDisplayingImage() {
+ u.Eng.SetSubTex(img.GetNode(), img.GetImage())
+ img.SetDisplayingImage(true)
+ }
+ }
+ }
+ }
+ pressed := unpressButtons(u)
+ for _, p := range pressed {
+ if p == u.Buttons["pass"] {
+ ch := make(chan bool)
+ success := passCards(ch, u.CurPlayerIndex, u)
+ quit := make(chan bool)
+ u.AnimChans = append(u.AnimChans, quit)
+ go func() {
+ onDone := func() {
+ if !success {
+ fmt.Println("Invalid pass")
+ } else {
+ view.LoadTakeView(u)
+ }
+ }
+ reposition.SwitchOnChan(ch, quit, onDone, u)
+ }()
+ }
}
u.CurCard = nil
- u.CurImg = nil
}
func beginClickTake(t touch.Event, u *uistate.UIState) {
@@ -554,12 +529,10 @@
// returns true if pass was successful
func passCards(ch chan bool, playerId int, u *uistate.UIState) bool {
cardsPassed := make([]*card.Card, 0)
- dropsToReset := make([]*staticimg.StaticImg, 0)
for _, d := range u.DropTargets {
passCard := d.GetCardHere()
if passCard != nil {
cardsPassed = append(cardsPassed, passCard)
- dropsToReset = append(dropsToReset, d)
}
}
// if the pass is not valid, don't pass any cards
@@ -570,19 +543,9 @@
for !success {
success = sync.LogPass(u, cardsPassed)
}
- // UI component
- pullTab := u.Buttons["dragPass"]
- blueBanner := u.BackgroundImgs[1]
- imgs := []*staticimg.StaticImg{pullTab, blueBanner}
- for _, d := range dropsToReset {
- imgs = append(imgs, d)
- d.SetCardHere(nil)
- }
- var blankTex sprite.SubTex
- for _, i := range imgs {
- u.Eng.SetSubTex(i.GetNode(), blankTex)
- }
- reposition.AnimateHandCardPass(ch, u.Other, cardsPassed, u)
+ imgs := append(u.Other, u.DropTargets...)
+ imgs = append(imgs, u.Buttons["pass"])
+ reposition.AnimateHandCardPass(ch, imgs, u)
return true
}
@@ -622,7 +585,6 @@
if err := u.CurTable.ValidPlayLogic(c, playerId); err != "" {
return err
}
- u.DropTargets[0].SetCardHere(nil)
success := sync.LogPlay(u, c)
for !success {
success = sync.LogPlay(u, c)
@@ -675,8 +637,8 @@
}
oldY := c.GetInitial().Y
suit := c.GetSuit()
- u.CurCard.Move(d.GetCurrent(), c.GetDimensions(), u.Eng)
- d.SetCardHere(u.CurCard)
+ c.Move(d.GetCurrent(), c.GetDimensions(), u.Eng)
+ d.SetCardHere(c)
// realign suit the card just left
reposition.RealignSuit(suit, oldY, u)
return true
diff --git a/lib/components/board.dart b/lib/components/board.dart
index 41b072c..bb611ac 100644
--- a/lib/components/board.dart
+++ b/lib/components/board.dart
@@ -76,6 +76,8 @@
class HeartsBoardState extends State<HeartsBoard> {
Widget build(BuildContext context) {
+ double offscreenDelta = config.isMini ? 5.0 : 2.0;
+
return new Container(
height: config.height,
width: config.width,
@@ -87,24 +89,24 @@
? _buildMiniBoardLayout()
: _buildBoardLayout()),
new Positioned(
- top: config.height * 5.5,
+ top: config.height * (offscreenDelta + 0.5),
left: (config.width - config.cardWidth) / 2,
- child: _buildTrick(
+ child: _buildOffScreenCards(
config.isMini ? rotateByGamePlayerNumber(0) : 0)), // bottom
new Positioned(
top: (config.height - config.cardHeight) / 2,
- left: config.width * -4.5,
- child: _buildTrick(
+ left: config.width * (-offscreenDelta + 0.5),
+ child: _buildOffScreenCards(
config.isMini ? rotateByGamePlayerNumber(1) : 1)), // left
new Positioned(
- top: config.height * -4.5,
+ top: config.height * (-offscreenDelta + 0.5),
left: (config.width - config.cardWidth) / 2,
- child: _buildTrick(
+ child: _buildOffScreenCards(
config.isMini ? rotateByGamePlayerNumber(2) : 2)), // top
new Positioned(
top: (config.height - config.cardHeight) / 2,
- left: config.width * 5.5,
- child: _buildTrick(
+ left: config.width * (offscreenDelta + 0.5),
+ child: _buildOffScreenCards(
config.isMini ? rotateByGamePlayerNumber(3) : 3)) // right
]));
}
@@ -161,7 +163,6 @@
child: new CardCollectionComponent(
showCard, true, CardCollectionOrientation.show1,
useKeys: true,
- animationType: component_card.CardAnimationType.NONE,
acceptCallback: config.gameAcceptCallback,
acceptType: isMe && isPlayerTurn ? DropType.card : DropType.none,
widthCard: config.cardWidth - 6.0,
@@ -319,7 +320,7 @@
useKeys: true));
}
- Widget _buildTrick(int playerNumber) {
+ Widget _buildOffScreenCards(int playerNumber) {
HeartsGame game = config.game;
List<logic_card.Card> cards =
@@ -327,9 +328,18 @@
// If took trick, exclude the last 4 cards for the trick taking animation.
if (config.trickTaking && playerNumber == game.lastTrickTaker) {
cards = new List.from(cards.sublist(0, cards.length - 4));
+ } else {
+ cards = new List.from(cards);
}
- double sizeFactor = config.isMini ? 1.0 : 2.0;
+ double sizeFactor = 2.0;
+ if (config.isMini) {
+ sizeFactor = 1.0;
+ if (playerNumber != game.playerNumber) {
+ cards.addAll(
+ game.cardCollections[playerNumber + HeartsGame.OFFSET_HAND]);
+ }
+ }
return new CardCollectionComponent(
cards, true, CardCollectionOrientation.show1,
diff --git a/lib/components/card_collection.dart b/lib/components/card_collection.dart
index 1a2f93f..6ede30d 100644
--- a/lib/components/card_collection.dart
+++ b/lib/components/card_collection.dart
@@ -75,10 +75,12 @@
class CardCollectionComponentState extends State<CardCollectionComponent> {
String status = 'bar';
- bool _handleWillAccept(dynamic data) {
- print('will accept?');
- print(data);
- return true;
+ bool _handleWillAccept(component_card.Card data) {
+ return !config.cards.contains(data.card);
+ }
+
+ bool _handleWillAcceptMultiple(CardCollectionComponent data) {
+ return data != config; // don't accept your own self.
}
void _handleAccept(component_card.Card data) {
@@ -344,7 +346,7 @@
});
case DropType.card_collection:
return new DragTarget<CardCollectionComponent>(
- onWillAccept: _handleWillAccept,
+ onWillAccept: _handleWillAcceptMultiple,
onAccept: _handleAcceptMultiple, builder:
(BuildContext context, List<CardCollectionComponent> data, _) {
return new Container(
diff --git a/lib/components/game.dart b/lib/components/game.dart
index c726f92..7a858ad 100644
--- a/lib/components/game.dart
+++ b/lib/components/game.dart
@@ -10,6 +10,7 @@
import 'package:flutter/scheduler.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
+import 'package:vector_math/vector_math_64.dart' as vector_math;
import '../logic/card.dart' as logic_card;
import '../logic/croupier.dart' show Croupier;
diff --git a/lib/components/hearts/hearts.part.dart b/lib/components/hearts/hearts.part.dart
index 7a1386e..d74946b 100644
--- a/lib/components/hearts/hearts.part.dart
+++ b/lib/components/hearts/hearts.part.dart
@@ -141,15 +141,17 @@
.add(HeartsGame.OFFSET_HAND + playerNum);
break;
case HeartsPhase.Play:
- for (int i = 0; i < 4; i++) {
- if (_showSplitView || i == playerNum) {
+ if (_showSplitView) {
+ for (int i = 0; i < 4; i++) {
+ visibleCardCollectionIndexes.add(HeartsGame.OFFSET_HAND + i);
+ visibleCardCollectionIndexes.add(HeartsGame.OFFSET_TRICK + i);
visibleCardCollectionIndexes.add(HeartsGame.OFFSET_PLAY + i);
}
- if (_showSplitView) {
- visibleCardCollectionIndexes
- .add(HeartsGame.OFFSET_HAND + playerNum);
- visibleCardCollectionIndexes.add(HeartsGame.OFFSET_TRICK + i);
- }
+ } else {
+ visibleCardCollectionIndexes
+ .add(HeartsGame.OFFSET_PLAY + playerNum);
+ visibleCardCollectionIndexes
+ .add(HeartsGame.OFFSET_HAND + playerNum);
}
break;
@@ -252,7 +254,7 @@
game.debugString = null;
} catch (e) {
print("You can't do that! ${e.toString()}");
- config.game.debugString = e.toString();
+ config.game.debugString = "You must pass 3 cards";
}
});
}
@@ -396,16 +398,44 @@
String _getStatus() {
HeartsGame game = config.game;
- // Who's turn is it?
- String name = _getName(game.whoseTurn) ?? "Player ${game.whoseTurn}";
- String status =
- game.whoseTurn == game.playerNumber ? "Your turn" : "${name}'s turn";
+ String status;
+ switch (game.phase) {
+ case HeartsPhase.Play:
+ // Who's turn is it?
+ String name = _getName(game.whoseTurn) ?? "Player ${game.whoseTurn}";
+ status = game.whoseTurn == game.playerNumber
+ ? "Your turn"
+ : "${name}'s turn";
- // Override if someone is taking a trick.
- if (this.trickTaking) {
- String trickTaker =
- _getName(game.lastTrickTaker) ?? "Player ${game.lastTrickTaker}";
- status = "${trickTaker}'s trick";
+ // Override if someone is taking a trick.
+ if (this.trickTaking) {
+ String trickTaker =
+ _getName(game.lastTrickTaker) ?? "Player ${game.lastTrickTaker}";
+ status = game.lastTrickTaker == game.playerNumber
+ ? "Your trick"
+ : "${trickTaker}'s trick";
+ }
+ break;
+ case HeartsPhase.Pass:
+ if (game.hasPassed(game.playerNumber)) {
+ status = "Waiting for cards...";
+ } else {
+ String name =
+ _getName(game.passTarget) ?? "Player ${game.passTarget}";
+ status = "Pass to ${name}";
+ }
+ break;
+ case HeartsPhase.Take:
+ if (game.hasTaken(game.playerNumber)) {
+ status = "Waiting for other players...";
+ } else {
+ String name =
+ _getName(game.takeTarget) ?? "Player ${game.takeTarget}";
+ status = "Take from ${name}";
+ }
+ break;
+ default:
+ break;
}
// Override if there is a debug string.
@@ -417,18 +447,53 @@
}
Widget _buildStatusBar() {
+ HeartsGame game = config.game;
+
+ List<Widget> statusWidgets = new List<Widget>();
+ statusWidgets.add(new Text(_getStatus(), style: style.Text.largeStyle));
+
+ switch (game.phase) {
+ case HeartsPhase.Play:
+ statusWidgets
+ .add(new IconButton(icon: "action/swap_vert", onPressed: () {
+ setState(() {
+ _showSplitView = !_showSplitView;
+ });
+ }));
+ break;
+ case HeartsPhase.Pass:
+ case HeartsPhase.Take:
+ // TODO(alexfandrianto): Icons for arrow_upward and arrow_downward were
+ // just added to the material icon list. However, they are not available
+ // through Flutter yet.
+ double rotationAngle = 0.0; // right
+ switch (game.roundNumber % 4) {
+ case 1:
+ rotationAngle = math.PI; // left
+ break;
+ case 2:
+ rotationAngle = -math.PI / 2; // up
+ break;
+ }
+ if (game.phase == HeartsPhase.Take) {
+ rotationAngle = rotationAngle + math.PI; // opposite
+ }
+ statusWidgets.add(new Transform(
+ transform:
+ new vector_math.Matrix4.identity().rotateZ(rotationAngle),
+ alignment: new FractionalOffset(0.5, 0.5),
+ child: new Icon(icon: "navigation/arrow_forward")));
+ break;
+ default:
+ break;
+ }
+
return new Container(
padding: new EdgeDims.all(10.0),
decoration:
new BoxDecoration(backgroundColor: style.theme.primaryColor),
- child: new Row([
- new Text(_getStatus(), style: style.Text.largeStyle),
- new IconButton(icon: "action/swap_vert", onPressed: () {
- setState(() {
- _showSplitView = !_showSplitView;
- });
- })
- ], justifyContent: FlexJustifyContent.spaceBetween));
+ child: new Row(statusWidgets,
+ justifyContent: FlexJustifyContent.spaceBetween));
}
Widget _buildFullMiniBoard() {
@@ -467,7 +532,6 @@
true,
CardCollectionOrientation.show1,
useKeys: true,
- animationType: component_card.CardAnimationType.NONE,
acceptCallback: _makeGameMoveCallback,
acceptType:
p == game.whoseTurn ? DropType.card : DropType.none,
@@ -480,7 +544,7 @@
cardCollections.add(new Container(
decoration:
new BoxDecoration(backgroundColor: style.theme.primaryColor),
- child: new Column([_buildStatusBar(), playArea])));
+ child: new BlockBody([_buildStatusBar(), playArea])));
}
List<logic_card.Card> cards = game.cardCollections[p];
@@ -489,10 +553,8 @@
dragChildren: true, // Can drag, but may not have anywhere to drop
comparator: _compareCards,
width: config.width,
- useKeys: _showSplitView);
- cardCollections.add(c); // flex
-
- cardCollections.add(_makeDebugButtons());
+ useKeys: true);
+ cardCollections.add(new BlockBody([c, _makeDebugButtons()]));
return new Column(cardCollections,
justifyContent: FlexJustifyContent.spaceBetween);
@@ -602,32 +664,39 @@
Color bgColor = completed ? Colors.teal[600] : Colors.teal[500];
+ Widget statusBar = _buildStatusBar();
+
Widget topArea = new Container(
decoration: new BoxDecoration(backgroundColor: bgColor),
padding: new EdgeDims.all(10.0),
width: config.width,
child: new Flex(topCardWidgets,
justifyContent: FlexJustifyContent.spaceBetween));
+ Widget combinedTopArea = new BlockBody([statusBar, topArea]);
Widget handArea = new CardCollectionComponent(
hand, true, CardCollectionOrientation.suit,
dragChildren: draggable,
comparator: _compareCards,
width: config.width,
+ acceptCallback: cb,
+ acceptType: cb != null ? DropType.card : null,
backgroundColor: Colors.grey[500],
altColor: Colors.grey[700],
useKeys: true);
- return new Column(<Widget>[topArea, handArea, _makeDebugButtons()],
+ Widget combinedBottomArea = new BlockBody([handArea, _makeDebugButtons()]);
+
+ return new Column(<Widget>[combinedTopArea, combinedBottomArea],
justifyContent: FlexJustifyContent.spaceBetween);
}
Widget _topCardWidget(List<logic_card.Card> cards, AcceptCb cb) {
Widget ccc = new CardCollectionComponent(
cards, true, CardCollectionOrientation.show1,
+ dragChildren: cb != null,
acceptCallback: cb,
acceptType: cb != null ? DropType.card : null,
- animationType: component_card.CardAnimationType.NONE,
backgroundColor: Colors.white,
altColor: Colors.grey[200],
useKeys: true);
diff --git a/lib/logic/hearts/hearts_game.part.dart b/lib/logic/hearts/hearts_game.part.dart
index a635060..8a25172 100644
--- a/lib/logic/hearts/hearts_game.part.dart
+++ b/lib/logic/hearts/hearts_game.part.dart
@@ -207,10 +207,14 @@
cardCollections[PLAYER_C].length == 13 &&
cardCollections[PLAYER_D].length == 13;
+ bool hasPassed(int player) =>
+ cardCollections[player + OFFSET_PASS].length == 3;
bool get allPassed => cardCollections[PLAYER_A_PASS].length == 3 &&
cardCollections[PLAYER_B_PASS].length == 3 &&
cardCollections[PLAYER_C_PASS].length == 3 &&
cardCollections[PLAYER_D_PASS].length == 3;
+ bool hasTaken(int player) =>
+ cardCollections[getTakeTarget(player) + OFFSET_PASS].length == 0;
bool get allTaken => cardCollections[PLAYER_A_PASS].length == 0 &&
cardCollections[PLAYER_B_PASS].length == 0 &&
cardCollections[PLAYER_C_PASS].length == 0 &&
diff --git a/manifest.yaml b/manifest.yaml
index f9e8c92..6fbe3fb 100644
--- a/manifest.yaml
+++ b/manifest.yaml
@@ -6,6 +6,7 @@
- name: av/play_arrow
- name: action/swap_vert
- name: navigation/arrow_back
+ - name: navigation/arrow_forward
- name: navigation/menu
assets:
- images/default/classic/down/c10.png