blob: af4abe90298e759ff8556ebfb706c6991b78aace [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.
package main
import (
"sort"
"time"
"hearts/direction"
"hearts/img/reposition"
"hearts/img/resize"
"hearts/img/staticimg"
"hearts/img/texture"
"hearts/logic/card"
"hearts/logic/table"
"golang.org/x/mobile/app"
"golang.org/x/mobile/event/paint"
"golang.org/x/mobile/event/size"
"golang.org/x/mobile/event/touch"
"golang.org/x/mobile/exp/f32"
"golang.org/x/mobile/exp/sprite"
"golang.org/x/mobile/exp/sprite/clock"
"golang.org/x/mobile/exp/sprite/glsprite"
"golang.org/x/mobile/gl"
)
const (
numPlayers = 4
cardSize = 35
cardWidth = float32(cardSize)
cardHeight = float32(cardSize)
topPadding = float32(7)
bottomPadding = float32(5)
)
var (
startTime = time.Now()
eng = glsprite.Engine()
scene *sprite.Node
cards []*card.Card
backgroundImgs []*staticimg.StaticImg
emptySuitImgs []*staticimg.StaticImg
dropTargets []*staticimg.StaticImg
buttons []*staticimg.StaticImg
curCard *card.Card
// lastMouseXY is in Px: divide by pixelsPerPt to get Pt
lastMouseXY = []float32{-1, -1}
// windowSize is in Pt
windowSize = []float32{-1, -1}
pixelsPerPt float32
padding = float32(5)
dir direction.Direction
)
func main() {
app.Main(func(a app.App) {
var sz size.Event
dir = direction.Right
for e := range a.Events() {
switch e := app.Filter(e).(type) {
case size.Event:
// rearrange images on screen based on new size
sz = e
oldWidth := windowSize[0]
oldHeight := windowSize[1]
updateWindowSize(sz)
updateImgPositions(oldWidth, oldHeight)
case touch.Event:
onTouch(e)
case paint.Event:
onPaint(sz)
a.EndPaint(e)
}
}
})
}
func updateWindowSize(sz size.Event) {
windowSize[0] = float32(sz.WidthPt)
windowSize[1] = float32(sz.HeightPt)
pixelsPerPt = float32(sz.WidthPx) / windowSize[0]
}
func updateImgPositions(oldWidth, oldHeight float32) {
if windowExists(oldWidth, oldHeight) {
padding = padding * windowSize[0] / oldWidth
}
resize.AdjustImgs(oldWidth, oldHeight, cards, dropTargets, backgroundImgs, buttons, emptySuitImgs, windowSize, eng)
}
func windowExists(windowWidth, windowHeight float32) bool {
return !(windowWidth < 0 || windowHeight < 0)
}
// returns a card object if a card was clicked, or nil if no card was clicked
func findClickedCard(t touch.Event) *card.Card {
// i goes from the end backwards so that it checks cards displayed on top of other cards first
for i := len(cards) - 1; i >= 0; i-- {
c := cards[i]
if touchingCard(t, c) {
return c
}
}
return nil
}
func touchingCard(t touch.Event, c *card.Card) bool {
withinXBounds := t.X/pixelsPerPt >= c.GetX() && t.X/pixelsPerPt <= c.GetWidth()+c.GetX()
withinYBounds := t.Y/pixelsPerPt >= c.GetY() && t.Y/pixelsPerPt <= c.GetHeight()+c.GetY()
return withinXBounds && withinYBounds
}
func touchingStaticImg(t touch.Event, s *staticimg.StaticImg) bool {
withinXBounds := t.X/pixelsPerPt >= s.GetX() && t.X/pixelsPerPt <= s.GetWidth()+s.GetX()
withinYBounds := t.Y/pixelsPerPt >= s.GetY() && t.Y/pixelsPerPt <= s.GetHeight()+s.GetY()
return withinXBounds && withinYBounds
}
// returns a button object if a button was clicked, or nil if no button was clicked
func findClickedButton(t touch.Event) *staticimg.StaticImg {
for _, b := range buttons {
if touchingStaticImg(t, b) {
return b
}
}
return nil
}
func passCards(t touch.Event) {
for _, d := range dropTargets {
passCard := d.GetCardHere()
if passCard != nil {
reposition.SpinAway(passCard, dir, t)
d.SetCardHere(nil)
}
}
}
func dropCardOnTarget(c *card.Card, t touch.Event) bool {
for _, d := range dropTargets {
// checking to see if card was dropped onto a drop target
if touchingStaticImg(t, d) {
lastDroppedCard := d.GetCardHere()
if lastDroppedCard != nil {
reposition.ResetCardPosition(lastDroppedCard, cards, emptySuitImgs, padding, windowSize, eng)
}
oldY := c.GetInitialY()
suit := c.GetSuit()
newX := d.GetX()
newY := d.GetY()
width := c.GetWidth()
height := c.GetHeight()
curCard.Move(newX, newY, width, height, eng)
d.SetCardHere(curCard)
// realign suit the card just left
reposition.RealignSuit(suit, oldY, cards, emptySuitImgs, padding, windowSize, eng)
return true
}
}
return false
}
func removeCardFromTarget(c *card.Card) bool {
for _, d := range dropTargets {
if d.GetCardHere() == c {
d.SetCardHere(nil)
return true
}
}
return false
}
func unpressButtons() {
for _, b := range buttons {
eng.SetSubTex(b.GetNode(), b.GetImage())
}
}
func pressButton(b *staticimg.StaticImg) {
eng.SetSubTex(b.GetNode(), b.GetAlt())
}
func onTouch(t touch.Event) {
switch t.Type.String() {
case "begin":
curCard = findClickedCard(t)
b := findClickedButton(t)
if b != nil {
pressButton(b)
// specific to pass screen scenario: if any button is clicked, all cards on drop targets get passed
passCards(t)
}
case "move":
// only do anything if the user has clicked on a card: then, drag it
if curCard != nil {
reposition.DragCard(curCard, pixelsPerPt, lastMouseXY, eng, t)
}
case "end":
if curCard != nil {
if !dropCardOnTarget(curCard, t) {
// check to see if card was removed from a drop target
removeCardFromTarget(curCard)
// add card back to hand
reposition.ResetCardPosition(curCard, cards, emptySuitImgs, padding, windowSize, eng)
}
}
// reset all buttons to 'unpressed' image, in case any had been clicked
unpressButtons()
curCard = nil
}
lastMouseXY[0] = t.X
lastMouseXY[1] = t.Y
}
func onPaint(sz size.Event) {
if scene == nil {
loadPassScreen()
}
gl.ClearColor(1, 1, 1, 1)
gl.Clear(gl.COLOR_BUFFER_BIT)
now := clock.Time(time.Since(startTime) * 60 / time.Second)
eng.Render(scene, now, sz)
}
func loadPassScreen() {
numSuits := 4
numDropTargets := 3
scene = &sprite.Node{}
eng.Register(scene)
eng.SetTransform(scene, f32.Affine{
{1, 0, 0},
{0, 1, 0},
})
t := table.InitializeGame(numPlayers)
t.Deal()
cards = t.GetPlayers()[0].GetHand()
dropTargets = make([]*staticimg.StaticImg, 0)
backgroundImgs = make([]*staticimg.StaticImg, 0)
emptySuitImgs = make([]*staticimg.StaticImg, 0)
buttons = make([]*staticimg.StaticImg, 0)
sort.Sort(card.CardSorter(cards))
clubCount := 0
diamondCount := 0
spadeCount := 0
heartCount := 0
for i := 0; i < len(cards); i++ {
switch cards[i].GetSuit() {
case card.Club:
clubCount++
case card.Diamond:
diamondCount++
case card.Spade:
spadeCount++
case card.Heart:
heartCount++
}
}
suitCounts := []int{clubCount, diamondCount, spadeCount, heartCount}
texs := texture.LoadTextures(eng)
// adding blue banner for croupier header
headerImage := texs["blue.png"]
headerX := float32(0)
headerY := float32(0)
headerWidth := windowSize[0]
var headerHeight float32
if 2*cardHeight < headerWidth/4 {
headerHeight = 2 * cardHeight
} else {
headerHeight = headerWidth / 4
}
headerPos := card.MakePosition(headerX, headerY, headerX, headerY, headerWidth, headerHeight)
header := texture.MakeImgWithoutAlt(headerImage, headerPos, eng, scene)
backgroundImgs = append(backgroundImgs, header)
// adding croupier name on top of banner
headerTextImage := texs["croupierName.png"]
var headerTextWidth float32
var headerTextHeight float32
if headerHeight-topPadding > headerWidth/6 {
headerTextWidth = headerWidth / 2
headerTextHeight = headerTextWidth / 3
} else {
headerTextHeight = 2 * headerHeight / 3
headerTextWidth = headerTextHeight * 3
}
headerTextX := headerX + (headerWidth-headerTextWidth)/2
headerTextY := headerY + (headerHeight-headerTextHeight+topPadding)/2
headerTextPos := card.MakePosition(headerTextX, headerTextY, headerTextX, headerTextY, headerTextWidth, headerTextHeight)
headerText := texture.MakeImgWithoutAlt(headerTextImage, headerTextPos, eng, scene)
backgroundImgs = append(backgroundImgs, headerText)
// adding blue background banner for drop targets
topOfHand := windowSize[1] - 5*(cardHeight+padding) - (2 * padding / 5) - bottomPadding
passBannerImage := texs["blue.png"]
passBannerX := float32(0)
passBannerY := topOfHand - (2 * padding)
passBannerWidth := windowSize[0]
passBannerHeight := cardHeight + (4 * padding / 5)
passBannerPos := card.MakePosition(passBannerX, passBannerY, passBannerX, passBannerY, passBannerWidth, passBannerHeight)
passBanner := texture.MakeImgWithoutAlt(passBannerImage, passBannerPos, eng, scene)
backgroundImgs = append(backgroundImgs, passBanner)
// adding drop targets
dropTargetImage := texs["white.png"]
dropTargetWidth := cardWidth
dropTargetHeight := cardHeight
dropTargetY := passBannerY + (2 * padding / 5)
for i := 0; i < numDropTargets; i++ {
dropTargetX := windowSize[0]/2 - (dropTargetWidth+float32(numDropTargets)*(padding+dropTargetWidth))/2 + float32(i)*(padding+dropTargetWidth)
dropTargetPos := card.MakePosition(dropTargetX, dropTargetY, dropTargetX, dropTargetY, dropTargetWidth, dropTargetHeight)
newTarget := texture.MakeImgWithoutAlt(dropTargetImage, dropTargetPos, eng, scene)
dropTargets = append(dropTargets, newTarget)
}
// adding pass button
pressedImg := texs["passPressed.png"]
unpressedImg := texs["passUnpressed.png"]
buttonWidth := cardWidth
buttonHeight := cardHeight / 2
buttonX := windowSize[0]/2 + (float32(numDropTargets)*(padding+buttonWidth)-buttonWidth)/2
buttonY := passBannerY + (2 * padding / 5)
buttonPos := card.MakePosition(buttonX, buttonY, buttonX, buttonY, buttonWidth, buttonHeight)
button := texture.MakeImgWithAlt(unpressedImg, pressedImg, buttonPos, true, eng, scene)
buttons = append(buttons, button)
// adding arrow below pass button
var arrow *staticimg.StaticImg
if dir == direction.Right {
arrowImage := texs["rightArrow.png"]
arrowWidth := cardWidth
arrowHeight := cardHeight / 2
arrowX := windowSize[0]/2 + (float32(numDropTargets)*(padding+buttonWidth)-arrowWidth)/2
arrowY := buttonY + cardHeight/2
arrowPos := card.MakePosition(arrowX, arrowY, arrowX, arrowY, arrowWidth, arrowHeight)
arrow = texture.MakeImgWithoutAlt(arrowImage, arrowPos, eng, scene)
} else if dir == direction.Left {
arrowImage := texs["leftArrow.png"]
arrowWidth := cardWidth
arrowHeight := cardHeight / 2
arrowX := windowSize[0]/2 + (float32(numDropTargets)*(padding+buttonWidth)-arrowWidth)/2
arrowY := buttonY + cardHeight/2
arrowPos := card.MakePosition(arrowX, arrowY, arrowX, arrowY, arrowWidth, arrowHeight)
arrow = texture.MakeImgWithoutAlt(arrowImage, arrowPos, eng, scene)
} else if dir == direction.Across {
arrowImage := texs["acrossArrow.png"]
arrowWidth := cardWidth / 4
arrowHeight := cardHeight / 2
arrowX := windowSize[0]/2 + (float32(numDropTargets)*(padding+buttonWidth)-arrowWidth)/2
arrowY := buttonY + cardHeight/2
arrowPos := card.MakePosition(arrowX, arrowY, arrowX, arrowY, arrowWidth, arrowHeight)
arrow = texture.MakeImgWithoutAlt(arrowImage, arrowPos, eng, scene)
}
backgroundImgs = append(backgroundImgs, arrow)
// adding gray background banners for each suit
suitBannerImage := texs["gray.jpeg"]
suitBannerX := float32(0)
suitBannerWidth := windowSize[0]
suitBannerHeight := cardHeight + (4 * padding / 5)
for i := 0; i < numSuits; i++ {
suitBannerY := windowSize[1] - float32(i+1)*(cardHeight+padding) - (2 * padding / 5) - bottomPadding
suitBannerPos := card.MakePosition(suitBannerX, suitBannerY, suitBannerX, suitBannerY, suitBannerWidth, suitBannerHeight)
suitBanner := texture.MakeImgWithoutAlt(suitBannerImage, suitBannerPos, eng, scene)
backgroundImgs = append(backgroundImgs, suitBanner)
}
// adding suit image to any empty suit in hand
for i, c := range suitCounts {
var texKey string
switch i {
case 0:
texKey = "Club.png"
case 1:
texKey = "Diamond.png"
case 2:
texKey = "Spade.png"
case 3:
texKey = "Heart.png"
}
suitIconImage := texs[texKey]
suitIconAlt := texs["gray.png"]
suitIconX := windowSize[0]/2 - cardWidth/3
suitIconY := windowSize[1] - float32(4-i)*(cardHeight+padding) + cardHeight/6 - bottomPadding
suitIconWidth := 2 * cardWidth / 3
suitIconHeight := 2 * cardHeight / 3
display := c == 0
suitIconPos := card.MakePosition(suitIconX, suitIconY, suitIconX, suitIconY, suitIconWidth, suitIconHeight)
suitIcon := texture.MakeImgWithAlt(suitIconImage, suitIconAlt, suitIconPos, display, eng, scene)
emptySuitImgs = append(emptySuitImgs, suitIcon)
}
// adding clubs
for i := 0; i < clubCount; i++ {
numInSuit := i
texture.PopulateCardImage(cards[i],
texs,
numInSuit,
clubCount,
diamondCount,
spadeCount,
heartCount,
cardWidth,
cardHeight,
padding,
bottomPadding,
windowSize,
eng,
scene)
}
// adding diamonds
for i := clubCount; i < clubCount+diamondCount; i++ {
numInSuit := i - clubCount
texture.PopulateCardImage(cards[i],
texs,
numInSuit,
clubCount,
diamondCount,
spadeCount,
heartCount,
cardWidth,
cardHeight,
padding,
bottomPadding,
windowSize,
eng,
scene)
}
// adding spades
for i := clubCount + diamondCount; i < clubCount+diamondCount+spadeCount; i++ {
numInSuit := i - clubCount - diamondCount
texture.PopulateCardImage(cards[i],
texs,
numInSuit,
clubCount,
diamondCount,
spadeCount,
heartCount,
cardWidth,
cardHeight,
padding,
bottomPadding,
windowSize,
eng,
scene)
}
// adding hearts
for i := clubCount + diamondCount + spadeCount; i < clubCount+diamondCount+spadeCount+heartCount; i++ {
numInSuit := i - clubCount - diamondCount - spadeCount
texture.PopulateCardImage(cards[i],
texs,
numInSuit,
clubCount,
diamondCount,
spadeCount,
heartCount,
cardWidth,
cardHeight,
padding,
bottomPadding,
windowSize,
eng,
scene)
}
}