save and store selection
This commit is contained in:
parent
0e5084144f
commit
d79e3a8956
@ -2,6 +2,7 @@
|
|||||||
<nav v-if="isGamemaster" class="gamecontrols">
|
<nav v-if="isGamemaster" class="gamecontrols">
|
||||||
<button @click="startGame">Start</button>
|
<button @click="startGame">Start</button>
|
||||||
<button @click="resetGame">Reset</button>
|
<button @click="resetGame">Reset</button>
|
||||||
|
<button @click="continueGame">Continue</button>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -23,6 +24,9 @@ export default {
|
|||||||
resetGame() {
|
resetGame() {
|
||||||
this.$engine.resetGame()
|
this.$engine.resetGame()
|
||||||
},
|
},
|
||||||
|
continueGame() {
|
||||||
|
this.$engine.continueGame()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
<div class="play__layout">
|
<div class="play__layout">
|
||||||
<div class="play__layout-playground">
|
<div class="play__layout-playground">
|
||||||
<Quote :text="quote" />
|
<Quote :text="quote" />
|
||||||
<Sources :sources="sources" />
|
<Sources :sources="sources" :selectable="selectable" />
|
||||||
<ConfirmButton />
|
<ConfirmButton v-if="selectable"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="play__layout-right-column">
|
<div class="play__layout-right-column">
|
||||||
<PlayerList :players="players" />
|
<PlayerList :players="players" />
|
||||||
@ -28,6 +28,14 @@ export default {
|
|||||||
players() {
|
players() {
|
||||||
return this.$store.state.players.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>
|
</script>
|
||||||
|
@ -29,7 +29,6 @@ export default {
|
|||||||
|
|
||||||
&__player {
|
&__player {
|
||||||
display: flex;
|
display: flex;
|
||||||
// height: 1.4em;
|
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-family: Dosis;
|
font-family: Dosis;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="source">
|
<div class="source">
|
||||||
<div
|
<div
|
||||||
:class="['source__container', { 'source__container__selected': selected } ]"
|
:class="getCssClass"
|
||||||
@click="clicked"
|
@click="clicked"
|
||||||
>
|
>
|
||||||
<div class="source__source">
|
<div class="source__source">
|
||||||
@ -13,10 +13,14 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: ['source'],
|
props: ['source', 'selectable'],
|
||||||
computed: {
|
computed: {
|
||||||
selected() {
|
getCssClass() {
|
||||||
return this.isSelected()
|
return {
|
||||||
|
'source__container': true,
|
||||||
|
'source__container-selectable': this.selectable,
|
||||||
|
'source__container__selected': this.isSelected(),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -24,6 +28,8 @@ export default {
|
|||||||
return this.$store.state.selection.selection[this.source.id]
|
return this.$store.state.selection.selection[this.source.id]
|
||||||
},
|
},
|
||||||
clicked() {
|
clicked() {
|
||||||
|
if (!this.selectable) return
|
||||||
|
|
||||||
if (this.isSelected()) {
|
if (this.isSelected()) {
|
||||||
this.$store.commit('selection/unselect', this.source.id)
|
this.$store.commit('selection/unselect', this.source.id)
|
||||||
} else {
|
} else {
|
||||||
@ -53,16 +59,21 @@ export default {
|
|||||||
background-color: #402080;
|
background-color: #402080;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
|
|
||||||
&:hover {
|
&-selectable {
|
||||||
background-color: #8060c0;
|
&:hover {
|
||||||
cursor: pointer;
|
background-color: #8060c0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
&.source__container__selected {
|
||||||
|
background-color: #ffffff;
|
||||||
|
box-shadow: 0 0 10px #ffffff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__selected,
|
&__selected {
|
||||||
&__selected:hover {
|
background-color: #d0d0d0;
|
||||||
background-color: #ffffff;
|
|
||||||
color: #402080;
|
color: #402080;
|
||||||
box-shadow: 0 0 10px #ffffff;
|
box-shadow: 0 0 10px #d0d0d0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
v-for="(source, idx) in sources"
|
v-for="(source, idx) in sources"
|
||||||
:class="['sources__source-' + idx]"
|
:class="['sources__source-' + idx]"
|
||||||
:source="source"
|
:source="source"
|
||||||
|
:selectable="selectable"
|
||||||
:key="source.id"
|
:key="source.id"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -16,7 +17,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: ['sources'],
|
props: ['sources', 'selectable'],
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$store.commit('selection/clearSelection')
|
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 fetchUserInfo from './fetchUserInfo'
|
||||||
import startGame from './startGame'
|
import startGame from './startGame'
|
||||||
import resetGame from './resetGame'
|
import resetGame from './resetGame'
|
||||||
|
import continueGame from './continueGame'
|
||||||
import parseSyncData from './parseSyncData'
|
import parseSyncData from './parseSyncData'
|
||||||
import saveSelection from './saveSelection'
|
import saveSelection from './saveSelection'
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ export default (context, inject) => {
|
|||||||
fetchUserInfo,
|
fetchUserInfo,
|
||||||
startGame,
|
startGame,
|
||||||
resetGame,
|
resetGame,
|
||||||
|
continueGame,
|
||||||
parseSyncData,
|
parseSyncData,
|
||||||
saveSelection,
|
saveSelection,
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
export default async function(selection) {
|
export default async function(selection) {
|
||||||
const { store } = this.context
|
const { store } = this.context
|
||||||
|
|
||||||
|
if (selection.length === 0) {
|
||||||
|
selection = ['-']
|
||||||
|
}
|
||||||
|
|
||||||
await this.callApi('/api/saveSelection', {
|
await this.callApi('/api/saveSelection', {
|
||||||
g: store.state.engine.user?.game,
|
g: store.state.engine.user?.game,
|
||||||
selection: selection.join(','),
|
selection: selection[0],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
export const state = () => ({
|
export const state = () => ({
|
||||||
quote: "",
|
quote: "",
|
||||||
sources: [],
|
sources: [],
|
||||||
|
selections: {},
|
||||||
})
|
})
|
||||||
|
|
||||||
export const mutations = {
|
export const mutations = {
|
||||||
@ -8,9 +9,11 @@ export const mutations = {
|
|||||||
if (game && game.round) {
|
if (game && game.round) {
|
||||||
state.quote = game.round.quote
|
state.quote = game.round.quote
|
||||||
state.sources = game.round.sources
|
state.sources = game.round.sources
|
||||||
|
state.selections = game.round.selections
|
||||||
} else {
|
} else {
|
||||||
state.quote = ""
|
state.quote = ""
|
||||||
state.sources = []
|
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
|
return gm.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gm *Game) GetState() string {
|
func (gm *Game) GetState() (string, string) {
|
||||||
gm.mu.Lock()
|
gm.mu.Lock()
|
||||||
defer gm.mu.Unlock()
|
defer gm.mu.Unlock()
|
||||||
|
|
||||||
return gm.state
|
return gm.state, gm.phase
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gm *Game) GetName() string {
|
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 {
|
for id, playerInfo := range gm.players {
|
||||||
if !playerInfo.isPlaying {
|
if !playerInfo.isPlaying {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
state := syncdata.PLAYER_STATE_UNDECIDED
|
if len(gm.round.selections[id]) > 0 {
|
||||||
selection := gm.round.selection[id]
|
selections[id] = true
|
||||||
if len(selection) > 0 {
|
} else if !playerInfo.isIdle {
|
||||||
state = syncdata.PLAYER_STATE_DECIDED
|
selections[id] = false
|
||||||
} else if playerInfo.isIdle {
|
|
||||||
state = syncdata.PLAYER_STATE_IDLE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
playerState = append(playerState, syncdata.PlayerState{
|
|
||||||
Id: id,
|
|
||||||
State: state,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
roundInfo := syncdata.RoundInfo{
|
return selections
|
||||||
Quote: quote.GetQuote(),
|
|
||||||
Sources: sources,
|
|
||||||
PlayerState: playerState,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &roundInfo
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (gm *Game) runRound() {
|
func (gm *Game) runRound() {
|
||||||
state := gm.GetState()
|
state, _ := gm.GetState()
|
||||||
if state != STATE_IDLE && state != STATE_PLAY {
|
if state != STATE_IDLE && state != STATE_PLAY {
|
||||||
fmt.Println(fmt.Errorf("expected state \"IDLE\" | \"PLAY\" != \"%s\"", state))
|
fmt.Println(fmt.Errorf("expected state \"IDLE\" | \"PLAY\" != \"%s\"", state))
|
||||||
return
|
return
|
||||||
@ -27,7 +27,7 @@ func (gm *Game) setupRound() {
|
|||||||
defer gm.mu.Unlock()
|
defer gm.mu.Unlock()
|
||||||
|
|
||||||
gm.round = Round{
|
gm.round = Round{
|
||||||
selection: make(map[string]string, 0),
|
selections: make(map[string]string, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"sirlab.de/go/knyt/user"
|
"sirlab.de/go/knyt/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,5 +14,17 @@ func (gm *Game) updateSelection(usr *user.User, selection string) {
|
|||||||
gm.mu.Lock()
|
gm.mu.Lock()
|
||||||
defer gm.mu.Unlock()
|
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 (
|
const (
|
||||||
PHASE_NONE = ""
|
PHASE_NONE = ""
|
||||||
PHASE_SELECT_QUOTE = "select-quote"
|
PHASE_SELECT_QUOTE = "select-quote"
|
||||||
|
PHASE_REVEAL_START = "reveal-start"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PLAYER_SELECTION_EMPTY = ""
|
||||||
|
PLAYER_SELECTION_NONE = "-"
|
||||||
)
|
)
|
||||||
|
|
||||||
type playerInfo struct {
|
type playerInfo struct {
|
||||||
@ -31,9 +37,9 @@ type Source struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Round struct {
|
type Round struct {
|
||||||
quoteId string
|
quoteId string
|
||||||
selection map[string]string
|
selections map[string]string
|
||||||
sources []Source
|
sources []Source
|
||||||
}
|
}
|
||||||
|
|
||||||
type Game struct {
|
type Game struct {
|
||||||
|
@ -25,6 +25,7 @@ func main() {
|
|||||||
mux.PrivateHandleFunc("/api/sync", app.SyncHandler)
|
mux.PrivateHandleFunc("/api/sync", app.SyncHandler)
|
||||||
mux.PrivateHandleFunc("/api/startGame", app.StartGame)
|
mux.PrivateHandleFunc("/api/startGame", app.StartGame)
|
||||||
mux.PrivateHandleFunc("/api/resetGame", app.ResetGame)
|
mux.PrivateHandleFunc("/api/resetGame", app.ResetGame)
|
||||||
|
mux.PrivateHandleFunc("/api/continueGame", app.ContinueGame)
|
||||||
mux.PrivateHandleFunc("/api/saveSelection", app.SaveSelection)
|
mux.PrivateHandleFunc("/api/saveSelection", app.SaveSelection)
|
||||||
|
|
||||||
// default handler
|
// default handler
|
||||||
|
@ -17,15 +17,10 @@ type SourceInfo struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PlayerState struct {
|
|
||||||
Id string `json:"id"`
|
|
||||||
State string `json:"state"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RoundInfo struct {
|
type RoundInfo struct {
|
||||||
Quote string `json:"quote"`
|
Quote string `json:"quote"`
|
||||||
Sources []SourceInfo `json:"sources"`
|
Sources []SourceInfo `json:"sources"`
|
||||||
PlayerState []PlayerState `json:"state"`
|
Selections map[string]bool `json:"selections"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GameInfo struct {
|
type GameInfo struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user