blob: 65918e73fe5a3cc12965713ffdb2351abd930596 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// touchhandler handles all touch events for the app
package touchhandler
import (
"fmt"
"strconv"
"strings"
"golang.org/x/mobile/event/touch"
"golang.org/x/mobile/exp/sprite"
"hearts/img/coords"
"hearts/img/reposition"
"hearts/img/staticimg"
"hearts/img/uistate"
"hearts/img/view"
"hearts/logic/card"
"hearts/sync"
)
func OnTouch(t touch.Event, u *uistate.UIState) {
if t.Type == touch.TypeBegin {
u.ViewOnTouch = u.CurView
} else if u.CurView != u.ViewOnTouch {
return
}
switch u.CurView {
case uistate.Discovery:
switch t.Type {
case touch.TypeBegin:
beginClickDiscovery(t, u)
}
case uistate.Arrange:
switch t.Type {
case touch.TypeBegin:
beginClickArrange(t, u)
}
case uistate.Table:
switch t.Type {
case touch.TypeBegin:
if u.Debug {
beginClickTable(t, u)
}
}
case uistate.Pass:
switch t.Type {
case touch.TypeBegin:
beginClickPass(t, u)
case touch.TypeMove:
moveClickPass(t, u)
case touch.TypeEnd:
endClickPass(t, u)
}
case uistate.Take:
switch t.Type {
case touch.TypeBegin:
beginClickTake(t, u)
case touch.TypeMove:
if u.CurCard != nil {
moveClickTake(t, u)
}
case touch.TypeEnd:
if u.CurCard != nil {
endClickTake(t, u.CurCard, u)
u.CurCard = nil
}
}
case uistate.Play:
switch t.Type {
case touch.TypeBegin:
beginClickPlay(t, u)
case touch.TypeMove:
if u.CurCard != nil {
moveClickPlay(t, u)
}
case touch.TypeEnd:
if u.CurCard != nil {
endClickPlay(t, u.CurCard, u)
}
}
case uistate.Split:
switch t.Type {
case touch.TypeBegin:
beginClickSplit(t, u)
case touch.TypeMove:
if u.CurCard != nil {
moveClickSplit(t, u)
}
case touch.TypeEnd:
if u.CurCard != nil {
endClickSplit(t, u.CurCard, u)
u.CurCard = nil
}
}
case uistate.Score:
switch t.Type {
case touch.TypeBegin:
beginClickScore(t, u)
}
}
u.LastMouseXY.X = t.X
u.LastMouseXY.Y = t.Y
}
func beginClickDiscovery(t touch.Event, u *uistate.UIState) {
buttonList := findClickedButton(t, u)
for _, button := range buttonList {
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
if logName != "" && settingsName != "" {
sync.LogSettingsName(settingsName, u)
u.ScanChan <- true
u.ScanChan = nil
u.SGChan = make(chan bool)
go sync.Advertise(logName, settingsName, gameStartData, u.SGChan, u.Ctx)
view.LoadArrangeView(u)
}
} 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
if sgName != "" {
sync.LogSettingsName(sgName, u)
}
u.ScanChan <- true
u.ScanChan = nil
view.LoadArrangeView(u)
} else {
fmt.Println("Failed to join")
}
}
}
}
}
}
func beginClickArrange(t touch.Event, u *uistate.UIState) {
buttonList := findClickedButton(t, u)
for _, b := range buttonList {
if b == u.Buttons["exit"] {
if u.SGChan != nil {
u.SGChan <- true
u.SGChan = nil
}
u.IsOwner = false
u.DiscGroups = make(map[string]*uistate.DiscStruct)
u.ScanChan = make(chan bool)
go sync.ScanForSG(u.Ctx, u.ScanChan, u)
view.LoadDiscoveryView(u)
} else if b == u.Buttons["start"] {
if b.GetDisplayingImage() {
successStart := sync.LogGameStart(u)
for !successStart {
successStart = sync.LogGameStart(u)
}
newHands := u.CurTable.Deal()
successDeal := sync.LogDeal(u, u.CurPlayerIndex, newHands)
for !successDeal {
successDeal = sync.LogDeal(u, u.CurPlayerIndex, newHands)
}
}
} else {
for key, button := range u.Buttons {
if b == button {
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)
}
}
}
}
}
}
}
func beginClickTable(t touch.Event, u *uistate.UIState) {
buttonList := findClickedButton(t, u)
if len(buttonList) > 0 {
updateViewFromTable(buttonList[0], u)
}
}
func beginClickPass(t touch.Event, u *uistate.UIState) {
u.CurCard = findClickedCard(t, u)
if u.CurCard != nil {
reposition.BringNodeToFront(u.CurCard.GetNode(), u)
}
buttonList := findClickedButton(t, u)
for _, b := range buttonList {
if b == u.Buttons["table"] {
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)
}
}
}
}
}
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 {
reposition.DragCard(t, u)
}
}
func endClickPass(t touch.Event, u *uistate.UIState) {
if u.CurCard != nil {
if !dropCardOnTarget(u.CurCard, t, u) {
// check to see if card was removed from a drop target
removeCardFromTarget(u.CurCard, u)
// add card back to hand
reposition.ResetCardPosition(u.CurCard, u.Eng)
reposition.RealignSuit(u.CurCard.GetSuit(), u.CurCard.GetInitial().Y, u)
}
// check to see whether pull tab should be displayed
readyToPass := true
for _, d := range u.DropTargets {
if d.GetCardHere() == nil {
readyToPass = false
}
}
pullTab := u.Buttons["dragPass"]
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)
}
}
reposition.SwitchOnChan(ch, quit, onDone, u)
}()
}
u.CurCard = nil
u.CurImg = nil
}
func beginClickTake(t touch.Event, u *uistate.UIState) {
u.CurCard = findClickedCard(t, u)
if u.CurCard != nil {
reposition.BringNodeToFront(u.CurCard.GetNode(), u)
u.CurCard.GetNode().Arranger = nil
}
buttonList := findClickedButton(t, u)
for _, b := range buttonList {
if b == u.Buttons["table"] {
view.LoadTableView(u)
} else if b == u.Buttons["hand"] {
view.LoadPassOrTakeOrPlay(u)
}
}
}
func moveClickTake(t touch.Event, u *uistate.UIState) {
reposition.DragCard(t, u)
}
func endClickTake(t touch.Event, c *card.Card, u *uistate.UIState) {
// check to see if card was removed from a drop target
removeCardFromTarget(c, u)
// add card back to hand
reposition.ResetCardPosition(c, u.Eng)
reposition.RealignSuit(c.GetSuit(), c.GetInitial().Y, u)
doneTaking := true
for _, d := range u.DropTargets {
if d.GetCardHere() != nil {
doneTaking = false
}
}
if doneTaking {
ch := make(chan bool)
success := takeCards(ch, u.CurPlayerIndex, u)
quit := make(chan bool)
u.AnimChans = append(u.AnimChans, quit)
go func() {
onDone := func() {
if !success {
fmt.Println("Invalid take")
} else {
view.LoadPlayView(u)
}
}
reposition.SwitchOnChan(ch, quit, onDone, u)
}()
}
}
func beginClickPlay(t touch.Event, u *uistate.UIState) {
u.CurCard = findClickedCard(t, u)
if u.CurCard != nil {
reposition.BringNodeToFront(u.CurCard.GetNode(), u)
}
buttonList := findClickedButton(t, u)
for _, b := range buttonList {
if b == u.Buttons["toggleSplit"] && !u.SwitchingViews {
view.LoadSplitView(false, u)
} else if b == u.Buttons["table"] {
view.LoadTableView(u)
} else if b == u.Buttons["hand"] {
view.LoadPassOrTakeOrPlay(u)
}
}
}
func moveClickPlay(t touch.Event, u *uistate.UIState) {
reposition.DragCard(t, u)
}
func endClickPlay(t touch.Event, c *card.Card, u *uistate.UIState) {
if dropCardOnTarget(c, t, u) {
ch := make(chan bool)
if err := playCard(ch, u.CurPlayerIndex, u); err != "" {
view.ChangePlayMessage(err, u)
removeCardFromTarget(c, u)
// add card back to hand
reposition.ResetCardPosition(c, u.Eng)
reposition.RealignSuit(c.GetSuit(), c.GetInitial().Y, u)
}
quit := make(chan bool)
u.AnimChans = append(u.AnimChans, quit)
go func() {
onDone := func() { view.LoadPlayView(u) }
reposition.SwitchOnChan(ch, quit, onDone, u)
}()
} else {
// check to see if card was removed from a drop target
removeCardFromTarget(c, u)
// add card back to hand
reposition.ResetCardPosition(c, u.Eng)
reposition.RealignSuit(c.GetSuit(), c.GetInitial().Y, u)
}
}
func beginClickSplit(t touch.Event, u *uistate.UIState) {
u.CurCard = findClickedCard(t, u)
if u.CurCard != nil {
reposition.BringNodeToFront(u.CurCard.GetNode(), u)
}
buttonList := findClickedButton(t, u)
for _, b := range buttonList {
if b == u.Buttons["toggleSplit"] && !u.SwitchingViews {
ch := make(chan bool)
u.SwitchingViews = true
reposition.AnimateOutSplit(ch, u)
quit := make(chan bool)
u.AnimChans = append(u.AnimChans, quit)
go func() {
onDone := func() {
u.SwitchingViews = false
view.LoadPlayView(u)
}
reposition.SwitchOnChan(ch, quit, onDone, u)
}()
} else if b == u.Buttons["table"] {
view.LoadTableView(u)
} else if b == u.Buttons["hand"] {
view.LoadPassOrTakeOrPlay(u)
}
}
}
func moveClickSplit(t touch.Event, u *uistate.UIState) {
reposition.DragCard(t, u)
}
func endClickSplit(t touch.Event, c *card.Card, u *uistate.UIState) {
if dropCardHere(c, u.DropTargets[0], t, u) {
ch := make(chan bool)
if err := playCard(ch, u.CurPlayerIndex, u); err != "" {
view.ChangePlayMessage(err, u)
removeCardFromTarget(c, u)
// add card back to hand
reposition.ResetCardPosition(c, u.Eng)
}
} else {
// add card back to hand
reposition.ResetCardPosition(c, u.Eng)
}
}
func beginClickScore(t touch.Event, u *uistate.UIState) {
buttonList := findClickedButton(t, u)
if len(buttonList) > 0 {
success := sync.LogReady(u)
for !success {
sync.LogReady(u)
}
view.LoadWaitingView(u)
}
}
// returns a card object if a card was clicked, or nil if no card was clicked
func findClickedCard(t touch.Event, u *uistate.UIState) *card.Card {
// i goes from the end backwards so that it checks cards displayed on top of other cards first
for i := len(u.Cards) - 1; i >= 0; i-- {
c := u.Cards[i]
if touchingCard(t, c, u) {
return c
}
}
return nil
}
// returns a button object if a button was clicked, or nil if no button was clicked
func findClickedButton(t touch.Event, u *uistate.UIState) []*staticimg.StaticImg {
pressed := make([]*staticimg.StaticImg, 0)
for _, b := range u.Buttons {
if touchingStaticImg(t, b, u) {
pressed = append(pressed, b)
}
}
return pressed
}
// 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
if !u.CurTable.ValidPass(cardsPassed) || u.CurTable.GetPlayers()[playerId].GetDonePassing() {
return false
}
success := sync.LogPass(u, cardsPassed)
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)
return true
}
func takeCards(ch chan bool, playerId int, u *uistate.UIState) bool {
player := u.CurTable.GetPlayers()[playerId]
passedCards := player.GetPassedTo()
if len(passedCards) != 3 {
return false
}
success := sync.LogTake(u)
for !success {
success = sync.LogTake(u)
}
reposition.AnimateHandCardTake(ch, u.Other, u)
return true
}
func playCard(ch chan bool, playerId int, u *uistate.UIState) string {
c := u.DropTargets[0].GetCardHere()
if c == nil {
return "No card has been played"
}
// checks to make sure that:
// -player has not already played a card this round
// -all players have passed cards
// -the play is in the right order
// -the play is valid given game logic
if u.CurTable.GetPlayers()[playerId].GetDonePlaying() {
return "You have already played a card in this trick"
}
if !u.CurTable.AllDonePassing() {
return "Not all players have passed their cards"
}
if !u.CurTable.ValidPlayOrder(playerId) {
return "It is not your turn"
}
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)
}
// no animation when in split view
if u.CurView == uistate.Play {
reposition.AnimateHandCardPlay(ch, c, u)
}
return ""
}
func pressButton(b *staticimg.StaticImg, u *uistate.UIState) {
u.Eng.SetSubTex(b.GetNode(), b.GetAlt())
b.SetDisplayingImage(false)
}
func unpressButtons(u *uistate.UIState) {
for _, b := range u.Buttons {
u.Eng.SetSubTex(b.GetNode(), b.GetImage())
b.SetDisplayingImage(true)
}
}
// checks all drop targets to see if a card was dropped there
func dropCardOnTarget(c *card.Card, t touch.Event, u *uistate.UIState) bool {
for _, d := range u.DropTargets {
// checking to see if card was dropped onto a drop target
if touchingStaticImg(t, d, u) {
lastDroppedCard := d.GetCardHere()
if lastDroppedCard != nil {
reposition.ResetCardPosition(lastDroppedCard, u.Eng)
reposition.RealignSuit(lastDroppedCard.GetSuit(), lastDroppedCard.GetInitial().Y, u)
}
oldY := c.GetInitial().Y
suit := c.GetSuit()
u.CurCard.Move(d.GetCurrent(), c.GetDimensions(), u.Eng)
d.SetCardHere(u.CurCard)
// realign suit the card just left
reposition.RealignSuit(suit, oldY, u)
return true
}
}
return false
}
// checks one specific drop target to see if a card was dropped there
func dropCardHere(c *card.Card, d *staticimg.StaticImg, t touch.Event, u *uistate.UIState) bool {
if !touchingStaticImg(t, d, u) {
return false
}
lastDroppedCard := d.GetCardHere()
if lastDroppedCard != nil {
reposition.ResetCardPosition(lastDroppedCard, u.Eng)
reposition.RealignSuit(lastDroppedCard.GetSuit(), lastDroppedCard.GetInitial().Y, u)
}
oldY := c.GetInitial().Y
suit := c.GetSuit()
u.CurCard.Move(d.GetCurrent(), c.GetDimensions(), u.Eng)
d.SetCardHere(u.CurCard)
// realign suit the card just left
reposition.RealignSuit(suit, oldY, u)
return true
}
func removeCardFromTarget(c *card.Card, u *uistate.UIState) bool {
for _, d := range u.DropTargets {
if d.GetCardHere() == c {
d.SetCardHere(nil)
return true
}
}
return false
}
func touchingCard(t touch.Event, c *card.Card, u *uistate.UIState) bool {
withinXBounds := t.X/u.PixelsPerPt >= c.GetCurrent().X && t.X/u.PixelsPerPt <= c.GetDimensions().X+c.GetCurrent().X
withinYBounds := t.Y/u.PixelsPerPt >= c.GetCurrent().Y && t.Y/u.PixelsPerPt <= c.GetDimensions().Y+c.GetCurrent().Y
return withinXBounds && withinYBounds
}
func touchingStaticImg(t touch.Event, s *staticimg.StaticImg, u *uistate.UIState) bool {
withinXBounds := t.X/u.PixelsPerPt >= s.GetCurrent().X && t.X/u.PixelsPerPt <= s.GetDimensions().X+s.GetCurrent().X
withinYBounds := t.Y/u.PixelsPerPt >= s.GetCurrent().Y && t.Y/u.PixelsPerPt <= s.GetDimensions().Y+s.GetCurrent().Y
return withinXBounds && withinYBounds
}
func updateViewFromTable(b *staticimg.StaticImg, u *uistate.UIState) {
if b == u.Buttons["player0"] {
u.CurPlayerIndex = 0
view.LoadPassOrTakeOrPlay(u)
} else if b == u.Buttons["player1"] {
u.CurPlayerIndex = 1
view.LoadPassOrTakeOrPlay(u)
} else if b == u.Buttons["player2"] {
u.CurPlayerIndex = 2
view.LoadPassOrTakeOrPlay(u)
} else if b == u.Buttons["player3"] {
u.CurPlayerIndex = 3
view.LoadPassOrTakeOrPlay(u)
} else if b == u.Buttons["table"] {
view.LoadTableView(u)
} else if b == u.Buttons["hand"] {
view.LoadPassOrTakeOrPlay(u)
}
}