netris/cmd/netris/gui_title.go

444 lines
8.6 KiB
Go

package main
import (
"fmt"
"math/rand"
"strconv"
"time"
"git.sr.ht/~tslocum/netris/pkg/game"
"git.sr.ht/~tslocum/netris/pkg/mino"
"github.com/tslocum/tview"
)
const (
SubTitle = " .rocketnine.space v"
)
var (
titleVisible bool
titleScreen int
titleSelectedButton int
gameSettingsSelectedButton int
drawTitle = make(chan struct{}, game.CommandQueueSize)
titleGrid *tview.Grid
titleContainerGrid *tview.Grid
gameListSelected int
newGameGrid *tview.Grid
newGameNameInput *tview.InputField
newGameMaxPlayersInput *tview.InputField
newGameSpeedLimitInput *tview.InputField
playerSettingsForm *tview.Form
playerSettingsGrid *tview.Grid
playerSettingsContainerGrid *tview.Grid
gameList []*game.ListedGame
gameListHeader *tview.TextView
gameListView *tview.TextView
gameListGrid *tview.Grid
gameListContainerGrid *tview.Grid
newGameContainerGrid *tview.Grid
gameSettingsGrid *tview.Grid
gameSettingsContainerGrid *tview.Grid
gameGrid *tview.Grid
titleName *tview.TextView
titleL *tview.TextView
titleR *tview.TextView
titleMatrixL = newTitleMatrixSide()
titleMatrix = newTitleMatrixName()
titleMatrixR = newTitleMatrixSide()
titlePiecesL []*mino.Piece
titlePiecesR []*mino.Piece
buttonA *tview.Button
buttonB *tview.Button
buttonC *tview.Button
buttonLabelA *tview.TextView
buttonLabelB *tview.TextView
buttonLabelC *tview.TextView
)
func previousTitleButton() {
if titleSelectedButton == 0 {
return
}
titleSelectedButton--
}
func nextTitleButton() {
maxButton := 2
if titleScreen == 4 {
maxButton = 3
} else if titleScreen == 5 {
maxButton = 4
}
if titleSelectedButton >= maxButton {
return
}
titleSelectedButton++
}
func updateGameSettings() {
switch gameSettingsSelectedButton {
case 0:
app.SetFocus(buttonKeybindRotateCCW)
case 1:
app.SetFocus(buttonKeybindRotateCW)
case 2:
app.SetFocus(buttonKeybindMoveLeft)
case 3:
app.SetFocus(buttonKeybindMoveRight)
case 4:
app.SetFocus(buttonKeybindSoftDrop)
case 5:
app.SetFocus(buttonKeybindHardDrop)
case 6:
app.SetFocus(buttonKeybindCancel)
case 7:
app.SetFocus(buttonKeybindSave)
}
}
func setTitleVisible(visible bool) {
if titleVisible == visible {
return
}
titleVisible = visible
if !titleVisible {
app.SetRoot(gameGrid, true)
app.SetFocus(nil)
} else {
titleScreen = 0
titleSelectedButton = 0
drawTitle <- struct{}{}
app.SetRoot(titleContainerGrid, true)
updateTitle()
}
}
func updateTitle() {
if titleScreen == 1 {
buttonA.SetLabel("Player Settings")
buttonLabelA.SetText("\nChange name")
buttonB.SetLabel("Game Settings")
buttonLabelB.SetText("\nChange keybindings")
buttonC.SetLabel("Return")
buttonLabelC.SetText("\nReturn to the last screen")
} else if titleScreen == 4 {
buttonA.SetLabel("New Game")
buttonB.SetLabel("Join by IP")
buttonC.SetLabel("Return")
} else {
if joinedGame {
buttonA.SetLabel("Resume")
buttonLabelA.SetText("\nResume game in progress")
buttonB.SetLabel("Settings")
buttonLabelB.SetText("\nChange player name, keybindings, etc")
buttonC.SetLabel("Quit")
buttonLabelC.SetText("\nQuit game")
} else {
buttonA.SetLabel("Play")
buttonLabelA.SetText("\nPlay with others online")
buttonB.SetLabel("Practice")
buttonLabelB.SetText("\nPlay by yourself")
buttonC.SetLabel("Settings")
buttonLabelC.SetText("\nPlayer name, keybindings, etc.")
}
}
if titleScreen == 4 {
switch titleSelectedButton {
case 2:
app.SetFocus(buttonB)
case 3:
app.SetFocus(buttonC)
case 1:
app.SetFocus(buttonA)
default:
app.SetFocus(nil)
}
return
} else if titleScreen == 5 {
switch titleSelectedButton {
case 1:
app.SetFocus(newGameMaxPlayersInput)
case 2:
app.SetFocus(newGameSpeedLimitInput)
case 3:
app.SetFocus(buttonCancel)
case 4:
app.SetFocus(buttonStart)
default:
app.SetFocus(newGameNameInput)
}
return
} else if titleScreen > 1 {
return
}
switch titleSelectedButton {
case 1:
app.SetFocus(buttonB)
case 2:
app.SetFocus(buttonC)
default:
app.SetFocus(buttonA)
}
}
func handleTitle() {
var t *time.Ticker
for {
if t == nil {
t = time.NewTicker(850 * time.Millisecond)
} else {
select {
case <-t.C:
case <-drawTitle:
if t != nil {
t.Stop()
}
t = time.NewTicker(850 * time.Millisecond)
}
}
if !titleVisible {
continue
}
titleMatrixL.ClearOverlay()
for _, p := range titlePiecesL {
p.Y -= 1
if p.Y < -3 {
p.Y = titleMatrixL.H + 2
}
if rand.Intn(4) == 0 {
p.Mino = p.Rotate(1, 0)
p.ApplyRotation(1, 0)
}
for _, m := range p.Mino {
titleMatrixL.SetBlock(p.X+m.X, p.Y+m.Y, p.Solid, true)
}
}
titleMatrixR.ClearOverlay()
for _, p := range titlePiecesR {
p.Y -= 1
if p.Y < -3 {
p.Y = titleMatrixL.H + 2
}
if rand.Intn(4) == 0 {
p.Mino = p.Rotate(1, 0)
p.ApplyRotation(1, 0)
}
for _, m := range p.Mino {
if titleMatrixR.Block(p.X+m.X, p.Y+m.Y) != mino.BlockNone {
continue
}
titleMatrixR.SetBlock(p.X+m.X, p.Y+m.Y, p.Solid, true)
}
}
app.QueueUpdateDraw(renderTitle)
}
}
func renderTitle() {
var newBlock mino.Block
for i, b := range titleMatrix.M {
switch b {
case mino.BlockSolidRed:
newBlock = mino.BlockSolidMagenta
case mino.BlockSolidYellow:
newBlock = mino.BlockSolidRed
case mino.BlockSolidGreen:
newBlock = mino.BlockSolidYellow
case mino.BlockSolidCyan:
newBlock = mino.BlockSolidGreen
case mino.BlockSolidBlue:
newBlock = mino.BlockSolidCyan
case mino.BlockSolidMagenta:
newBlock = mino.BlockSolidBlue
default:
continue
}
titleMatrix.M[i] = newBlock
}
renderLock.Lock()
renderMatrix(titleMatrix)
titleName.Clear()
titleName.Write(renderBuffer.Bytes())
renderMatrix(titleMatrixL)
titleL.Clear()
titleL.Write(renderBuffer.Bytes())
renderMatrix(titleMatrixR)
titleR.Clear()
titleR.Write(renderBuffer.Bytes())
renderLock.Unlock()
}
func renderGameList() {
w := 36
gameListView.Clear()
gameListView.Write(renderULCorner)
for i := 0; i < w; i++ {
gameListView.Write(renderHLine)
}
gameListView.Write(renderURCorner)
gameListView.Write([]byte("\n"))
gameListView.Write(renderVLine)
gameListView.Write([]byte(fmt.Sprintf("%-29s%s", "Game", "Players")))
gameListView.Write(renderVLine)
gameListView.Write([]byte("\n"))
gameListView.Write(renderLTee)
for i := 0; i < w; i++ {
gameListView.Write(renderHLine)
}
gameListView.Write(renderRTee)
gameListView.Write([]byte("\n"))
h := 8
for i, g := range gameList {
p := strconv.Itoa(g.Players)
if g.MaxPlayers > 0 {
p += "/" + strconv.Itoa(g.MaxPlayers)
}
gameListView.Write(renderVLine)
if titleSelectedButton == 0 && gameListSelected == i {
gameListView.Write([]byte("[#000000:#FFFFFF]"))
}
gameListView.Write([]byte(fmt.Sprintf("%-29s%7s", g.Name, p)))
if titleSelectedButton == 0 && gameListSelected == i {
gameListView.Write([]byte("[-:-]"))
}
gameListView.Write(renderVLine)
gameListView.Write([]byte("\n"))
h--
}
if h > 0 {
for i := 0; i < h; i++ {
gameListView.Write(renderVLine)
for i := 0; i < w; i++ {
gameListView.Write([]byte(" "))
}
gameListView.Write(renderVLine)
}
}
gameListView.Write(renderLLCorner)
for i := 0; i < w; i++ {
gameListView.Write(renderHLine)
}
gameListView.Write(renderLRCorner)
}
func refreshGameList() {
app.QueueUpdateDraw(func() {
gameListHeader.SetText("Finding games...")
})
go func() {
ok := fetchGameList()
app.QueueUpdateDraw(func() {
if !ok {
gameListHeader.SetText("Failed to connect to game server")
return
}
var plural string
if len(gameList) != 1 {
plural = "s"
}
gameListHeader.SetText(fmt.Sprintf("Found %d game%s", len(gameList), plural))
})
}()
}
func fetchGameList() bool {
s, err := game.Connect(connectAddress)
if err != nil {
return false
}
s.Write(&game.GameCommandListGames{})
t := time.NewTimer(10 * time.Second)
for {
select {
case <-t.C:
return false
case e := <-s.In:
if e.Command() == game.CommandListGames {
if p, ok := e.(*game.GameCommandListGames); ok {
gameList = p.Games
if gameListSelected >= len(gameList) {
gameListSelected = len(gameList) - 1
}
app.QueueUpdateDraw(renderGameList)
s.Close()
if !t.Stop() {
<-t.C
}
return true
}
}
}
}
}
func resetNewGameInputs() {
newGameNameInput.SetText("netris")
newGameMaxPlayersInput.SetText("0")
newGameSpeedLimitInput.SetText("0")
}