save and store selection
This commit is contained in:
parent
0e5084144f
commit
d79e3a8956
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -29,7 +29,6 @@ export default {
|
||||
|
||||
&__player {
|
||||
display: flex;
|
||||
// height: 1.4em;
|
||||
color: #ffffff;
|
||||
font-family: Dosis;
|
||||
font-size: 18px;
|
||||
|
@ -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,16 +59,21 @@ export default {
|
||||
background-color: #402080;
|
||||
color: #ffffff;
|
||||
|
||||
&:hover {
|
||||
background-color: #8060c0;
|
||||
cursor: pointer;
|
||||
&-selectable {
|
||||
&:hover {
|
||||
background-color: #8060c0;
|
||||
cursor: pointer;
|
||||
}
|
||||
&.source__container__selected {
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 0 10px #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
&__selected,
|
||||
&__selected:hover {
|
||||
background-color: #ffffff;
|
||||
&__selected {
|
||||
background-color: #d0d0d0;
|
||||
color: #402080;
|
||||
box-shadow: 0 0 10px #ffffff;
|
||||
box-shadow: 0 0 10px #d0d0d0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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')
|
||||
},
|
||||
|
7
client/src/plugins/engine/continueGame.js
Normal file
7
client/src/plugins/engine/continueGame.js
Normal file
@ -0,0 +1,7 @@
|
||||
export default async function() {
|
||||
const { store } = this.context
|
||||
|
||||
await this.callApi('/api/continueGame', {
|
||||
g: store.state.engine.user?.game,
|
||||
})
|
||||
}
|
@ -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,
|
||||
}
|
||||
|
@ -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],
|
||||
})
|
||||
}
|
||||
|
@ -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 = {}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
25
server/src/application/continueGame.go
Normal file
25
server/src/application/continueGame.go
Normal 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()
|
||||
}
|
53
server/src/game/continueGame.go
Normal file
53
server/src/game/continueGame.go
Normal 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
|
||||
}
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
@ -31,9 +37,9 @@ type Source struct {
|
||||
}
|
||||
|
||||
type Round struct {
|
||||
quoteId string
|
||||
selection map[string]string
|
||||
sources []Source
|
||||
quoteId string
|
||||
selections map[string]string
|
||||
sources []Source
|
||||
}
|
||||
|
||||
type Game struct {
|
||||
|
@ -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
|
||||
|
@ -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"`
|
||||
Quote string `json:"quote"`
|
||||
Sources []SourceInfo `json:"sources"`
|
||||
Selections map[string]bool `json:"selections"`
|
||||
}
|
||||
|
||||
type GameInfo struct {
|
||||
|
Loading…
Reference in New Issue
Block a user