Allow liking plays once the round is complete.
This commit is contained in:
Generated
+3450
-2183
File diff suppressed because it is too large
Load Diff
+33
-32
@@ -22,60 +22,61 @@
|
||||
"> 0.2%"
|
||||
],
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.12.1",
|
||||
"@material/card": "^6.0.0",
|
||||
"@material/mwc-button": "^0.15.0",
|
||||
"@material/mwc-fab": "^0.15.0",
|
||||
"@material/mwc-icon-button": "^0.15.0",
|
||||
"@material/mwc-linear-progress": "^0.15.0",
|
||||
"@material/mwc-menu": "^0.15.0",
|
||||
"@material/mwc-select": "^0.15.0",
|
||||
"@material/mwc-slider": "^0.15.0",
|
||||
"@material/mwc-switch": "^0.15.0",
|
||||
"@material/mwc-tab": "^0.15.0",
|
||||
"@material/mwc-tab-bar": "^0.15.0",
|
||||
"@material/mwc-textarea": "^0.15.0",
|
||||
"@material/mwc-textfield": "^0.15.0",
|
||||
"@fortawesome/fontawesome-free": "^5.14.0",
|
||||
"@material/card": "^7.0.0",
|
||||
"@material/mwc-button": "^0.18.0",
|
||||
"@material/mwc-fab": "^0.18.0",
|
||||
"@material/mwc-icon-button": "^0.18.0",
|
||||
"@material/mwc-linear-progress": "^0.18.0",
|
||||
"@material/mwc-menu": "^0.18.0",
|
||||
"@material/mwc-select": "^0.18.0",
|
||||
"@material/mwc-slider": "^0.18.0",
|
||||
"@material/mwc-switch": "^0.18.0",
|
||||
"@material/mwc-tab": "^0.18.0",
|
||||
"@material/mwc-tab-bar": "^0.18.0",
|
||||
"@material/mwc-textarea": "^0.18.0",
|
||||
"@material/mwc-textfield": "^0.18.0",
|
||||
"@polymer/paper-tooltip": "^3.0.1",
|
||||
"@webcomponents/webcomponentsjs": "^2.4.3",
|
||||
"@webcomponents/webcomponentsjs": "^2.4.4",
|
||||
"canvas-confetti": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/canvas-confetti": "^1.0.0",
|
||||
"@types/chrome": "^0.0.114",
|
||||
"@types/chromecast-caf-receiver": "^5.0.5",
|
||||
"@types/chrome": "^0.0.122",
|
||||
"@types/chromecast-caf-receiver": "^5.0.11",
|
||||
"@types/chromecast-caf-sender": "^1.0.3",
|
||||
"app-manifest-loader": "^2.4.1",
|
||||
"chromecast-device-emulator": "^1.2.7",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"closure-webpack-plugin": "^2.3.0",
|
||||
"compression-webpack-plugin": "^4.0.0",
|
||||
"compression-webpack-plugin": "^5.0.2",
|
||||
"css-hot-loader": "^1.4.4",
|
||||
"css-loader": "^3.6.0",
|
||||
"css-loader": "^4.2.2",
|
||||
"cssnano": "^4.1.10",
|
||||
"elm": "^0.19.1-3",
|
||||
"elm-hot-webpack-loader": "^1.1.6",
|
||||
"elm-analyse": "^0.16.5",
|
||||
"elm-hot-webpack-loader": "^1.1.7",
|
||||
"elm-webpack-loader": "^6.0.1",
|
||||
"extract-loader": "^5.1.0",
|
||||
"fibers": "^5.0.0",
|
||||
"file-loader": "^6.0.0",
|
||||
"google-closure-compiler": "^20200517.0.0",
|
||||
"html-loader": "^1.1.0",
|
||||
"html-webpack-plugin": "^4.3.0",
|
||||
"file-loader": "^6.1.0",
|
||||
"google-closure-compiler": "^20200830.0.0",
|
||||
"html-loader": "^1.3.0",
|
||||
"html-webpack-plugin": "^4.4.1",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-preset-env": "^6.7.0",
|
||||
"prettier": "2.0.5",
|
||||
"prettier": "2.1.1",
|
||||
"prettier-plugin-elm": "^0.7.0",
|
||||
"resolve-url-loader": "^3.1.1",
|
||||
"sass": "^1.26.5",
|
||||
"sass-loader": "^8.0.2",
|
||||
"sass": "^1.26.10",
|
||||
"sass-loader": "^10.0.2",
|
||||
"style-loader": "^1.2.1",
|
||||
"terser-webpack-plugin": "^3.0.4",
|
||||
"ts-loader": "^7.0.5",
|
||||
"typescript": "^3.9.3",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"terser-webpack-plugin": "^4.1.0",
|
||||
"ts-loader": "^8.0.3",
|
||||
"typescript": "^4.0.2",
|
||||
"webpack": "^4.44.1",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,22 +69,25 @@ init wrap game hand pick =
|
||||
model =
|
||||
emptyModel game
|
||||
|
||||
( round, roundCmd ) =
|
||||
case game.round of
|
||||
Round.P r ->
|
||||
round =
|
||||
model.game.round
|
||||
|
||||
( newRound, roundCmd ) =
|
||||
case round.stage of
|
||||
Round.P stage ->
|
||||
let
|
||||
( playing, cmd ) =
|
||||
Playing.init wrap r pick
|
||||
Playing.init wrap (Round.withStage stage round) pick
|
||||
in
|
||||
( Round.P playing, cmd )
|
||||
( playing, cmd )
|
||||
|
||||
_ ->
|
||||
( game.round, Cmd.none )
|
||||
( round, Cmd.none )
|
||||
|
||||
timeCmd =
|
||||
Time.now (UpdateTimer >> wrap)
|
||||
in
|
||||
( { model | hand = hand, game = { game | round = round } }, Cmd.batch [ roundCmd, timeCmd ] )
|
||||
( { model | hand = hand, game = { game | round = newRound } }, Cmd.batch [ roundCmd, timeCmd ] )
|
||||
|
||||
|
||||
update : (Msg -> msg) -> Shared -> Msg -> Model -> ( Model, Cmd msg )
|
||||
@@ -92,14 +95,17 @@ update wrap shared msg model =
|
||||
let
|
||||
game =
|
||||
model.game
|
||||
|
||||
round =
|
||||
game.round
|
||||
in
|
||||
case msg of
|
||||
Pick maybeFor played ->
|
||||
case game.round of
|
||||
Round.P playingRound ->
|
||||
case round.stage of
|
||||
Round.P stage ->
|
||||
let
|
||||
picks =
|
||||
playingRound.pick
|
||||
stage.pick
|
||||
|
||||
picked =
|
||||
case maybeFor of
|
||||
@@ -113,7 +119,7 @@ update wrap shared msg model =
|
||||
else
|
||||
let
|
||||
missing =
|
||||
Parts.missingSlotIndices picks.cards playingRound.call.body |> Set.toList
|
||||
Parts.missingSlotIndices picks.cards round.call.body |> Set.toList
|
||||
in
|
||||
case missing of
|
||||
next :: _ ->
|
||||
@@ -126,33 +132,33 @@ update wrap shared msg model =
|
||||
in
|
||||
picks.cards |> Dict.insert last played
|
||||
|
||||
newRound =
|
||||
Round.P { playingRound | pick = { picks | cards = picked } }
|
||||
newStage =
|
||||
Round.P { stage | pick = { picks | cards = picked } }
|
||||
|
||||
focus =
|
||||
Dom.focus played
|
||||
|> Task.onError (\_ -> Task.succeed ())
|
||||
|> Task.perform (\_ -> wrap NoOp)
|
||||
in
|
||||
( { model | game = { game | round = newRound } }, focus )
|
||||
( { model | game = { game | round = { round | stage = newStage } } }, focus )
|
||||
|
||||
_ ->
|
||||
( model, Cmd.none )
|
||||
|
||||
Unpick slotId ->
|
||||
case game.round of
|
||||
Round.P playingRound ->
|
||||
case round.stage of
|
||||
Round.P stage ->
|
||||
let
|
||||
picks =
|
||||
playingRound.pick
|
||||
stage.pick
|
||||
|
||||
picked =
|
||||
picks.cards |> Dict.remove slotId
|
||||
|
||||
newRound =
|
||||
Round.P { playingRound | pick = { picks | cards = picked } }
|
||||
newStage =
|
||||
Round.P { stage | pick = { picks | cards = picked } }
|
||||
in
|
||||
( { model | game = { game | round = newRound } }, Cmd.none )
|
||||
( { model | game = { game | round = { round | stage = newStage } } }, Cmd.none )
|
||||
|
||||
_ ->
|
||||
( model, Cmd.none )
|
||||
@@ -162,12 +168,12 @@ update wrap shared msg model =
|
||||
( dragDrop, result ) =
|
||||
DragDrop.update dragDropMsg model.dragDrop
|
||||
|
||||
newRound =
|
||||
case game.round of
|
||||
Round.P playingRound ->
|
||||
newStage =
|
||||
case round.stage of
|
||||
Round.P stage ->
|
||||
let
|
||||
picks =
|
||||
playingRound.pick
|
||||
stage.pick
|
||||
|
||||
picked =
|
||||
case result of
|
||||
@@ -179,17 +185,17 @@ update wrap shared msg model =
|
||||
Nothing ->
|
||||
picks.cards
|
||||
in
|
||||
Round.P { playingRound | pick = { picks | cards = picked } }
|
||||
Round.P { stage | pick = { picks | cards = picked } }
|
||||
|
||||
_ ->
|
||||
game.round
|
||||
stage ->
|
||||
stage
|
||||
in
|
||||
( { model | game = { game | round = newRound }, dragDrop = dragDrop }, Cmd.none )
|
||||
( { model | game = { game | round = { round | stage = newStage } }, dragDrop = dragDrop }, Cmd.none )
|
||||
|
||||
EditBlank id text ->
|
||||
case game.round of
|
||||
Round.P playingRound ->
|
||||
if playingRound.pick.state /= Round.Submitted then
|
||||
case round.stage of
|
||||
Round.P stage ->
|
||||
if stage.pick.state /= Round.Submitted then
|
||||
( { model | filledCards = model.filledCards |> Dict.insert id text }, Cmd.none )
|
||||
|
||||
else
|
||||
@@ -219,36 +225,36 @@ update wrap shared msg model =
|
||||
|
||||
PickPlay id ->
|
||||
let
|
||||
makePick r wrapRound =
|
||||
makePick stage wrapRound =
|
||||
let
|
||||
pick =
|
||||
if r.pick == Just id then
|
||||
if stage.pick == Just id then
|
||||
Nothing
|
||||
|
||||
else
|
||||
Just id
|
||||
in
|
||||
{ r | pick = pick } |> wrapRound
|
||||
{ stage | pick = pick } |> wrapRound
|
||||
|
||||
newRound =
|
||||
case game.round of
|
||||
newStage =
|
||||
case round.stage of
|
||||
Round.J judging ->
|
||||
makePick judging Round.J
|
||||
|
||||
Round.R revealing ->
|
||||
makePick revealing Round.R
|
||||
|
||||
_ ->
|
||||
game.round
|
||||
stage ->
|
||||
stage
|
||||
in
|
||||
( { model | game = { game | round = newRound } }, Cmd.none )
|
||||
( { model | game = { game | round = { round | stage = newStage } } }, Cmd.none )
|
||||
|
||||
Submit ->
|
||||
case game.round of
|
||||
Round.P playingRound ->
|
||||
case round.stage of
|
||||
Round.P stage ->
|
||||
let
|
||||
picks =
|
||||
playingRound.pick
|
||||
stage.pick
|
||||
|
||||
fillIfNeeded card =
|
||||
if card.details.source == Source.Custom then
|
||||
@@ -276,10 +282,10 @@ update wrap shared msg model =
|
||||
fills =
|
||||
model.hand |> List.filterMap fillIfNeeded
|
||||
|
||||
newRound =
|
||||
Round.P { playingRound | pick = { picks | state = Round.Submitted } }
|
||||
newStage =
|
||||
Round.P { stage | pick = { picks | state = Round.Submitted } }
|
||||
in
|
||||
( { model | game = { game | round = newRound } }
|
||||
( { model | game = { game | round = { round | stage = newStage } } }
|
||||
, Cmd.batch (fills ++ [ picks.cards |> Dict.values |> Actions.submit ])
|
||||
)
|
||||
|
||||
@@ -287,16 +293,16 @@ update wrap shared msg model =
|
||||
( model, Cmd.none )
|
||||
|
||||
TakeBack ->
|
||||
case game.round of
|
||||
Round.P playingRound ->
|
||||
case round.stage of
|
||||
Round.P stage ->
|
||||
let
|
||||
picks =
|
||||
playingRound.pick
|
||||
stage.pick
|
||||
|
||||
newRound =
|
||||
Round.P { playingRound | pick = { picks | state = Round.Selected } }
|
||||
newStage =
|
||||
Round.P { stage | pick = { picks | state = Round.Selected } }
|
||||
in
|
||||
( { model | game = { game | round = newRound } }, Actions.takeBack )
|
||||
( { model | game = { game | round = { round | stage = newStage } } }, Actions.takeBack )
|
||||
|
||||
_ ->
|
||||
( model, Cmd.none )
|
||||
@@ -310,7 +316,7 @@ update wrap shared msg model =
|
||||
Judge ->
|
||||
let
|
||||
cmd =
|
||||
case game.round of
|
||||
case round.stage of
|
||||
Round.J judging ->
|
||||
judging.pick |> Maybe.map Actions.judge |> Maybe.withDefault Cmd.none
|
||||
|
||||
@@ -321,36 +327,36 @@ update wrap shared msg model =
|
||||
|
||||
Like ->
|
||||
let
|
||||
( newRound, action ) =
|
||||
( newStage, action ) =
|
||||
let
|
||||
like r roundWrap =
|
||||
like stage roundWrap =
|
||||
let
|
||||
likeDetail =
|
||||
r.likeDetail
|
||||
stage.likeDetail
|
||||
|
||||
insert =
|
||||
r.pick
|
||||
stage.pick
|
||||
|> Maybe.map Set.insert
|
||||
|> Maybe.withDefault identity
|
||||
|
||||
newLikeDetail =
|
||||
{ likeDetail | liked = insert likeDetail.liked }
|
||||
in
|
||||
( { r | pick = Nothing, likeDetail = newLikeDetail } |> roundWrap
|
||||
, r.pick |> Maybe.map Actions.like |> Maybe.withDefault Cmd.none
|
||||
( { stage | pick = Nothing, likeDetail = newLikeDetail } |> roundWrap
|
||||
, stage.pick |> Maybe.map Actions.like |> Maybe.withDefault Cmd.none
|
||||
)
|
||||
in
|
||||
case game.round of
|
||||
case round.stage of
|
||||
Round.J judging ->
|
||||
like judging Round.J
|
||||
|
||||
Round.R revealing ->
|
||||
like revealing Round.R
|
||||
|
||||
_ ->
|
||||
( game.round, Cmd.none )
|
||||
stage ->
|
||||
( stage, Cmd.none )
|
||||
in
|
||||
( { model | game = { game | round = newRound } }, action )
|
||||
( { model | game = { game | round = { round | stage = newStage } } }, action )
|
||||
|
||||
SetPlayStyles playStyles ->
|
||||
( { model | playStyles = playStyles }, Cmd.none )
|
||||
@@ -359,11 +365,8 @@ update wrap shared msg model =
|
||||
case model.completeRound of
|
||||
Just _ ->
|
||||
let
|
||||
round =
|
||||
model.game.round
|
||||
|
||||
tts =
|
||||
speak shared (round |> Round.data |> .call) Nothing
|
||||
speak shared round.call Nothing
|
||||
in
|
||||
( { model | completeRound = Nothing }
|
||||
, tts
|
||||
@@ -376,11 +379,11 @@ update wrap shared msg model =
|
||||
( model, Actions.redraw )
|
||||
|
||||
Discard ->
|
||||
case model.game.round of
|
||||
Round.P p ->
|
||||
case round.stage of
|
||||
Round.P stage ->
|
||||
let
|
||||
action =
|
||||
p.pick.cards |> Dict.values |> List.head |> Maybe.map Actions.discard
|
||||
stage.pick.cards |> Dict.values |> List.head |> Maybe.map Actions.discard
|
||||
in
|
||||
( model, action |> Maybe.withDefault Cmd.none )
|
||||
|
||||
@@ -407,7 +410,7 @@ update wrap shared msg model =
|
||||
( { model | time = Just time }, Cmd.none )
|
||||
|
||||
EnforceTimeLimit ->
|
||||
( model, Actions.enforceTimeLimit (model.game.round |> Round.data |> .id) (model.game.round |> Round.stage) )
|
||||
( model, Actions.enforceTimeLimit round.id (round.stage |> Round.stage) )
|
||||
|
||||
ToggleHelp ->
|
||||
( { model | helpVisible = not model.helpVisible }, Cmd.none )
|
||||
@@ -493,12 +496,16 @@ view wrapLobby wrap shared auth timeAnchor name config users model =
|
||||
|
||||
applyGameEvent : (Msg -> msg) -> (Event -> msg) -> Lobby.Auth -> Shared -> Events.GameEvent -> Model -> ( Model, Cmd msg )
|
||||
applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
let
|
||||
game =
|
||||
model.game
|
||||
|
||||
round =
|
||||
game.round
|
||||
in
|
||||
case gameEvent of
|
||||
Events.HandRedrawn { player, hand } ->
|
||||
let
|
||||
game =
|
||||
model.game
|
||||
|
||||
-- TODO: Error, if the rule isn't enabled, we are out of sync.
|
||||
cost =
|
||||
game.rules.houseRules.reboot |> Maybe.map .cost |> Maybe.withDefault 0
|
||||
@@ -510,9 +517,9 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
newHand =
|
||||
hand |> Maybe.andThen (Maybe.justIf (player == auth.claims.uid)) |> Maybe.withDefault model.hand
|
||||
|
||||
newRound =
|
||||
newStage =
|
||||
if player == auth.claims.uid then
|
||||
case game.round of
|
||||
case round.stage of
|
||||
Round.P playing ->
|
||||
let
|
||||
pick =
|
||||
@@ -520,25 +527,25 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
in
|
||||
Round.P { playing | pick = { pick | cards = Dict.empty } }
|
||||
|
||||
other ->
|
||||
other
|
||||
stage ->
|
||||
stage
|
||||
|
||||
else
|
||||
game.round
|
||||
round.stage
|
||||
|
||||
players =
|
||||
game.players |> Dict.map updatePlayer
|
||||
in
|
||||
( { model | game = { game | players = players, round = newRound }, hand = newHand }, Cmd.none )
|
||||
( { model | game = { game | players = players, round = { round | stage = newStage } }, hand = newHand }, Cmd.none )
|
||||
|
||||
Events.PlaySubmitted { by } ->
|
||||
case model.game.round of
|
||||
Round.P round ->
|
||||
case round.stage of
|
||||
Round.P stage ->
|
||||
let
|
||||
game =
|
||||
model.game
|
||||
newStage =
|
||||
Round.P { stage | played = Set.insert by stage.played }
|
||||
in
|
||||
( { model | game = { game | round = Round.P { round | played = Set.insert by round.played } } }
|
||||
( { model | game = { game | round = { round | stage = newStage } } }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
@@ -547,13 +554,31 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
( model, Cmd.none )
|
||||
|
||||
Events.PlayTakenBack { by } ->
|
||||
case model.game.round of
|
||||
Round.P round ->
|
||||
case round.stage of
|
||||
Round.P stage ->
|
||||
let
|
||||
game =
|
||||
model.game
|
||||
newStage =
|
||||
Round.P { stage | played = Set.remove by stage.played }
|
||||
in
|
||||
( { model | game = { game | round = Round.P { round | played = Set.remove by round.played } } }
|
||||
( { model | game = { game | round = { round | stage = newStage } } }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
_ ->
|
||||
-- TODO: Error
|
||||
( model, Cmd.none )
|
||||
|
||||
Events.PlayLiked { play } ->
|
||||
case round.stage of
|
||||
Round.C stage ->
|
||||
let
|
||||
increment withLikes =
|
||||
{ withLikes | likes = withLikes.likes |> Maybe.withDefault 0 |> (+) 1 |> Just }
|
||||
|
||||
newStage =
|
||||
{ stage | plays = Dict.update play (Maybe.map increment) stage.plays }
|
||||
in
|
||||
( { model | game = { game | round = { round | stage = Round.C newStage } } }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
@@ -575,7 +600,7 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
Events.Timed (Events.WithTime { event, time }) ->
|
||||
case event of
|
||||
Events.StartRevealing { plays, afterPlaying } ->
|
||||
case model.game.round of
|
||||
case round.stage of
|
||||
Round.P r ->
|
||||
let
|
||||
{ played, drawn } =
|
||||
@@ -584,15 +609,12 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
oldGame =
|
||||
model.game
|
||||
|
||||
newRound =
|
||||
Round.revealing
|
||||
(Just { played = played, liked = Set.empty })
|
||||
r.id
|
||||
r.czar
|
||||
r.players
|
||||
r.call
|
||||
newStage =
|
||||
Round.Revealing
|
||||
{ played = played, liked = Set.empty }
|
||||
Nothing
|
||||
Nothing
|
||||
(plays |> List.map (\id -> Play id Nothing))
|
||||
time
|
||||
False
|
||||
|> Round.R
|
||||
|
||||
@@ -605,7 +627,7 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
|> (\h -> h ++ (drawn |> Maybe.withDefault []))
|
||||
|
||||
role =
|
||||
Player.role newRound auth.claims.uid
|
||||
Player.role round auth.claims.uid
|
||||
|
||||
notification =
|
||||
if role == Player.RCzar then
|
||||
@@ -617,7 +639,9 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
else
|
||||
Cmd.none
|
||||
in
|
||||
( { model | game = { oldGame | round = newRound }, hand = newHand }, notification )
|
||||
( { model | game = { oldGame | round = Round.withStage newStage round }, hand = newHand }
|
||||
, notification
|
||||
)
|
||||
|
||||
_ ->
|
||||
-- TODO: Error
|
||||
@@ -625,32 +649,24 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
|
||||
Events.RoundFinished { winner, playedBy } ->
|
||||
let
|
||||
game =
|
||||
model.game
|
||||
|
||||
playersWithWinner =
|
||||
game.players |> Dict.update winner (Maybe.map (\p -> { p | score = p.score + 1 }))
|
||||
|
||||
{ newRound, history, speech, newPlayers, filledCards } =
|
||||
case game.round of
|
||||
case round.stage of
|
||||
Round.J r ->
|
||||
let
|
||||
plays =
|
||||
r.plays |> List.filterMap (resolvePlayedBy playedBy)
|
||||
|
||||
playsDict =
|
||||
plays |> Dict.fromList
|
||||
( by, plays, playOrder ) =
|
||||
resolvePlayedBy r.plays playedBy
|
||||
|
||||
complete =
|
||||
Round.complete
|
||||
r.id
|
||||
r.czar
|
||||
r.players
|
||||
r.call
|
||||
playsDict
|
||||
(plays |> List.map (\( u, _ ) -> u))
|
||||
Round.Complete
|
||||
r.likeDetail
|
||||
r.pick
|
||||
by
|
||||
plays
|
||||
playOrder
|
||||
winner
|
||||
time
|
||||
|
||||
handIds =
|
||||
model.hand |> List.map (.details >> .id) |> Set.fromList
|
||||
@@ -659,15 +675,15 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
Set.member id handIds
|
||||
|
||||
tts =
|
||||
Dict.get winner playsDict
|
||||
|> Maybe.map (\p -> speak shared r.call (p.play |> Parts.fillsFromPlay |> Just))
|
||||
Dict.get winner plays
|
||||
|> Maybe.map (\p -> speak shared round.call (p.play |> Parts.fillsFromPlay |> Just))
|
||||
|> Maybe.withDefault Cmd.none
|
||||
|
||||
ps =
|
||||
playersWithWinner |> Dict.map (updateLikes playsDict)
|
||||
playersWithWinner |> Dict.map (updateLikes plays)
|
||||
in
|
||||
{ newRound = complete |> Round.C
|
||||
, history = complete :: game.history
|
||||
{ newRound = Round.withStage (Round.C complete) round
|
||||
, history = Round.withStage complete round :: game.history
|
||||
, speech = tts
|
||||
, newPlayers = ps
|
||||
, filledCards = model.filledCards |> Dict.filter isStillInHand
|
||||
@@ -675,7 +691,7 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
|
||||
_ ->
|
||||
-- TODO: Error
|
||||
{ newRound = game.round
|
||||
{ newRound = round
|
||||
, history = game.history
|
||||
, speech = Cmd.none
|
||||
, newPlayers = playersWithWinner
|
||||
@@ -696,14 +712,21 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
|
||||
Events.RoundStarted { id, czar, players, call, drawn } ->
|
||||
let
|
||||
game =
|
||||
model.game
|
||||
|
||||
drawnAsList =
|
||||
drawn |> Maybe.withDefault []
|
||||
|
||||
( newRound, cmd ) =
|
||||
Playing.init wrap (Round.playing id czar players call Set.empty time False) Round.noPick
|
||||
let
|
||||
r =
|
||||
{ id = id
|
||||
, czar = czar
|
||||
, players = players
|
||||
, call = call
|
||||
, startedAt = time
|
||||
, stage = Round.Playing Round.noPick Set.empty False
|
||||
}
|
||||
in
|
||||
Playing.init wrap r Round.noPick
|
||||
|
||||
notification =
|
||||
if Set.member auth.claims.uid players then
|
||||
@@ -720,15 +743,15 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
Nothing
|
||||
|
||||
else
|
||||
case model.game.round of
|
||||
Round.C c ->
|
||||
Just c
|
||||
case round.stage of
|
||||
Round.C stage ->
|
||||
round |> Round.withStage stage |> Just
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
in
|
||||
( { model
|
||||
| game = { game | round = Round.P newRound }
|
||||
| game = { game | round = newRound }
|
||||
, completeRound = completeRound
|
||||
, hand = model.hand ++ drawnAsList
|
||||
}
|
||||
@@ -737,27 +760,24 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
|
||||
Events.PlayRevealed { id, play } ->
|
||||
let
|
||||
game =
|
||||
model.game
|
||||
|
||||
( newRound, speech ) =
|
||||
case model.game.round of
|
||||
Round.R r ->
|
||||
case round.stage of
|
||||
Round.R stage ->
|
||||
let
|
||||
plays =
|
||||
r.plays |> List.map (reveal id play)
|
||||
stage.plays |> List.map (reveal id play)
|
||||
|
||||
round =
|
||||
{ r | plays = plays, lastRevealed = Just id } |> Round.R
|
||||
newStage =
|
||||
{ stage | plays = plays, lastRevealed = Just id } |> Round.R
|
||||
|
||||
tts =
|
||||
speak shared r.call (play |> Parts.fillsFromPlay |> Just)
|
||||
speak shared round.call (play |> Parts.fillsFromPlay |> Just)
|
||||
in
|
||||
( round, tts )
|
||||
( { round | stage = newStage }, tts )
|
||||
|
||||
_ ->
|
||||
-- TODO: Error
|
||||
( model.game.round, Cmd.none )
|
||||
( round, Cmd.none )
|
||||
in
|
||||
( { model | game = { game | round = newRound } }, speech )
|
||||
|
||||
@@ -766,63 +786,53 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
{ played, drawn } =
|
||||
afterPlaying
|
||||
|
||||
game =
|
||||
model.game
|
||||
|
||||
makeNewRound r pick ld known =
|
||||
makeNewRound stage pick ld known =
|
||||
case known of
|
||||
Just k ->
|
||||
Round.judging pick
|
||||
Round.Judging
|
||||
ld
|
||||
r.id
|
||||
r.czar
|
||||
r.players
|
||||
r.call
|
||||
pick
|
||||
k
|
||||
time
|
||||
False
|
||||
|> Round.J
|
||||
|
||||
Nothing ->
|
||||
-- TODO: Error.
|
||||
game.round
|
||||
stage
|
||||
|
||||
( newRound, newHand ) =
|
||||
case game.round of
|
||||
Round.P r ->
|
||||
( newStage, newHand ) =
|
||||
case round.stage of
|
||||
Round.P stage ->
|
||||
let
|
||||
picked =
|
||||
r.pick.cards |> Dict.values |> Set.fromList
|
||||
stage.pick.cards |> Dict.values |> Set.fromList
|
||||
|
||||
nH =
|
||||
model.hand
|
||||
|> List.filter (\c -> not (Set.member c.details.id picked))
|
||||
|> (\h -> h ++ (drawn |> Maybe.withDefault []))
|
||||
in
|
||||
( makeNewRound r Nothing (Just { played = played, liked = Set.empty }) plays
|
||||
( makeNewRound round.stage Nothing { played = played, liked = Set.empty } plays
|
||||
, nH
|
||||
)
|
||||
|
||||
Round.R r ->
|
||||
Round.R stage ->
|
||||
let
|
||||
p =
|
||||
plays
|
||||
|> Maybe.withDefault (r.plays |> List.filterMap Play.asKnown)
|
||||
|> Maybe.withDefault (stage.plays |> List.filterMap Play.asKnown)
|
||||
|> Just
|
||||
in
|
||||
( makeNewRound r r.pick (Just r.likeDetail) p, model.hand )
|
||||
( makeNewRound round.stage stage.pick stage.likeDetail p, model.hand )
|
||||
|
||||
_ ->
|
||||
stage ->
|
||||
-- TODO: Error.
|
||||
( game.round, model.hand )
|
||||
( stage, model.hand )
|
||||
in
|
||||
( { model | game = { game | round = newRound }, hand = newHand }, Cmd.none )
|
||||
( { model | game = { game | round = { round | stage = newStage } }, hand = newHand }, Cmd.none )
|
||||
|
||||
Events.PlayerAway { player } ->
|
||||
let
|
||||
game =
|
||||
model.game
|
||||
|
||||
players =
|
||||
game.players |> Dict.map (setPresence player Player.Away)
|
||||
in
|
||||
@@ -830,37 +840,26 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
|
||||
Events.PlayerBack { player } ->
|
||||
let
|
||||
game =
|
||||
model.game
|
||||
|
||||
players =
|
||||
game.players |> Dict.map (setPresence player Player.Active)
|
||||
in
|
||||
( { model | game = { game | players = players } }, Cmd.none )
|
||||
|
||||
Events.Paused ->
|
||||
let
|
||||
game =
|
||||
model.game
|
||||
in
|
||||
( { model | game = { game | paused = True } }, Cmd.none )
|
||||
|
||||
Events.Continued ->
|
||||
let
|
||||
game =
|
||||
model.game
|
||||
in
|
||||
( { model | game = { game | paused = False } }, Cmd.none )
|
||||
|
||||
Events.StageTimerDone { round, stage } ->
|
||||
Events.StageTimerDone details ->
|
||||
let
|
||||
game =
|
||||
model.game
|
||||
oldRound =
|
||||
round
|
||||
in
|
||||
if (game.round |> Round.data |> .id) == round && Round.stage game.round == stage then
|
||||
if oldRound.id == details.round && Round.stage oldRound.stage == details.stage then
|
||||
let
|
||||
newRound =
|
||||
case game.round of
|
||||
newStage =
|
||||
case oldRound.stage of
|
||||
Round.P playing ->
|
||||
Round.P { playing | timedOut = True }
|
||||
|
||||
@@ -873,24 +872,17 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
Round.C complete ->
|
||||
Round.C complete
|
||||
in
|
||||
( { model | game = { game | round = newRound } }, Cmd.none )
|
||||
( { model | game = { game | round = { oldRound | stage = newStage } } }, Cmd.none )
|
||||
|
||||
else
|
||||
( model, Cmd.none )
|
||||
|
||||
Events.GameEnded { winner } ->
|
||||
let
|
||||
game =
|
||||
model.game
|
||||
in
|
||||
( { model | game = { game | winner = Just winner }, confetti = False }, Cmd.none )
|
||||
|
||||
Events.CardDiscarded { player, card, replacement } ->
|
||||
let
|
||||
game =
|
||||
model.game
|
||||
|
||||
( hand, round, discarded ) =
|
||||
( hand, stage, discarded ) =
|
||||
case replacement of
|
||||
Just replacementCard ->
|
||||
let
|
||||
@@ -901,11 +893,8 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
else
|
||||
c
|
||||
|
||||
r =
|
||||
game.round
|
||||
|
||||
newRound =
|
||||
case r of
|
||||
newStage =
|
||||
case round.stage of
|
||||
Round.P playing ->
|
||||
let
|
||||
pick =
|
||||
@@ -922,22 +911,24 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
other ->
|
||||
other
|
||||
in
|
||||
( model.hand |> List.map replace, newRound, model.discarded )
|
||||
( model.hand |> List.map replace, newStage, model.discarded )
|
||||
|
||||
Nothing ->
|
||||
( model.hand, game.round, model.discarded ++ [ ( player, card ) ] )
|
||||
( model.hand, round.stage, model.discarded ++ [ ( player, card ) ] )
|
||||
in
|
||||
( { model | hand = hand, discarded = discarded, game = { game | round = round } }, Cmd.none )
|
||||
( { model | hand = hand, discarded = discarded, game = { game | round = { round | stage = stage } } }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
|
||||
applyGameStarted : (Msg -> msg) -> Lobby -> Round.Playing -> List Card.Response -> ( Model, Cmd msg )
|
||||
applyGameStarted : (Msg -> msg) -> Lobby -> Round -> List Card.Response -> ( Model, Cmd msg )
|
||||
applyGameStarted wrap lobby round hand =
|
||||
let
|
||||
users =
|
||||
lobby.users |> Dict.toList |> List.map (\( id, _ ) -> id)
|
||||
|
||||
game =
|
||||
{ round = Round.P round
|
||||
{ round = round
|
||||
, history = []
|
||||
, playerOrder = users
|
||||
, players =
|
||||
@@ -979,6 +970,29 @@ hotJoinPlayer player user model =
|
||||
{- Private -}
|
||||
|
||||
|
||||
resolvePlayedBy :
|
||||
List Play.Known
|
||||
-> Dict Play.Id Play.Details
|
||||
-> ( Dict User.Id Play.Id, Dict Play.Id Play.WithLikes, List User.Id )
|
||||
resolvePlayedBy plays playedBy =
|
||||
let
|
||||
step known ( pb, ps, po ) =
|
||||
let
|
||||
( newPb, newPs ) =
|
||||
case playedBy |> Dict.get known.id of
|
||||
Just details ->
|
||||
( pb |> Dict.insert details.playedBy known.id
|
||||
, ps |> Dict.insert known.id (Play.WithLikes known.responses details.likes)
|
||||
)
|
||||
|
||||
Nothing ->
|
||||
( pb, ps )
|
||||
in
|
||||
( newPb, newPs, known.id :: po )
|
||||
in
|
||||
plays |> List.foldr step ( Dict.empty, Dict.empty, [] )
|
||||
|
||||
|
||||
updateLikes : Dict User.Id Play.WithLikes -> User.Id -> Player -> Player
|
||||
updateLikes plays uid player =
|
||||
plays
|
||||
@@ -1033,24 +1047,27 @@ viewWinnerListItem shared users user =
|
||||
viewRound : (Msg -> msg) -> Shared -> Lobby.Auth -> Time.Anchor -> Config -> Dict User.Id User -> Model -> List (Html msg)
|
||||
viewRound wrap shared auth timeAnchor config users model =
|
||||
let
|
||||
round =
|
||||
model.game.round
|
||||
|
||||
( call, { instruction, action, content, slotAttrs, fillCallWith, roundAttrs } ) =
|
||||
case model.completeRound of
|
||||
Just completeRound ->
|
||||
( completeRound.call, Complete.view shared True config users completeRound )
|
||||
( completeRound.call, Complete.view wrap shared auth True config users completeRound )
|
||||
|
||||
Nothing ->
|
||||
case game.round of
|
||||
Round.P round ->
|
||||
( round.call, Playing.view wrap auth shared config users model round )
|
||||
case round.stage of
|
||||
Round.P stage ->
|
||||
( round.call, Playing.view wrap auth shared config users model (Round.withStage stage round) )
|
||||
|
||||
Round.R round ->
|
||||
( round.call, Revealing.view wrap auth shared config round )
|
||||
Round.R stage ->
|
||||
( round.call, Revealing.view wrap auth shared users config (Round.withStage stage round) )
|
||||
|
||||
Round.J round ->
|
||||
( round.call, Judging.view wrap auth shared config round )
|
||||
Round.J stage ->
|
||||
( round.call, Judging.view wrap auth shared users config (Round.withStage stage round) )
|
||||
|
||||
Round.C round ->
|
||||
( round.call, Complete.view shared False config users round )
|
||||
Round.C stage ->
|
||||
( round.call, Complete.view wrap shared auth False config users (Round.withStage stage round) )
|
||||
|
||||
game =
|
||||
model.game
|
||||
@@ -1188,19 +1205,22 @@ roundTimeDetails game =
|
||||
let
|
||||
stages =
|
||||
game.rules.stages
|
||||
|
||||
round =
|
||||
game.round
|
||||
in
|
||||
case game.round of
|
||||
case round.stage of
|
||||
Round.P playing ->
|
||||
( playing.startedAt, stages.playing.duration, playing.timedOut )
|
||||
( round.startedAt, stages.playing.duration, playing.timedOut )
|
||||
|
||||
Round.R revealing ->
|
||||
( revealing.startedAt, stages.revealing |> Maybe.andThen .duration, revealing.timedOut )
|
||||
( round.startedAt, stages.revealing |> Maybe.andThen .duration, revealing.timedOut )
|
||||
|
||||
Round.J judging ->
|
||||
( judging.startedAt, stages.judging.duration, judging.timedOut )
|
||||
( round.startedAt, stages.judging.duration, judging.timedOut )
|
||||
|
||||
Round.C complete ->
|
||||
( complete.startedAt, Nothing, False )
|
||||
Round.C _ ->
|
||||
( round.startedAt, Nothing, False )
|
||||
|
||||
|
||||
minorActions : (Msg -> msg) -> Shared -> Lobby.Auth -> Game -> Bool -> Html msg
|
||||
@@ -1240,7 +1260,7 @@ discardButton : (Msg -> msg) -> Shared -> Game -> Html msg
|
||||
discardButton wrap shared game =
|
||||
let
|
||||
action =
|
||||
case game.round of
|
||||
case game.round.stage of
|
||||
Round.P p ->
|
||||
Discard |> wrap |> Maybe.justIf (p.pick.cards |> Dict.size |> (==) 1)
|
||||
|
||||
@@ -1329,11 +1349,6 @@ reveal target responses play =
|
||||
play
|
||||
|
||||
|
||||
resolvePlayedBy : Dict Play.Id Play.Details -> Play.Known -> Maybe ( User.Id, Play.WithLikes )
|
||||
resolvePlayedBy playedBy k =
|
||||
Dict.get k.id playedBy |> Maybe.map (\d -> ( d.playedBy, { play = k.responses, likes = d.likes } ))
|
||||
|
||||
|
||||
type alias ActionDetails msg =
|
||||
{ content : List (Html msg)
|
||||
, title : MdString
|
||||
|
||||
@@ -22,7 +22,7 @@ import MassiveDecks.Util.NeList as NeList
|
||||
import Material.IconButton as IconButton
|
||||
|
||||
|
||||
view : (Msg -> msg) -> Shared -> Config -> Dict User.Id User -> String -> List Round.Complete -> List (Html msg)
|
||||
view : (Msg -> msg) -> Shared -> Config -> Dict User.Id User -> String -> List (Round.Specific Round.Complete) -> List (Html msg)
|
||||
view wrap shared config users name history =
|
||||
[ Html.div [ HtmlA.id "top-content" ]
|
||||
[ Html.div [ HtmlA.id "minor-actions" ]
|
||||
@@ -49,28 +49,31 @@ view wrap shared config users name history =
|
||||
{- Private -}
|
||||
|
||||
|
||||
viewRound : Shared -> Config -> Dict User.Id User -> Round.Complete -> ( String, Html msg )
|
||||
viewRound : Shared -> Config -> Dict User.Id User -> Round.Specific Round.Complete -> ( String, Html msg )
|
||||
viewRound shared config users round =
|
||||
let
|
||||
winning =
|
||||
round.plays |> Dict.get round.winner
|
||||
round.stage.plays |> Dict.get round.stage.winner
|
||||
|
||||
winningBody =
|
||||
winning
|
||||
|> Maybe.map .play
|
||||
|> Maybe.map (List.indexedMap (\i v -> ( i, v.body )) >> Dict.fromList)
|
||||
|> Maybe.withDefault Dict.empty
|
||||
|
||||
byLine =
|
||||
{ by = round.czar, specialRole = Just Plays.Czar, likes = Nothing }
|
||||
in
|
||||
( Round.idString round.id
|
||||
, Html.li [ HtmlA.class "historic-round" ]
|
||||
[ Html.div [ HtmlA.class "spacer" ]
|
||||
[ Html.div [ HtmlA.class "historic-call with-byline" ]
|
||||
[ Plays.byLine shared users round.czar (Just ( "czar", Icon.gavel )) Nothing
|
||||
[ Plays.viewByLine shared users byLine
|
||||
, Call.viewFilled shared config Card.Front [] (always []) winningBody round.call
|
||||
]
|
||||
]
|
||||
, HtmlK.ul [ HtmlA.class "plays cards" ]
|
||||
(round.plays |> Dict.toList |> List.map (viewPlay shared config users round.winner))
|
||||
(round.stage.plays |> Dict.toList |> List.map (viewPlay shared config users round.stage.winner))
|
||||
]
|
||||
)
|
||||
|
||||
@@ -83,7 +86,7 @@ viewPlay shared config users winner ( id, { play, likes } ) =
|
||||
in
|
||||
( id
|
||||
, Html.li [ HtmlA.class "with-byline" ]
|
||||
[ Plays.byLine shared users id (( "trophy", Icon.trophy ) |> Maybe.justIf (winner == id)) likes
|
||||
[ Plays.viewByLine shared users (Plays.ByLine id (Plays.Winner |> Maybe.justIf (winner == id)) likes)
|
||||
, HtmlK.ol [ HtmlA.class "play card-set" ] cards
|
||||
]
|
||||
)
|
||||
|
||||
@@ -30,7 +30,7 @@ type alias Model =
|
||||
, hand : List Card.Response
|
||||
, filledCards : Dict Card.Id String
|
||||
, playStyles : PlayStyles
|
||||
, completeRound : Maybe Round.Complete
|
||||
, completeRound : Maybe (Round.Specific Round.Complete)
|
||||
, viewingHistory : Bool
|
||||
, time : Maybe Time
|
||||
, helpVisible : Bool
|
||||
@@ -62,7 +62,7 @@ emptyModel game =
|
||||
-}
|
||||
type alias Game =
|
||||
{ round : Round
|
||||
, history : List Round.Complete
|
||||
, history : List (Round.Specific Round.Complete)
|
||||
, playerOrder : List User.Id
|
||||
, players : Dict User.Id Player
|
||||
, rules : Rules
|
||||
|
||||
@@ -72,7 +72,7 @@ type PlayState
|
||||
| Played
|
||||
|
||||
|
||||
role : Round -> User.Id -> Role
|
||||
role : Round.Specific stageDetails -> User.Id -> Role
|
||||
role round id =
|
||||
if isCzar round id then
|
||||
RCzar
|
||||
@@ -81,15 +81,15 @@ role round id =
|
||||
RPlayer
|
||||
|
||||
|
||||
isCzar : Round -> User.Id -> Bool
|
||||
isCzar : Round.Specific stageDetails -> User.Id -> Bool
|
||||
isCzar round id =
|
||||
(round |> Round.data |> .czar) == id
|
||||
(round |> .czar) == id
|
||||
|
||||
|
||||
playState : Round.Playing -> User.Id -> PlayState
|
||||
playState : Round.Specific Round.Playing -> User.Id -> PlayState
|
||||
playState round id =
|
||||
if Set.member id round.players then
|
||||
if Set.member id round.played then
|
||||
if Set.member id round.stage.played then
|
||||
Played
|
||||
|
||||
else
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
module MassiveDecks.Game.Round exposing
|
||||
( Complete
|
||||
, Data
|
||||
, Id
|
||||
, Judging
|
||||
, LikeDetail
|
||||
@@ -8,19 +7,18 @@ module MassiveDecks.Game.Round exposing
|
||||
, PickState(..)
|
||||
, Playing
|
||||
, Revealing
|
||||
, Round(..)
|
||||
, Round
|
||||
, Specific
|
||||
, Stage(..)
|
||||
, complete
|
||||
, data
|
||||
, StageDetails(..)
|
||||
, defaultLikeDetail
|
||||
, idDecoder
|
||||
, idString
|
||||
, judging
|
||||
, noPick
|
||||
, playing
|
||||
, revealing
|
||||
, stage
|
||||
, stageDescription
|
||||
, stageToName
|
||||
, stageToString
|
||||
, withStage
|
||||
)
|
||||
|
||||
import Dict exposing (Dict)
|
||||
@@ -60,9 +58,9 @@ type Stage
|
||||
|
||||
{-| Get the stage of the given round.
|
||||
-}
|
||||
stage : Round -> Stage
|
||||
stage round =
|
||||
case round of
|
||||
stage : StageDetails -> Stage
|
||||
stage specific =
|
||||
case specific of
|
||||
P _ ->
|
||||
SPlaying
|
||||
|
||||
@@ -78,8 +76,8 @@ stage round =
|
||||
|
||||
{-| Get the serializing name for the stage.
|
||||
-}
|
||||
stageToName : Stage -> String
|
||||
stageToName s =
|
||||
stageToString : Stage -> String
|
||||
stageToString s =
|
||||
case s of
|
||||
SPlaying ->
|
||||
"Playing"
|
||||
@@ -114,32 +112,38 @@ stageDescription toDescribe =
|
||||
|
||||
{-| A round during a game.
|
||||
-}
|
||||
type Round
|
||||
type alias Round =
|
||||
Specific StageDetails
|
||||
|
||||
|
||||
type alias Specific stageDetails =
|
||||
{ id : Id
|
||||
, czar : User.Id
|
||||
, players : Set User.Id
|
||||
, call : Card.Call
|
||||
, startedAt : Time
|
||||
, stage : stageDetails
|
||||
}
|
||||
|
||||
|
||||
withStage : stageDetails -> Specific oldDetails -> Specific stageDetails
|
||||
withStage newStage round =
|
||||
{ id = round.id
|
||||
, czar = round.czar
|
||||
, players = round.players
|
||||
, call = round.call
|
||||
, startedAt = round.startedAt
|
||||
, stage = newStage
|
||||
}
|
||||
|
||||
|
||||
type StageDetails
|
||||
= P Playing
|
||||
| R Revealing
|
||||
| J Judging
|
||||
| C Complete
|
||||
|
||||
|
||||
{-| A round while users are playing a round.
|
||||
-}
|
||||
type alias Playing =
|
||||
Data { played : Set User.Id, pick : Pick, timedOut : Bool }
|
||||
|
||||
|
||||
playing : Id -> User.Id -> Set User.Id -> Card.Call -> Set User.Id -> Time -> Bool -> Playing
|
||||
playing id czar players call played startedAt timedOut =
|
||||
{ id = id
|
||||
, czar = czar
|
||||
, players = players
|
||||
, call = call
|
||||
, played = played
|
||||
, pick = { state = Selected, cards = Dict.empty }
|
||||
, startedAt = startedAt
|
||||
, timedOut = timedOut
|
||||
}
|
||||
|
||||
|
||||
type alias LikeDetail =
|
||||
{ played : Maybe Play.Id
|
||||
, liked : Set Play.Id
|
||||
@@ -153,97 +157,6 @@ defaultLikeDetail =
|
||||
}
|
||||
|
||||
|
||||
type alias Revealing =
|
||||
Data
|
||||
{ plays : List Play
|
||||
, lastRevealed : Maybe Play.Id
|
||||
, pick : Maybe Play.Id
|
||||
, likeDetail : LikeDetail
|
||||
, timedOut : Bool
|
||||
}
|
||||
|
||||
|
||||
revealing : Maybe LikeDetail -> Id -> User.Id -> Set User.Id -> Card.Call -> List Play -> Time -> Bool -> Revealing
|
||||
revealing likeDetail id czar players call plays startedAt timedOut =
|
||||
{ id = id
|
||||
, czar = czar
|
||||
, players = players
|
||||
, call = call
|
||||
, plays = plays
|
||||
, lastRevealed = Nothing
|
||||
, startedAt = startedAt
|
||||
, pick = Nothing
|
||||
, likeDetail = likeDetail |> Maybe.withDefault defaultLikeDetail
|
||||
, timedOut = timedOut
|
||||
}
|
||||
|
||||
|
||||
{-| A round while the czar is judging a round.
|
||||
-}
|
||||
type alias Judging =
|
||||
Data
|
||||
{ plays : List Play.Known
|
||||
, pick : Maybe Play.Id
|
||||
, likeDetail : LikeDetail
|
||||
, timedOut : Bool
|
||||
}
|
||||
|
||||
|
||||
judging :
|
||||
Maybe Play.Id
|
||||
-> Maybe LikeDetail
|
||||
-> Id
|
||||
-> User.Id
|
||||
-> Set User.Id
|
||||
-> Card.Call
|
||||
-> List Play.Known
|
||||
-> Time
|
||||
-> Bool
|
||||
-> Judging
|
||||
judging pick likeDetail id czar players call plays startedAt timedOut =
|
||||
{ id = id
|
||||
, czar = czar
|
||||
, players = players
|
||||
, call = call
|
||||
, plays = plays
|
||||
, pick = pick
|
||||
, likeDetail = likeDetail |> Maybe.withDefault defaultLikeDetail
|
||||
, startedAt = startedAt
|
||||
, timedOut = timedOut
|
||||
}
|
||||
|
||||
|
||||
{-| A round that has been finished.
|
||||
-}
|
||||
type alias Complete =
|
||||
Data { plays : Dict User.Id Play.WithLikes, playOrder : List User.Id, winner : User.Id }
|
||||
|
||||
|
||||
complete : Id -> User.Id -> Set User.Id -> Card.Call -> Dict User.Id Play.WithLikes -> List User.Id -> User.Id -> Time -> Complete
|
||||
complete id czar players call plays playOrder winner startedAt =
|
||||
{ id = id
|
||||
, czar = czar
|
||||
, players = players
|
||||
, call = call
|
||||
, plays = plays
|
||||
, playOrder = playOrder
|
||||
, winner = winner
|
||||
, startedAt = startedAt
|
||||
}
|
||||
|
||||
|
||||
{-| Data common to all rounds.
|
||||
-}
|
||||
type alias Data specific =
|
||||
{ specific
|
||||
| id : Id
|
||||
, czar : User.Id
|
||||
, players : Set User.Id
|
||||
, call : Card.Call
|
||||
, startedAt : Time
|
||||
}
|
||||
|
||||
|
||||
{-| The user's pick for the round.
|
||||
-}
|
||||
type alias Pick =
|
||||
@@ -262,22 +175,41 @@ type PickState
|
||||
| Submitted
|
||||
|
||||
|
||||
data : Round -> Data {}
|
||||
data round =
|
||||
case round of
|
||||
P rd ->
|
||||
extract rd
|
||||
|
||||
J rd ->
|
||||
extract rd
|
||||
|
||||
C rd ->
|
||||
extract rd
|
||||
|
||||
R rd ->
|
||||
extract rd
|
||||
{-| A round while users are playing a round.
|
||||
-}
|
||||
type alias Playing =
|
||||
{ pick : Pick
|
||||
, played : Set User.Id
|
||||
, timedOut : Bool
|
||||
}
|
||||
|
||||
|
||||
extract : Data a -> Data {}
|
||||
extract rd =
|
||||
{ id = rd.id, call = rd.call, czar = rd.czar, players = rd.players, startedAt = rd.startedAt }
|
||||
type alias Revealing =
|
||||
{ likeDetail : LikeDetail
|
||||
, lastRevealed : Maybe Play.Id
|
||||
, pick : Maybe Play.Id
|
||||
, plays : List Play
|
||||
, timedOut : Bool
|
||||
}
|
||||
|
||||
|
||||
{-| A round while the czar is judging a round.
|
||||
-}
|
||||
type alias Judging =
|
||||
{ likeDetail : LikeDetail
|
||||
, pick : Maybe Play.Id
|
||||
, plays : List Play.Known
|
||||
, timedOut : Bool
|
||||
}
|
||||
|
||||
|
||||
{-| A round that has been finished.
|
||||
-}
|
||||
type alias Complete =
|
||||
{ likeDetail : LikeDetail
|
||||
, pick : Maybe Play.Id
|
||||
, playedBy : Dict User.Id Play.Id
|
||||
, plays : Dict Play.Id Play.WithLikes
|
||||
, playOrder : List User.Id
|
||||
, winner : User.Id
|
||||
}
|
||||
|
||||
@@ -10,47 +10,95 @@ import MassiveDecks.Card.Parts as Parts
|
||||
import MassiveDecks.Card.Play as Play
|
||||
import MassiveDecks.Card.Response as Response
|
||||
import MassiveDecks.Game.Action.Model as Action
|
||||
import MassiveDecks.Game.Messages exposing (..)
|
||||
import MassiveDecks.Game.Model exposing (..)
|
||||
import MassiveDecks.Game.Round as Round
|
||||
import MassiveDecks.Game.Player as Player
|
||||
import MassiveDecks.Game.Round as Round exposing (Round)
|
||||
import MassiveDecks.Game.Round.Plays as Plays
|
||||
import MassiveDecks.Model exposing (..)
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model exposing (Config)
|
||||
import MassiveDecks.Pages.Lobby.Model exposing (Auth)
|
||||
import MassiveDecks.Strings as Strings
|
||||
import MassiveDecks.User as User exposing (User)
|
||||
import MassiveDecks.Util.Maybe as Maybe
|
||||
import Set
|
||||
|
||||
|
||||
view : Shared -> Bool -> Config -> Dict User.Id User -> Round.Complete -> RoundView msg
|
||||
view shared nextRoundReady config users round =
|
||||
view : (Msg -> msg) -> Shared -> Auth -> Bool -> Config -> Dict User.Id User -> Round.Specific Round.Complete -> RoundView msg
|
||||
view _ shared auth nextRoundReady config users round =
|
||||
let
|
||||
role =
|
||||
Player.role round auth.claims.uid
|
||||
|
||||
stage =
|
||||
round.stage
|
||||
|
||||
winning =
|
||||
round.plays |> Dict.get round.winner
|
||||
stage.plays |> Dict.get stage.winner
|
||||
|
||||
likeAction =
|
||||
let
|
||||
canBeLiked play =
|
||||
Just play /= stage.likeDetail.played && not (Set.member play stage.likeDetail.liked)
|
||||
|
||||
likeIfCanBeLiked pick =
|
||||
Maybe.justIf (canBeLiked pick) Action.Like
|
||||
in
|
||||
case role of
|
||||
Player.RCzar ->
|
||||
Nothing
|
||||
|
||||
Player.RPlayer ->
|
||||
stage.pick |> Maybe.andThen likeIfCanBeLiked
|
||||
|
||||
msg _ =
|
||||
Nothing
|
||||
|
||||
userAndPlay userId =
|
||||
let
|
||||
tuple : Play.Id -> Play.WithLikes -> ( User.Id, Play.Id, Play.WithLikes )
|
||||
tuple id p =
|
||||
( userId, id, p )
|
||||
|
||||
play id =
|
||||
Dict.get id stage.plays
|
||||
|
||||
playId =
|
||||
Dict.get userId stage.playedBy
|
||||
in
|
||||
Maybe.map2 tuple playId (playId |> Maybe.andThen play)
|
||||
in
|
||||
{ instruction = Strings.AdvanceRoundInstruction |> Maybe.justIf nextRoundReady
|
||||
, action = Action.Advance |> Maybe.justIf nextRoundReady
|
||||
, action =
|
||||
Maybe.first
|
||||
[ Action.Advance |> Maybe.justIf nextRoundReady
|
||||
, likeAction
|
||||
]
|
||||
, content =
|
||||
HtmlK.ul [ HtmlA.class "complete plays cards" ]
|
||||
(round.playOrder
|
||||
|> List.map (\u -> ( u, Dict.get u round.plays ))
|
||||
|> List.map (viewPlay shared config users round.winner)
|
||||
)
|
||||
stage.playOrder
|
||||
|> List.filterMap userAndPlay
|
||||
|> List.map (details shared config stage.winner msg)
|
||||
|> Plays.view shared users [ ( "complete", True ) ] stage.likeDetail.liked stage.pick
|
||||
, slotAttrs = always []
|
||||
, fillCallWith = winning |> Maybe.map (.play >> Parts.fillsFromPlay) |> Maybe.withDefault Dict.empty
|
||||
, roundAttrs = []
|
||||
}
|
||||
|
||||
|
||||
viewPlay : Shared -> Config -> Dict User.Id User -> User.Id -> ( User.Id, Maybe Play.WithLikes ) -> ( String, Html msg )
|
||||
viewPlay shared config users winner ( id, play ) =
|
||||
details : Shared -> Config -> User.Id -> (Play.Id -> Maybe msg) -> ( User.Id, Play.Id, Play.WithLikes ) -> Plays.Details msg
|
||||
details shared config winner maybeMsg ( player, id, play ) =
|
||||
let
|
||||
cards =
|
||||
play
|
||||
|> Maybe.map (.play >> List.map (\r -> ( r.details.id, Html.li [] [ Response.view shared config Card.Front [] r ] )))
|
||||
|> Maybe.withDefault []
|
||||
play.play |> List.map (\r -> Html.li [] [ Response.view shared config Card.Front [] r ])
|
||||
|
||||
specialRole =
|
||||
if winner == player then
|
||||
Just Plays.Winner
|
||||
|
||||
else
|
||||
Nothing
|
||||
|
||||
byLine =
|
||||
Plays.ByLine player specialRole play.likes
|
||||
in
|
||||
( id
|
||||
, Html.li [ HtmlA.class "with-byline" ]
|
||||
[ Plays.byLine shared users id (( "trophy", Icon.trophy ) |> Maybe.justIf (winner == id)) (play |> Maybe.andThen .likes)
|
||||
, HtmlK.ol [ HtmlA.class "play card-set" ] cards
|
||||
]
|
||||
)
|
||||
Plays.Details id cards (id |> maybeMsg) (Just byLine)
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
module MassiveDecks.Game.Round.Judging exposing (view)
|
||||
|
||||
import Dict
|
||||
import Html.Attributes as HtmlA
|
||||
import Dict exposing (Dict)
|
||||
import MassiveDecks.Card.Model as Card
|
||||
import MassiveDecks.Card.Parts as Parts
|
||||
import MassiveDecks.Card.Play as Play
|
||||
import MassiveDecks.Card.Response as Response
|
||||
import MassiveDecks.Game.Action.Model as Action
|
||||
import MassiveDecks.Game.Messages exposing (..)
|
||||
import MassiveDecks.Game.Model exposing (..)
|
||||
import MassiveDecks.Game.Messages exposing (Msg(..))
|
||||
import MassiveDecks.Game.Model exposing (RoundView)
|
||||
import MassiveDecks.Game.Player as Player
|
||||
import MassiveDecks.Game.Round as Round
|
||||
import MassiveDecks.Game.Round.Plays as Plays
|
||||
@@ -16,20 +15,24 @@ import MassiveDecks.Model exposing (Shared)
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model exposing (Config)
|
||||
import MassiveDecks.Pages.Lobby.Model exposing (Auth)
|
||||
import MassiveDecks.Strings as Strings
|
||||
import MassiveDecks.User as User exposing (User)
|
||||
import MassiveDecks.Util.Maybe as Maybe
|
||||
import Set exposing (Set)
|
||||
|
||||
|
||||
view : (Msg -> msg) -> Auth -> Shared -> Config -> Round.Judging -> RoundView msg
|
||||
view wrap auth shared config round =
|
||||
view : (Msg -> msg) -> Auth -> Shared -> Dict User.Id User -> Config -> Round.Specific Round.Judging -> RoundView msg
|
||||
view wrap auth shared users config round =
|
||||
let
|
||||
role =
|
||||
Player.role (Round.J round) auth.claims.uid
|
||||
Player.role round auth.claims.uid
|
||||
|
||||
stage =
|
||||
round.stage
|
||||
|
||||
{ action, msg, instruction, isCzar } =
|
||||
case role of
|
||||
Player.RCzar ->
|
||||
{ action = Maybe.map (always Action.Judge) round.pick
|
||||
{ action = Maybe.map (always Action.Judge) stage.pick
|
||||
, msg = \p -> p |> PickPlay |> wrap |> Just
|
||||
, instruction = Strings.RevealPlaysInstruction
|
||||
, isCzar = True
|
||||
@@ -38,27 +41,27 @@ view wrap auth shared config round =
|
||||
Player.RPlayer ->
|
||||
let
|
||||
canBeLiked play =
|
||||
Just play /= round.likeDetail.played && not (Set.member play round.likeDetail.liked)
|
||||
Just play /= stage.likeDetail.played && not (Set.member play stage.likeDetail.liked)
|
||||
in
|
||||
{ action = Maybe.andThen (\p -> Maybe.justIf (canBeLiked p) Action.Like) round.pick
|
||||
{ action = Maybe.andThen (\p -> Maybe.justIf (canBeLiked p) Action.Like) stage.pick
|
||||
, msg = \p -> p |> PickPlay |> wrap |> Maybe.justIf (canBeLiked p)
|
||||
, instruction = Strings.WaitingForCzarInstruction
|
||||
, isCzar = False
|
||||
}
|
||||
|
||||
picked =
|
||||
round.plays
|
||||
|> List.filter (\play -> Just play.id == round.pick)
|
||||
stage.plays
|
||||
|> List.filter (\play -> Just play.id == stage.pick)
|
||||
|> List.head
|
||||
|> Maybe.map (.responses >> Parts.fillsFromPlay)
|
||||
|> Maybe.withDefault Dict.empty
|
||||
|
||||
details =
|
||||
round.plays |> List.map (playDetails shared config round.likeDetail.liked msg)
|
||||
stage.plays |> List.map (playDetails shared config msg)
|
||||
in
|
||||
{ instruction = Just instruction
|
||||
, action = action
|
||||
, content = details |> Plays.view [ ( "judging", True ), ( "is-czar", isCzar ) ] round.pick
|
||||
, content = details |> Plays.view shared users [ ( "judging", True ), ( "is-czar", isCzar ) ] stage.likeDetail.liked stage.pick
|
||||
, slotAttrs = always []
|
||||
, fillCallWith = picked
|
||||
, roundAttrs = []
|
||||
@@ -69,24 +72,14 @@ view wrap auth shared config round =
|
||||
{- Private -}
|
||||
|
||||
|
||||
playDetails : Shared -> Config -> Set Play.Id -> (Play.Id -> Maybe msg) -> Play.Known -> Plays.Details msg
|
||||
playDetails shared config liked msg { id, responses } =
|
||||
playDetails : Shared -> Config -> (Play.Id -> Maybe msg) -> Play.Known -> Plays.Details msg
|
||||
playDetails shared config msg { id, responses } =
|
||||
let
|
||||
maybeMsg =
|
||||
msg id
|
||||
|
||||
cls =
|
||||
if maybeMsg /= Nothing then
|
||||
[ HtmlA.class "active" ]
|
||||
|
||||
else
|
||||
[]
|
||||
|
||||
cards =
|
||||
responses
|
||||
|> List.map (\r -> Response.view shared config Card.Front [] r)
|
||||
|
||||
attrs =
|
||||
[ HtmlA.class "liked" ] |> Maybe.justIf (Set.member id liked) |> Maybe.withDefault []
|
||||
in
|
||||
Plays.Details id cards maybeMsg (attrs ++ cls)
|
||||
Plays.Details id cards maybeMsg Nothing
|
||||
|
||||
@@ -19,47 +19,52 @@ import MassiveDecks.Game.Action.Model as Action
|
||||
import MassiveDecks.Game.Messages as Game exposing (Msg)
|
||||
import MassiveDecks.Game.Model exposing (..)
|
||||
import MassiveDecks.Game.Player as Player
|
||||
import MassiveDecks.Game.Round as Round
|
||||
import MassiveDecks.Game.Round as Round exposing (Round)
|
||||
import MassiveDecks.Model exposing (Shared)
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model exposing (Config)
|
||||
import MassiveDecks.Pages.Lobby.Model as Lobby
|
||||
import MassiveDecks.Strings as Strings
|
||||
import MassiveDecks.User as User exposing (User)
|
||||
import MassiveDecks.Util.Html as Html
|
||||
import MassiveDecks.Util.Html.Attributes as HtmlA
|
||||
import MassiveDecks.Util.Random as Random
|
||||
import Random
|
||||
import Set exposing (Set)
|
||||
|
||||
|
||||
init : (Msg -> msg) -> Round.Playing -> Round.Pick -> ( Round.Playing, Cmd msg )
|
||||
init : (Msg -> msg) -> Round.Specific Round.Playing -> Round.Pick -> ( Round, Cmd msg )
|
||||
init wrap round pick =
|
||||
let
|
||||
cmd =
|
||||
round.players
|
||||
|> playStylesGenerator (Call.slotCount round.call)
|
||||
|> Random.generate (Game.SetPlayStyles >> wrap)
|
||||
|
||||
stage =
|
||||
round.stage
|
||||
in
|
||||
( { round | pick = pick }
|
||||
( round |> Round.withStage (Round.P { stage | pick = pick })
|
||||
, cmd
|
||||
)
|
||||
|
||||
|
||||
view : (Msg -> msg) -> Lobby.Auth -> Shared -> Config -> Dict User.Id User -> Model -> Round.Playing -> RoundView msg
|
||||
view : (Msg -> msg) -> Lobby.Auth -> Shared -> Config -> Dict User.Id User -> Model -> Round.Specific Round.Playing -> RoundView msg
|
||||
view wrap auth shared config _ model round =
|
||||
let
|
||||
slots =
|
||||
Call.slotCount round.call
|
||||
|
||||
stage =
|
||||
round.stage
|
||||
|
||||
missingFromPick =
|
||||
slots - (round.pick.cards |> Dict.size)
|
||||
slots - (stage.pick.cards |> Dict.size)
|
||||
|
||||
self =
|
||||
auth.claims.uid
|
||||
|
||||
( action, instruction, notPlaying ) =
|
||||
if round.players |> Set.member self then
|
||||
case round.pick.state of
|
||||
case stage.pick.state of
|
||||
Round.Selected ->
|
||||
if missingFromPick > 0 then
|
||||
( Nothing, Strings.PlayInstruction { numberOfCards = missingFromPick }, False )
|
||||
@@ -70,7 +75,7 @@ view wrap auth shared config _ model round =
|
||||
Round.Submitted ->
|
||||
( Just Action.TakeBack, Strings.WaitingForPlaysInstruction, True )
|
||||
|
||||
else if Player.isCzar (Round.P round) self then
|
||||
else if Player.isCzar round self then
|
||||
( Nothing, Strings.CzarsDontPlayInstruction, True )
|
||||
|
||||
else
|
||||
@@ -86,11 +91,11 @@ view wrap auth shared config _ model round =
|
||||
, ( "pick-full", missingFromPick < 1 )
|
||||
]
|
||||
]
|
||||
(model.hand |> List.map (round.pick.cards |> viewHandCard wrap shared config model.filledCards))
|
||||
(model.hand |> List.map (stage.pick.cards |> viewHandCard wrap shared config model.filledCards))
|
||||
|
||||
backgroundPlays =
|
||||
Html.div [ HtmlA.class "background-plays" ]
|
||||
(round.players |> Set.toList |> List.map (viewBackgroundPlay shared model.playStyles slots round.played))
|
||||
(round.players |> Set.toList |> List.map (viewBackgroundPlay shared model.playStyles slots stage.played))
|
||||
|
||||
fillCustom _ p =
|
||||
List.find (\c -> c.details.id == p) model.hand
|
||||
@@ -98,7 +103,7 @@ view wrap auth shared config _ model round =
|
||||
|> Maybe.withDefault ""
|
||||
|
||||
picked =
|
||||
round.pick.cards |> Dict.map fillCustom
|
||||
stage.pick.cards |> Dict.map fillCustom
|
||||
|
||||
showNonObviousSlotIndices =
|
||||
if round.call.body |> Parts.nonObviousSlotIndices then
|
||||
@@ -111,7 +116,7 @@ view wrap auth shared config _ model round =
|
||||
List.concat
|
||||
[ [ Game.Unpick i |> wrap |> HtmlE.onClick ]
|
||||
, DragDrop.droppable (Game.Drag >> wrap) i
|
||||
, round.pick.cards
|
||||
, stage.pick.cards
|
||||
|> Dict.get i
|
||||
|> Maybe.map (DragDrop.draggable (Game.Drag >> wrap))
|
||||
|> Maybe.withDefault []
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
module MassiveDecks.Game.Round.Plays exposing (Details, byLine, view)
|
||||
module MassiveDecks.Game.Round.Plays exposing
|
||||
( ByLine
|
||||
, Details
|
||||
, SpecialRole(..)
|
||||
, view
|
||||
, viewByLine
|
||||
)
|
||||
|
||||
import Dict exposing (Dict)
|
||||
import FontAwesome.Icon as Icon exposing (Icon)
|
||||
import FontAwesome.Solid as Icon
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as HtmlA
|
||||
import Html.Events as HtmlE
|
||||
@@ -13,6 +20,7 @@ import MassiveDecks.Strings.Languages as Lang
|
||||
import MassiveDecks.User as User exposing (User)
|
||||
import MassiveDecks.Util.Html as Html
|
||||
import MassiveDecks.Util.Html.Attributes as HtmlA
|
||||
import Set exposing (Set)
|
||||
|
||||
|
||||
{-| The details needed to render a specific play.
|
||||
@@ -21,50 +29,83 @@ type alias Details msg =
|
||||
{ id : Play.Id
|
||||
, responses : List (Html msg)
|
||||
, action : Maybe msg
|
||||
, attrs : List (Html.Attribute msg)
|
||||
, byLine : Maybe ByLine
|
||||
}
|
||||
|
||||
|
||||
{-| The details needed for a by line.
|
||||
-}
|
||||
type alias ByLine =
|
||||
{ by : User.Id
|
||||
, specialRole : Maybe SpecialRole
|
||||
, likes : Maybe Int
|
||||
}
|
||||
|
||||
|
||||
{-| If the user had a special role in the game, then this defines it.
|
||||
-}
|
||||
type SpecialRole
|
||||
= Winner
|
||||
| Czar
|
||||
|
||||
|
||||
{-| View a collection of plays.
|
||||
-}
|
||||
view : List ( String, Bool ) -> Maybe Play.Id -> List (Details msg) -> Html msg
|
||||
view classes picked details =
|
||||
view : Shared -> Dict User.Id User -> List ( String, Bool ) -> Set Play.Id -> Maybe Play.Id -> List (Details msg) -> Html msg
|
||||
view shared users classes liked picked details =
|
||||
HtmlK.ul [ HtmlA.classList (classes ++ [ ( "cards", True ), ( "plays", True ) ]) ]
|
||||
(details |> List.map (viewPlay picked))
|
||||
(details |> List.map (viewPlay shared users liked picked))
|
||||
|
||||
|
||||
{-| Create a byline.
|
||||
-}
|
||||
byLine : Shared -> Dict User.Id User -> User.Id -> Maybe ( String, Icon ) -> Maybe Int -> Html msg
|
||||
byLine shared users id icon likes =
|
||||
viewByLine : Shared -> Dict User.Id User -> ByLine -> Html msg
|
||||
viewByLine shared users { by, specialRole, likes } =
|
||||
let
|
||||
roleIcon role =
|
||||
let
|
||||
( cls, icon ) =
|
||||
case role of
|
||||
Winner ->
|
||||
( "trophy", Icon.trophy )
|
||||
|
||||
Czar ->
|
||||
( "czar", Icon.gavel )
|
||||
in
|
||||
Html.span [ HtmlA.class cls ] [ Icon.viewIcon icon ]
|
||||
|
||||
name =
|
||||
users |> Dict.get id |> Maybe.map .name |> Maybe.withDefault (Strings.UnknownUser |> Lang.string shared)
|
||||
users |> Dict.get by |> Maybe.map .name |> Maybe.withDefault (Strings.UnknownUser |> Lang.string shared)
|
||||
|
||||
contents =
|
||||
[ specialRole |> Maybe.map roleIcon
|
||||
, Html.span [ HtmlA.class "name" ] [ Html.text name ] |> Just
|
||||
, likes
|
||||
|> Maybe.map (\l -> Html.span [ HtmlA.class "likes" ] [ Strings.Likes { total = l } |> Lang.html shared ])
|
||||
]
|
||||
in
|
||||
Html.span [ HtmlA.class "byline", HtmlA.title name ]
|
||||
[ icon
|
||||
|> Maybe.map (\( cls, i ) -> Html.span [ HtmlA.class cls ] [ Icon.viewIcon i ])
|
||||
|> Maybe.withDefault Html.nothing
|
||||
, Html.span [ HtmlA.class "name" ] [ Html.text name ]
|
||||
, likes
|
||||
|> Maybe.map (\l -> Html.span [ HtmlA.class "likes" ] [ Strings.Likes { total = l } |> Lang.html shared ])
|
||||
|> Maybe.withDefault Html.nothing
|
||||
]
|
||||
Html.span [ HtmlA.class "byline", HtmlA.title name ] (contents |> List.filterMap identity)
|
||||
|
||||
|
||||
|
||||
{- Private -}
|
||||
|
||||
|
||||
viewPlay : Maybe Play.Id -> Details msg -> ( String, Html msg )
|
||||
viewPlay picked { id, responses, action, attrs } =
|
||||
viewPlay : Shared -> Dict User.Id User -> Set Play.Id -> Maybe Play.Id -> Details msg -> ( String, Html msg )
|
||||
viewPlay shared users liked picked { id, responses, action, byLine } =
|
||||
( id
|
||||
, Html.li []
|
||||
[ Html.ol
|
||||
(HtmlA.classList [ ( "play", True ), ( "card-set", True ), ( "picked", picked == Just id ) ]
|
||||
:: (action |> Maybe.map HtmlE.onClick |> Maybe.withDefault HtmlA.nothing)
|
||||
:: attrs
|
||||
)
|
||||
, Html.li [ HtmlA.classList [ ( "with-byline", byLine /= Nothing ) ] ]
|
||||
[ byLine |> Maybe.map (viewByLine shared users) |> Maybe.withDefault Html.nothing
|
||||
, Html.ol
|
||||
[ HtmlA.classList
|
||||
[ ( "play", True )
|
||||
, ( "card-set", True )
|
||||
, ( "active", action /= Nothing )
|
||||
, ( "picked", picked == Just id )
|
||||
, ( "liked", Set.member id liked )
|
||||
]
|
||||
, action |> Maybe.map HtmlE.onClick |> Maybe.withDefault HtmlA.nothing
|
||||
]
|
||||
(responses |> List.map (\card -> Html.li [] [ card ]))
|
||||
]
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module MassiveDecks.Game.Round.Revealing exposing (view)
|
||||
|
||||
import Dict
|
||||
import Dict exposing (Dict)
|
||||
import Html.Attributes as HtmlA
|
||||
import MassiveDecks.Card.Call as Call
|
||||
import MassiveDecks.Card.Model as Card
|
||||
@@ -17,15 +17,19 @@ import MassiveDecks.Model exposing (Shared)
|
||||
import MassiveDecks.Pages.Lobby.Configure.Model exposing (Config)
|
||||
import MassiveDecks.Pages.Lobby.Model exposing (Auth)
|
||||
import MassiveDecks.Strings as Strings
|
||||
import MassiveDecks.User as User exposing (User)
|
||||
import MassiveDecks.Util.Maybe as Maybe
|
||||
import Set exposing (Set)
|
||||
|
||||
|
||||
view : (Msg -> msg) -> Auth -> Shared -> Config -> Round.Revealing -> RoundView msg
|
||||
view wrap auth shared config round =
|
||||
view : (Msg -> msg) -> Auth -> Shared -> Dict User.Id User -> Config -> Round.Specific Round.Revealing -> RoundView msg
|
||||
view wrap auth shared users config round =
|
||||
let
|
||||
role =
|
||||
Player.role (Round.R round) auth.claims.uid
|
||||
Player.role round auth.claims.uid
|
||||
|
||||
stage =
|
||||
round.stage
|
||||
|
||||
{ msg, action, instruction, isCzar } =
|
||||
case role of
|
||||
@@ -43,13 +47,13 @@ view wrap auth shared config round =
|
||||
Player.RPlayer ->
|
||||
let
|
||||
canBeLiked id =
|
||||
(Just id /= round.likeDetail.played) && not (Set.member id round.likeDetail.liked)
|
||||
(Just id /= stage.likeDetail.played) && not (Set.member id stage.likeDetail.liked)
|
||||
|
||||
msgPick { id, responses } =
|
||||
id |> PickPlay |> wrap |> Maybe.justIf (canBeLiked id && (responses /= Nothing))
|
||||
in
|
||||
{ msg = msgPick
|
||||
, action = Maybe.andThen (\p -> Action.Like |> Maybe.justIf (canBeLiked p)) round.pick
|
||||
, action = Maybe.andThen (\p -> Action.Like |> Maybe.justIf (canBeLiked p)) stage.pick
|
||||
, instruction = Strings.WaitingForCzarInstruction
|
||||
, isCzar = False
|
||||
}
|
||||
@@ -58,10 +62,10 @@ view wrap auth shared config round =
|
||||
Call.slotCount round.call
|
||||
|
||||
plays =
|
||||
round.plays |> List.map (playDetails shared config round.likeDetail.liked slots msg)
|
||||
stage.plays |> List.map (playDetails shared config slots msg)
|
||||
|
||||
lastRevealed =
|
||||
case round.plays |> List.filter (\p -> Just p.id == round.lastRevealed) of
|
||||
case stage.plays |> List.filter (\p -> Just p.id == stage.lastRevealed) of
|
||||
play :: [] ->
|
||||
play.responses |> Maybe.map Parts.fillsFromPlay
|
||||
|
||||
@@ -70,7 +74,7 @@ view wrap auth shared config round =
|
||||
in
|
||||
{ instruction = Just instruction
|
||||
, action = action
|
||||
, content = plays |> Plays.view [ ( "revealing", True ), ( "is-czar", isCzar ) ] round.pick
|
||||
, content = plays |> Plays.view shared users [ ( "revealing", True ), ( "is-czar", isCzar ) ] stage.likeDetail.liked stage.pick
|
||||
, slotAttrs = always []
|
||||
, fillCallWith = lastRevealed |> Maybe.withDefault Dict.empty
|
||||
, roundAttrs = []
|
||||
@@ -81,8 +85,8 @@ view wrap auth shared config round =
|
||||
{- Private -}
|
||||
|
||||
|
||||
playDetails : Shared -> Config -> Set Play.Id -> Int -> (Play -> Maybe msg) -> Play -> Plays.Details msg
|
||||
playDetails shared config liked slots msg play =
|
||||
playDetails : Shared -> Config -> Int -> (Play -> Maybe msg) -> Play -> Plays.Details msg
|
||||
playDetails shared config slots msg play =
|
||||
let
|
||||
{ id, responses } =
|
||||
play
|
||||
@@ -90,19 +94,9 @@ playDetails shared config liked slots msg play =
|
||||
maybeMsg =
|
||||
msg play
|
||||
|
||||
cls =
|
||||
if maybeMsg /= Nothing then
|
||||
[ HtmlA.class "active" ]
|
||||
|
||||
else
|
||||
[]
|
||||
|
||||
cards =
|
||||
responses
|
||||
|> Maybe.map (List.map (\r -> Response.view shared config Card.Front [] r))
|
||||
|> Maybe.withDefault (List.repeat slots (Response.viewUnknown shared []))
|
||||
|
||||
attrs =
|
||||
[ HtmlA.class "liked" ] |> Maybe.justIf (Set.member id liked) |> Maybe.withDefault []
|
||||
in
|
||||
Plays.Details id cards maybeMsg (attrs ++ cls)
|
||||
Plays.Details id cards maybeMsg Nothing
|
||||
|
||||
@@ -18,6 +18,7 @@ module MassiveDecks.Models.Decoders exposing
|
||||
, revealingRound
|
||||
, settings
|
||||
, sourceInfo
|
||||
, specificRound
|
||||
, tokenValidity
|
||||
, userId
|
||||
, userSummary
|
||||
@@ -286,7 +287,7 @@ game : Maybe LikeDetail -> Json.Decoder Game
|
||||
game ld =
|
||||
Json.succeed Game
|
||||
|> Json.required "round" (round ld)
|
||||
|> Json.required "history" (Json.list completeRound)
|
||||
|> Json.required "history" (Json.list (specificRound (completeRound Nothing)))
|
||||
|> Json.required "playerOrder" (Json.list userId)
|
||||
|> Json.required "players" (Json.dict player)
|
||||
|> Json.required "rules" rules
|
||||
@@ -862,7 +863,7 @@ privilegeChanged =
|
||||
gameStarted : Json.Decoder Events.Event
|
||||
gameStarted =
|
||||
Json.succeed (\r -> \h -> { round = r, hand = h } |> Events.GameStarted)
|
||||
|> Json.required "round" playingRound
|
||||
|> Json.required "round" (specificRound playingRound)
|
||||
|> Json.optional "hand" (Json.list response |> Json.map Just) Nothing
|
||||
|
||||
|
||||
@@ -1094,26 +1095,39 @@ styleByName name =
|
||||
|
||||
round : Maybe LikeDetail -> Json.Decoder Round
|
||||
round ld =
|
||||
Json.field "stage" Json.string |> Json.andThen (roundByName ld)
|
||||
let
|
||||
stageDetailsByName name =
|
||||
case name of
|
||||
"Playing" ->
|
||||
playingRound |> Json.map Round.P
|
||||
|
||||
"Revealing" ->
|
||||
revealingRound ld |> Json.map Round.R
|
||||
|
||||
"Judging" ->
|
||||
judgingRound ld |> Json.map Round.J
|
||||
|
||||
"Complete" ->
|
||||
completeRound ld |> Json.map Round.C
|
||||
|
||||
_ ->
|
||||
unknownValue "round stage" name
|
||||
|
||||
byName stageName =
|
||||
specificRound (Json.field "stage" (stageDetailsByName stageName))
|
||||
in
|
||||
Json.field "stage" Json.string |> Json.andThen byName
|
||||
|
||||
|
||||
roundByName : Maybe LikeDetail -> String -> Json.Decoder Round
|
||||
roundByName ld name =
|
||||
case name of
|
||||
"Playing" ->
|
||||
playingRound |> Json.map Round.P
|
||||
|
||||
"Revealing" ->
|
||||
revealingRound ld |> Json.map Round.R
|
||||
|
||||
"Judging" ->
|
||||
judgingRound ld |> Json.map Round.J
|
||||
|
||||
"Complete" ->
|
||||
completeRound |> Json.map Round.C
|
||||
|
||||
_ ->
|
||||
unknownValue "round stage" name
|
||||
specificRound : Json.Decoder stageDetails -> Json.Decoder (Round.Specific stageDetails)
|
||||
specificRound stageDetails =
|
||||
Json.succeed Round.Specific
|
||||
|> Json.required "id" Round.idDecoder
|
||||
|> Json.required "czar" userId
|
||||
|> Json.required "players" playerSet
|
||||
|> Json.required "call" call
|
||||
|> Json.required "startedAt" Time.timeDecoder
|
||||
|> Json.custom stageDetails
|
||||
|
||||
|
||||
playerSet : Json.Decoder (Set User.Id)
|
||||
@@ -1123,13 +1137,8 @@ playerSet =
|
||||
|
||||
playingRound : Json.Decoder Round.Playing
|
||||
playingRound =
|
||||
Json.succeed Round.playing
|
||||
|> Json.required "id" Round.idDecoder
|
||||
|> Json.required "czar" userId
|
||||
|> Json.required "players" playerSet
|
||||
|> Json.required "call" call
|
||||
Json.succeed (Round.Playing Round.noPick)
|
||||
|> Json.required "played" playerSet
|
||||
|> Json.required "startedAt" Time.timeDecoder
|
||||
|> Json.optional "timedOut" Json.bool False
|
||||
|
||||
|
||||
@@ -1142,39 +1151,25 @@ likeDetail =
|
||||
|
||||
revealingRound : Maybe Round.LikeDetail -> Json.Decoder Round.Revealing
|
||||
revealingRound ld =
|
||||
Json.succeed (Round.revealing ld)
|
||||
|> Json.required "id" Round.idDecoder
|
||||
|> Json.required "czar" userId
|
||||
|> Json.required "players" playerSet
|
||||
|> Json.required "call" call
|
||||
Json.succeed (Round.Revealing (ld |> Maybe.withDefault Round.defaultLikeDetail) Nothing Nothing)
|
||||
|> Json.required "plays" (Json.list play)
|
||||
|> Json.required "startedAt" Time.timeDecoder
|
||||
|> Json.optional "timedOut" Json.bool False
|
||||
|
||||
|
||||
judgingRound : Maybe Round.LikeDetail -> Json.Decoder Round.Judging
|
||||
judgingRound ld =
|
||||
Json.succeed (Round.judging Nothing ld)
|
||||
|> Json.required "id" Round.idDecoder
|
||||
|> Json.required "czar" userId
|
||||
|> Json.required "players" playerSet
|
||||
|> Json.required "call" call
|
||||
Json.succeed (Round.Judging (ld |> Maybe.withDefault Round.defaultLikeDetail) Nothing)
|
||||
|> Json.required "plays" (Json.list knownPlay)
|
||||
|> Json.required "startedAt" Time.timeDecoder
|
||||
|> Json.optional "timedOut" Json.bool False
|
||||
|
||||
|
||||
completeRound : Json.Decoder Round.Complete
|
||||
completeRound =
|
||||
Json.map8 Round.complete
|
||||
(Json.field "id" Round.idDecoder)
|
||||
(Json.field "czar" userId)
|
||||
(Json.field "players" playerSet)
|
||||
(Json.field "call" call)
|
||||
(Json.field "plays" (Json.dict playWithLikes))
|
||||
(Json.field "playOrder" (Json.list userId))
|
||||
(Json.field "winner" userId)
|
||||
(Json.field "startedAt" Time.timeDecoder)
|
||||
completeRound : Maybe Round.LikeDetail -> Json.Decoder Round.Complete
|
||||
completeRound ld =
|
||||
Json.succeed (Round.Complete (ld |> Maybe.withDefault Round.defaultLikeDetail) Nothing)
|
||||
|> Json.required "playedBy" (Json.dict playId)
|
||||
|> Json.required "plays" (Json.dict playWithLikes)
|
||||
|> Json.required "playOrder" (Json.list userId)
|
||||
|> Json.required "winner" userId
|
||||
|
||||
|
||||
playWithLikes : Json.Decoder Play.WithLikes
|
||||
|
||||
@@ -227,7 +227,7 @@ roundId =
|
||||
|
||||
stage : Round.Stage -> Json.Value
|
||||
stage =
|
||||
Round.stageToName >> Json.string
|
||||
Round.stageToString >> Json.string
|
||||
|
||||
|
||||
checkAlive : List Lobby.Token -> Json.Value
|
||||
|
||||
@@ -197,7 +197,11 @@ update wrap shared msg model =
|
||||
Events.GameStarted { round, hand } ->
|
||||
let
|
||||
( g, gameCmd ) =
|
||||
Game.applyGameStarted (GameMsg >> wrap) lobby round (hand |> Maybe.withDefault [])
|
||||
Game.applyGameStarted
|
||||
(GameMsg >> wrap)
|
||||
lobby
|
||||
(round |> Round.withStage (Round.P round.stage))
|
||||
(hand |> Maybe.withDefault [])
|
||||
|
||||
r =
|
||||
model.route
|
||||
@@ -1157,21 +1161,25 @@ userDetails shared game userId user =
|
||||
|
||||
|
||||
playStateDetail : Maybe Round -> User.Id -> Maybe ( String, MdString )
|
||||
playStateDetail round userId =
|
||||
case round of
|
||||
Just (Round.P p) ->
|
||||
case Player.playState p userId of
|
||||
Player.Playing ->
|
||||
Just ( "playing", Strings.StillPlaying )
|
||||
playStateDetail maybeRound userId =
|
||||
let
|
||||
givenRound round =
|
||||
case round.stage of
|
||||
Round.P stage ->
|
||||
case Player.playState (Round.withStage stage round) userId of
|
||||
Player.Playing ->
|
||||
Just ( "playing", Strings.StillPlaying )
|
||||
|
||||
Player.Played ->
|
||||
Just ( "played", Strings.Played )
|
||||
Player.Played ->
|
||||
Just ( "played", Strings.Played )
|
||||
|
||||
Player.NotInRound ->
|
||||
Player.NotInRound ->
|
||||
Nothing
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
in
|
||||
maybeRound |> Maybe.andThen givenRound
|
||||
|
||||
|
||||
viewDetails : Shared -> List (Maybe ( String, MdString )) -> List (Html msg)
|
||||
|
||||
@@ -32,7 +32,7 @@ type Event
|
||||
| Presence { user : User.Id, state : PresenceState }
|
||||
| Configured { change : Json.Patch }
|
||||
-- Not a game event because we don't need to be in a game
|
||||
| GameStarted { round : Round.Playing, hand : Maybe (List Card.Response) }
|
||||
| GameStarted { round : Round.Specific Round.Playing, hand : Maybe (List Card.Response) }
|
||||
| Game GameEvent
|
||||
| PrivilegeChanged { user : User.Id, privilege : User.Privilege }
|
||||
| UserRoleChanged { user : User.Id, role : User.Role, hand : Maybe (List Card.Response) }
|
||||
@@ -51,6 +51,7 @@ type GameEvent
|
||||
| CardDiscarded { player : User.Id, card : Card.Response, replacement : Maybe Card.Response }
|
||||
| PlaySubmitted { by : User.Id }
|
||||
| PlayTakenBack { by : User.Id }
|
||||
| PlayLiked { play : Play.Id }
|
||||
| PlayerAway { player : User.Id }
|
||||
| PlayerBack { player : User.Id }
|
||||
| Timed TimedState
|
||||
|
||||
@@ -24,40 +24,44 @@ import Set exposing (Set)
|
||||
|
||||
view : Shared -> Config -> Dict User.Id User -> Game.Model -> List (Html msg)
|
||||
view shared config users game =
|
||||
case game.game.round of
|
||||
let
|
||||
round =
|
||||
game.game.round
|
||||
in
|
||||
case round.stage of
|
||||
Round.P playing ->
|
||||
viewPlaying shared config game.playStyles playing
|
||||
viewPlaying shared config game.playStyles round playing
|
||||
|
||||
Round.R revealing ->
|
||||
viewRevealing shared config users revealing
|
||||
viewRevealing shared config users round revealing
|
||||
|
||||
Round.J judging ->
|
||||
viewJudging shared config users judging
|
||||
viewJudging shared config users round judging
|
||||
|
||||
Round.C complete ->
|
||||
viewComplete shared config users complete
|
||||
viewComplete shared config users round complete
|
||||
|
||||
|
||||
|
||||
{- Private -}
|
||||
|
||||
|
||||
viewPlaying : Shared -> Config -> Game.PlayStyles -> Round.Playing -> List (Html msg)
|
||||
viewPlaying shared config playStyles round =
|
||||
viewPlaying : Shared -> Config -> Game.PlayStyles -> Round -> Round.Playing -> List (Html msg)
|
||||
viewPlaying shared config playStyles round stage =
|
||||
let
|
||||
slots =
|
||||
round.call |> Call.slotCount
|
||||
in
|
||||
[ viewCall shared config Nothing round.call
|
||||
, viewUnknownPlays shared slots playStyles round.players round.played
|
||||
, viewUnknownPlays shared slots playStyles round.players stage.played
|
||||
]
|
||||
|
||||
|
||||
viewRevealing : Shared -> Config -> Dict User.Id User -> Round.Revealing -> List (Html msg)
|
||||
viewRevealing shared config users round =
|
||||
viewRevealing : Shared -> Config -> Dict User.Id User -> Round -> Round.Revealing -> List (Html msg)
|
||||
viewRevealing shared config users round stage =
|
||||
let
|
||||
fillWith =
|
||||
case round.plays |> List.filter (\p -> Just p.id == round.lastRevealed) of
|
||||
case stage.plays |> List.filter (\p -> Just p.id == stage.lastRevealed) of
|
||||
play :: [] ->
|
||||
play.responses |> Maybe.map Parts.fillsFromPlay
|
||||
|
||||
@@ -65,35 +69,35 @@ viewRevealing shared config users round =
|
||||
Nothing
|
||||
|
||||
plays =
|
||||
round.plays |> List.map (\p -> ( Nothing, p.responses |> Maybe.map (\r -> { play = r, likes = Nothing }) ))
|
||||
stage.plays |> List.map (\p -> ( Nothing, p.responses |> Maybe.map (\r -> { play = r, likes = Nothing }) ))
|
||||
in
|
||||
[ viewCall shared config fillWith round.call
|
||||
, viewPlays shared config (round.call |> Call.slotCount) users Nothing plays
|
||||
]
|
||||
|
||||
|
||||
viewJudging : Shared -> Config -> Dict User.Id User -> Round.Judging -> List (Html msg)
|
||||
viewJudging shared config users round =
|
||||
viewJudging : Shared -> Config -> Dict User.Id User -> Round -> Round.Judging -> List (Html msg)
|
||||
viewJudging shared config users round stage =
|
||||
let
|
||||
plays =
|
||||
round.plays |> List.map (\p -> ( Nothing, Just { play = p.responses, likes = Nothing } ))
|
||||
stage.plays |> List.map (\p -> ( Nothing, Just { play = p.responses, likes = Nothing } ))
|
||||
in
|
||||
[ viewCall shared config Nothing round.call
|
||||
, viewPlays shared config (round.call |> Call.slotCount) users Nothing plays
|
||||
]
|
||||
|
||||
|
||||
viewComplete : Shared -> Config -> Dict User.Id User -> Round.Complete -> List (Html msg)
|
||||
viewComplete shared config users round =
|
||||
viewComplete : Shared -> Config -> Dict User.Id User -> Round -> Round.Complete -> List (Html msg)
|
||||
viewComplete shared config users round stage =
|
||||
let
|
||||
plays =
|
||||
round.playOrder |> List.map (\u -> ( Just u, Dict.get u round.plays ))
|
||||
stage.playOrder |> List.map (\u -> ( Just u, Dict.get u stage.plays ))
|
||||
|
||||
winner =
|
||||
Dict.get round.winner round.plays |> Maybe.map (.play >> Parts.fillsFromPlay)
|
||||
Dict.get stage.winner stage.plays |> Maybe.map (.play >> Parts.fillsFromPlay)
|
||||
in
|
||||
[ viewCall shared config winner round.call
|
||||
, viewPlays shared config (round.call |> Call.slotCount) users (Just round.winner) plays
|
||||
, viewPlays shared config (round.call |> Call.slotCount) users (Just stage.winner) plays
|
||||
]
|
||||
|
||||
|
||||
|
||||
Generated
-15686
File diff suppressed because it is too large
Load Diff
Generated
+608
-836
File diff suppressed because it is too large
Load Diff
+24
-24
@@ -28,51 +28,51 @@
|
||||
"config.json5"
|
||||
],
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.2",
|
||||
"axios": "^0.19.2",
|
||||
"ajv": "^6.12.4",
|
||||
"axios": "^0.20.0",
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.1",
|
||||
"express-async-errors": "^3.1.1",
|
||||
"express-winston": "^4.0.3",
|
||||
"express-winston": "^4.0.5",
|
||||
"express-ws": "^4.0.0",
|
||||
"fast-json-patch": "^3.0.0-1",
|
||||
"generic-pool": "^3.7.1",
|
||||
"hashids": "^2.2.1",
|
||||
"helmet": "^3.23.0",
|
||||
"http-status-codes": "^1.3.2",
|
||||
"helmet": "^4.1.0",
|
||||
"http-status-codes": "^2.1.2",
|
||||
"json5": "^2.1.3",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"moment": "^2.26.0",
|
||||
"pg": "^8.2.1",
|
||||
"rfc6902": "^3.0.4",
|
||||
"moment": "^2.27.0",
|
||||
"pg": "^8.3.3",
|
||||
"rfc6902": "^4.0.0",
|
||||
"source-map-support": "^0.5.19",
|
||||
"uuid": "^8.1.0",
|
||||
"winston": "^3.2.1",
|
||||
"uuid": "^8.3.0",
|
||||
"winston": "^3.3.3",
|
||||
"wu": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/express-serve-static-core": "^4.17.7",
|
||||
"@types/express": "^4.17.8",
|
||||
"@types/express-serve-static-core": "^4.17.12",
|
||||
"@types/express-winston": "^4.0.0",
|
||||
"@types/express-ws": "^3.0.0",
|
||||
"@types/generic-pool": "^3.1.9",
|
||||
"@types/hashids": "^1.0.30",
|
||||
"@types/helmet": "^0.0.47",
|
||||
"@types/hashids": "^1.0.31",
|
||||
"@types/helmet": "^0.0.48",
|
||||
"@types/json5": "0.0.30",
|
||||
"@types/jsonwebtoken": "^8.5.0",
|
||||
"@types/pg": "^7.14.3",
|
||||
"@types/qs": "^6.9.3",
|
||||
"@types/source-map-support": "^0.5.1",
|
||||
"@types/uuid": "^8.0.0",
|
||||
"@types/pg": "^7.14.4",
|
||||
"@types/qs": "^6.9.4",
|
||||
"@types/source-map-support": "^0.5.3",
|
||||
"@types/uuid": "^8.3.0",
|
||||
"@types/wu": "^2.1.42",
|
||||
"@typescript-eslint/eslint-plugin": "^3.2.0",
|
||||
"@typescript-eslint/parser": "^3.2.0",
|
||||
"eslint": "^7.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.0.1",
|
||||
"@typescript-eslint/parser": "^4.0.1",
|
||||
"eslint": "^7.8.1",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"nodemon": "^2.0.4",
|
||||
"prettier": "2.0.5",
|
||||
"typescript": "^3.9.3",
|
||||
"prettier": "2.1.1",
|
||||
"typescript": "^4.0.2",
|
||||
"typescript-json-validator": "^2.4.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import * as EnforceTimeLimit from "./game-action/enforce-time-limit";
|
||||
import * as PlayerAction from "./game-action/player";
|
||||
import * as Redraw from "./game-action/redraw";
|
||||
import * as SetPresence from "./game-action/set-presence";
|
||||
import * as Like from "./game-action/like";
|
||||
|
||||
/**
|
||||
* An action only a player can perform.
|
||||
@@ -21,7 +22,8 @@ export type GameAction =
|
||||
| Czar.Czar
|
||||
| Redraw.Redraw
|
||||
| EnforceTimeLimit.EnforceTimeLimit
|
||||
| SetPresence.SetPresence;
|
||||
| SetPresence.SetPresence
|
||||
| Like.Like;
|
||||
|
||||
class GameActions extends Actions.Group<
|
||||
Action.Action,
|
||||
@@ -35,7 +37,8 @@ class GameActions extends Actions.Group<
|
||||
Czar.actions,
|
||||
Redraw.actions,
|
||||
EnforceTimeLimit.actions,
|
||||
SetPresence.actions
|
||||
SetPresence.actions,
|
||||
Like.actions
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+18
-13
@@ -1,12 +1,14 @@
|
||||
import * as Play from "../../../games/cards/play";
|
||||
import * as Round from "../../../games/game/round";
|
||||
import * as Lobby from "../../../lobby";
|
||||
import * as Actions from "../../actions";
|
||||
import * as Handler from "../../handler";
|
||||
import { Player } from "../player";
|
||||
import * as Play from "../../games/cards/play";
|
||||
import * as Round from "../../games/game/round";
|
||||
import * as Lobby from "../../lobby";
|
||||
import * as Actions from "../actions";
|
||||
import * as Handler from "../handler";
|
||||
import { GameAction } from "../game-action";
|
||||
import * as PlayLiked from "../../events/game-event/play-liked";
|
||||
import * as Event from "../../event";
|
||||
|
||||
/**
|
||||
* A player plays a white card into a round.
|
||||
* A player or spectator likes a play.
|
||||
*/
|
||||
export interface Like {
|
||||
action: "Like";
|
||||
@@ -14,7 +16,7 @@ export interface Like {
|
||||
}
|
||||
|
||||
class LikeActions extends Actions.Implementation<
|
||||
Player,
|
||||
GameAction,
|
||||
Like,
|
||||
"Like",
|
||||
Lobby.WithActiveGame
|
||||
@@ -27,11 +29,9 @@ class LikeActions extends Actions.Implementation<
|
||||
action
|
||||
) => {
|
||||
if (
|
||||
lobby.game.round.verifyStage<Round.Revealing | Round.Judging>(
|
||||
action,
|
||||
"Revealing",
|
||||
"Judging"
|
||||
)
|
||||
lobby.game.round.verifyStage<
|
||||
Round.Revealing | Round.Judging | Round.Complete
|
||||
>(action, "Revealing", "Judging", "Complete")
|
||||
) {
|
||||
const cRound = lobby.game.round;
|
||||
const target = cRound.plays.find((p) => p.id === action.play);
|
||||
@@ -41,8 +41,13 @@ class LikeActions extends Actions.Implementation<
|
||||
target.likes.find((id) => id === auth.uid) === undefined
|
||||
) {
|
||||
target.likes.push(auth.uid);
|
||||
const events =
|
||||
lobby.game.round.stage === "Complete"
|
||||
? [Event.targetAll(PlayLiked.of(action.play))]
|
||||
: [];
|
||||
return {
|
||||
lobby,
|
||||
events,
|
||||
};
|
||||
} else {
|
||||
return {};
|
||||
@@ -1,9 +1,12 @@
|
||||
import * as Actions from "./../actions";
|
||||
import * as Like from "./player/like";
|
||||
import * as Submit from "./player/submit";
|
||||
import * as TakeBack from "./player/take-back";
|
||||
import * as Fill from "./player/fill";
|
||||
import * as Discard from "./player/discard";
|
||||
import * as Action from "../../action";
|
||||
import * as Lobby from "../../lobby";
|
||||
import * as Token from "../../user/token";
|
||||
import * as GameAction from "../game-action";
|
||||
|
||||
/**
|
||||
* An action only players can perform.
|
||||
@@ -11,14 +14,27 @@ import * as Discard from "./player/discard";
|
||||
export type Player =
|
||||
| Submit.Submit
|
||||
| TakeBack.TakeBack
|
||||
| Like.Like
|
||||
| Fill.Fill
|
||||
| Discard.Discard;
|
||||
|
||||
export const actions = new Actions.PassThroughGroup(
|
||||
Submit.actions,
|
||||
TakeBack.actions,
|
||||
Like.actions,
|
||||
Fill.actions,
|
||||
Discard.actions
|
||||
);
|
||||
class PlayerActions extends Actions.Group<
|
||||
Action.Action,
|
||||
Player,
|
||||
Lobby.WithActiveGame,
|
||||
Lobby.WithActiveGame
|
||||
> {
|
||||
constructor() {
|
||||
super(Submit.actions, TakeBack.actions, Fill.actions, Discard.actions);
|
||||
}
|
||||
|
||||
limit(
|
||||
auth: Token.Claims,
|
||||
lobby: Lobby.WithActiveGame,
|
||||
action: Player
|
||||
): lobby is Lobby.WithActiveGame {
|
||||
GameAction.expectRole(auth, action, lobby.game, "Player");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export const actions = new PlayerActions();
|
||||
|
||||
@@ -39,9 +39,6 @@ export const Schema = {
|
||||
{
|
||||
$ref: "#/definitions/TakeBack",
|
||||
},
|
||||
{
|
||||
$ref: "#/definitions/Like",
|
||||
},
|
||||
{
|
||||
$ref: "#/definitions/Fill",
|
||||
},
|
||||
@@ -60,6 +57,9 @@ export const Schema = {
|
||||
{
|
||||
$ref: "#/definitions/EnforceTimeLimit",
|
||||
},
|
||||
{
|
||||
$ref: "#/definitions/Like",
|
||||
},
|
||||
{
|
||||
$ref: "#/definitions/Configure",
|
||||
},
|
||||
@@ -384,7 +384,7 @@ export const Schema = {
|
||||
type: "object",
|
||||
},
|
||||
Id: {
|
||||
description: "A unique id for a play.",
|
||||
description: "A unique id for an instance of a card.",
|
||||
type: "string",
|
||||
},
|
||||
Id_1: {
|
||||
@@ -455,7 +455,7 @@ export const Schema = {
|
||||
Like: {
|
||||
additionalProperties: false,
|
||||
defaultProperties: [],
|
||||
description: "A player plays a white card into a round.",
|
||||
description: "A player or spectator likes a play.",
|
||||
properties: {
|
||||
action: {
|
||||
enum: ["Like"],
|
||||
|
||||
@@ -12,6 +12,7 @@ import { StageTimerDone } from "./game-event/stage-timer-done";
|
||||
import { StartRevealing } from "./game-event/start-revealing";
|
||||
import { StartJudging } from "./game-event/start-judging";
|
||||
import { CardDiscarded } from "./game-event/card-discarded";
|
||||
import { PlayLiked } from "./game-event/play-liked";
|
||||
|
||||
export type GameEvent =
|
||||
| GameStarted
|
||||
@@ -27,4 +28,5 @@ export type GameEvent =
|
||||
| PlayerPresenceChanged
|
||||
| PauseStateChanged
|
||||
| StageTimerDone
|
||||
| GameEnded;
|
||||
| GameEnded
|
||||
| PlayLiked;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import * as Play from "../../games/cards/play";
|
||||
|
||||
/**
|
||||
* Indicates a play was liked.
|
||||
* Note this is only sent out after likes are visible (when the round is complete).
|
||||
*/
|
||||
export interface PlayLiked {
|
||||
event: "PlayLiked";
|
||||
id: Play.Id;
|
||||
}
|
||||
|
||||
export const of = (id: Play.Id): PlayLiked => ({
|
||||
event: "PlayLiked",
|
||||
id,
|
||||
});
|
||||
@@ -34,14 +34,14 @@ export function reduce<T, L extends Lobby>(
|
||||
toChange: (lobby: L, item: T) => ConstrainedChange<L>
|
||||
): ConstrainedChange<L> {
|
||||
let currentLobby = lobby;
|
||||
let lobbyUnchanged = true;
|
||||
let lobbyChanged = false;
|
||||
const events: Event.Distributor[] = [];
|
||||
const timeouts: Timeout.After[] = [];
|
||||
const tasks: Task[] = [];
|
||||
for (const item of items) {
|
||||
const result = toChange(currentLobby, item);
|
||||
if (result.lobby !== undefined) {
|
||||
lobbyUnchanged = false;
|
||||
lobbyChanged = true;
|
||||
currentLobby = result.lobby;
|
||||
}
|
||||
if (result.events !== undefined) {
|
||||
@@ -55,7 +55,7 @@ export function reduce<T, L extends Lobby>(
|
||||
}
|
||||
}
|
||||
return {
|
||||
...(lobbyUnchanged ? { lobby: currentLobby } : {}),
|
||||
...(lobbyChanged ? { lobby: currentLobby } : {}),
|
||||
...(events.length > 0 ? { events } : {}),
|
||||
...(timeouts.length > 0 ? { timeouts } : {}),
|
||||
...(tasks.length > 0 ? { tasks } : {}),
|
||||
|
||||
Reference in New Issue
Block a user