save and store selection

This commit is contained in:
Settel 2021-08-30 18:21:14 +02:00
parent 0e5084144f
commit d79e3a8956
18 changed files with 180 additions and 51 deletions

View File

@ -2,6 +2,7 @@
<nav v-if="isGamemaster" class="gamecontrols">
<button @click="startGame">Start</button>
<button @click="resetGame">Reset</button>
<button @click="continueGame">Continue</button>
</nav>
</template>
@ -23,6 +24,9 @@ export default {
resetGame() {
this.$engine.resetGame()
},
continueGame() {
this.$engine.continueGame()
},
},
}
</script>

View File

@ -3,8 +3,8 @@
<div class="play__layout">
<div class="play__layout-playground">
<Quote :text="quote" />
<Sources :sources="sources" />
<ConfirmButton />
<Sources :sources="sources" :selectable="selectable" />
<ConfirmButton v-if="selectable"/>
</div>
<div class="play__layout-right-column">
<PlayerList :players="players" />
@ -28,6 +28,14 @@ export default {
players() {
return this.$store.state.players.players
},
selectable() {
const userId = this.$store.state.engine.user.id
const selection = this.$store.state.round.selections[userId]
if (this.$store.state.game.phase != 'select-quote') return false
if (typeof selection === 'undefined') return true
return !selection
},
},
}
</script>

View File

@ -29,7 +29,6 @@ export default {
&__player {
display: flex;
// height: 1.4em;
color: #ffffff;
font-family: Dosis;
font-size: 18px;

View File

@ -1,7 +1,7 @@
<template>
<div class="source">
<div
:class="['source__container', { 'source__container__selected': selected } ]"
:class="getCssClass"
@click="clicked"
>
<div class="source__source">
@ -13,10 +13,14 @@
<script>
export default {
props: ['source'],
props: ['source', 'selectable'],
computed: {
selected() {
return this.isSelected()
getCssClass() {
return {
'source__container': true,
'source__container-selectable': this.selectable,
'source__container__selected': this.isSelected(),
}
},
},
methods: {
@ -24,6 +28,8 @@ export default {
return this.$store.state.selection.selection[this.source.id]
},
clicked() {
if (!this.selectable) return
if (this.isSelected()) {
this.$store.commit('selection/unselect', this.source.id)
} else {
@ -53,19 +59,24 @@ export default {
background-color: #402080;
color: #ffffff;
&-selectable {
&:hover {
background-color: #8060c0;
cursor: pointer;
}
&__selected,
&__selected:hover {
&.source__container__selected {
background-color: #ffffff;
color: #402080;
box-shadow: 0 0 10px #ffffff;
}
}
&__selected {
background-color: #d0d0d0;
color: #402080;
box-shadow: 0 0 10px #d0d0d0;
}
}
&__source {
width: 100%;
font-family: "Wendy One";

View File

@ -6,6 +6,7 @@
v-for="(source, idx) in sources"
:class="['sources__source-' + idx]"
:source="source"
:selectable="selectable"
:key="source.id"
/>
</div>
@ -16,7 +17,7 @@
<script>
export default {
props: ['sources'],
props: ['sources', 'selectable'],
mounted() {
this.$store.commit('selection/clearSelection')
},

View File

@ -0,0 +1,7 @@
export default async function() {
const { store } = this.context
await this.callApi('/api/continueGame', {
g: store.state.engine.user?.game,
})
}

View File

@ -5,6 +5,7 @@ import fetchUpdate from './fetchUpdate'
import fetchUserInfo from './fetchUserInfo'
import startGame from './startGame'
import resetGame from './resetGame'
import continueGame from './continueGame'
import parseSyncData from './parseSyncData'
import saveSelection from './saveSelection'
@ -22,6 +23,7 @@ export default (context, inject) => {
fetchUserInfo,
startGame,
resetGame,
continueGame,
parseSyncData,
saveSelection,
}

View File

@ -1,8 +1,12 @@
export default async function(selection) {
const { store } = this.context
if (selection.length === 0) {
selection = ['-']
}
await this.callApi('/api/saveSelection', {
g: store.state.engine.user?.game,
selection: selection.join(','),
selection: selection[0],
})
}

View File

@ -1,6 +1,7 @@
export const state = () => ({
quote: "",
sources: [],
selections: {},
})
export const mutations = {
@ -8,9 +9,11 @@ export const mutations = {
if (game && game.round) {
state.quote = game.round.quote
state.sources = game.round.sources
state.selections = game.round.selections
} else {
state.quote = ""
state.sources = []
state.selections = {}
}
},
}

View File

@ -0,0 +1,25 @@
package application
import (
"fmt"
"net/http"
"sirlab.de/go/knyt/user"
)
func (app *Application) ContinueGame(usr *user.User, w http.ResponseWriter, r *http.Request) {
gameRef := r.URL.Query().Get("g")
gm, err := app.GetGameById(gameRef)
if err != nil {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "game not found")
return
}
if usr.GetGameId() != gameRef || !usr.IsGamemaster() {
w.WriteHeader(http.StatusForbidden)
fmt.Fprintf(w, "forbidden")
return
}
gm.ContinueGame()
}

View File

@ -0,0 +1,53 @@
package game
import (
"fmt"
)
func (gm *Game) ContinueGame() {
state, phase := gm.GetState()
if state != STATE_PLAY {
fmt.Printf("invalid state, can't continue game in %s state\n", state)
return
}
switch phase {
case PHASE_SELECT_QUOTE:
gm.proceedToReveal()
default:
fmt.Printf("invalid state, can't continue game in %s state, %s phase\n", state, phase)
return
}
}
func (gm *Game) proceedToReveal() {
if !gm.allPlayersHaveSelectedAQuote() {
fmt.Println("not all players have selected a quote yet")
return
}
err := gm.changeGamePhase(STATE_PLAY, PHASE_SELECT_QUOTE, PHASE_REVEAL_START)
if err != nil {
fmt.Println(err)
return
}
gm.notifyClients()
}
func (gm *Game) allPlayersHaveSelectedAQuote() bool {
gm.mu.Lock()
defer gm.mu.Unlock()
for id, playerInfo := range gm.players {
if !playerInfo.isPlaying || playerInfo.isIdle {
continue
}
if len(gm.round.selections[id]) == 0 {
return false
}
}
return true
}

View File

@ -40,11 +40,11 @@ func (gm *Game) GetId() string {
return gm.id
}
func (gm *Game) GetState() string {
func (gm *Game) GetState() (string, string) {
gm.mu.Lock()
defer gm.mu.Unlock()
return gm.state
return gm.state, gm.phase
}
func (gm *Game) GetName() string {

View File

@ -61,31 +61,28 @@ func (gm *Game) getRoundInfo() *syncdata.RoundInfo {
})
}
playerState := make([]syncdata.PlayerState, 0)
roundInfo := syncdata.RoundInfo{
Quote: quote.GetQuote(),
Sources: sources,
Selections: gm.getSelections(),
}
return &roundInfo
}
func (gm *Game) getSelections() map[string]bool {
selections := make(map[string]bool, 0)
for id, playerInfo := range gm.players {
if !playerInfo.isPlaying {
continue
}
state := syncdata.PLAYER_STATE_UNDECIDED
selection := gm.round.selection[id]
if len(selection) > 0 {
state = syncdata.PLAYER_STATE_DECIDED
} else if playerInfo.isIdle {
state = syncdata.PLAYER_STATE_IDLE
if len(gm.round.selections[id]) > 0 {
selections[id] = true
} else if !playerInfo.isIdle {
selections[id] = false
}
}
playerState = append(playerState, syncdata.PlayerState{
Id: id,
State: state,
})
}
roundInfo := syncdata.RoundInfo{
Quote: quote.GetQuote(),
Sources: sources,
PlayerState: playerState,
}
return &roundInfo
return selections
}

View File

@ -5,7 +5,7 @@ import (
)
func (gm *Game) runRound() {
state := gm.GetState()
state, _ := gm.GetState()
if state != STATE_IDLE && state != STATE_PLAY {
fmt.Println(fmt.Errorf("expected state \"IDLE\" | \"PLAY\" != \"%s\"", state))
return
@ -27,7 +27,7 @@ func (gm *Game) setupRound() {
defer gm.mu.Unlock()
gm.round = Round{
selection: make(map[string]string, 0),
selections: make(map[string]string, 0),
}
}

View File

@ -1,6 +1,7 @@
package game
import (
"fmt"
"sirlab.de/go/knyt/user"
)
@ -13,5 +14,17 @@ func (gm *Game) updateSelection(usr *user.User, selection string) {
gm.mu.Lock()
defer gm.mu.Unlock()
gm.round.selection[usr.GetId()] = selection
if selection == PLAYER_SELECTION_NONE {
gm.round.selections[usr.GetId()] = PLAYER_SELECTION_NONE
return
}
for _, source := range gm.round.sources {
if source.id == selection {
gm.round.selections[usr.GetId()] = selection
return
}
}
fmt.Printf("invalid selection id \"%s\"\n", selection)
}

View File

@ -16,6 +16,12 @@ const (
const (
PHASE_NONE = ""
PHASE_SELECT_QUOTE = "select-quote"
PHASE_REVEAL_START = "reveal-start"
)
const (
PLAYER_SELECTION_EMPTY = ""
PLAYER_SELECTION_NONE = "-"
)
type playerInfo struct {
@ -32,7 +38,7 @@ type Source struct {
type Round struct {
quoteId string
selection map[string]string
selections map[string]string
sources []Source
}

View File

@ -25,6 +25,7 @@ func main() {
mux.PrivateHandleFunc("/api/sync", app.SyncHandler)
mux.PrivateHandleFunc("/api/startGame", app.StartGame)
mux.PrivateHandleFunc("/api/resetGame", app.ResetGame)
mux.PrivateHandleFunc("/api/continueGame", app.ContinueGame)
mux.PrivateHandleFunc("/api/saveSelection", app.SaveSelection)
// default handler

View File

@ -17,15 +17,10 @@ type SourceInfo struct {
Name string `json:"name"`
}
type PlayerState struct {
Id string `json:"id"`
State string `json:"state"`
}
type RoundInfo struct {
Quote string `json:"quote"`
Sources []SourceInfo `json:"sources"`
PlayerState []PlayerState `json:"state"`
Selections map[string]bool `json:"selections"`
}
type GameInfo struct {