Resolve #129: Allow liking plays during revealing.
This commit is contained in:
Generated
+3
-3
@@ -2160,9 +2160,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/chrome": {
|
||||
"version": "0.0.111",
|
||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.111.tgz",
|
||||
"integrity": "sha512-Vxo44zBE3EjBW5RZd11GL8aKo14EdcmPxaETtRR4khZHU2BEw0YBLLZMRRgcgRgpbO0pAGhSfUyI8htHJsyTIg==",
|
||||
"version": "0.0.112",
|
||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.112.tgz",
|
||||
"integrity": "sha512-VJjHiDuF4Xmcn1DuOnGcrMDiGBvoJnaIE6u3ZkhsSz2L16HhnfqIuuM8EAvgQa0ODbNpotHqtPRTKpYoNfpAqQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/filesystem": "*",
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/canvas-confetti": "^1.0.0",
|
||||
"@types/chrome": "^0.0.111",
|
||||
"@types/chrome": "^0.0.112",
|
||||
"@types/chromecast-caf-receiver": "^5.0.5",
|
||||
"@types/chromecast-caf-sender": "^1.0.3",
|
||||
"app-manifest-loader": "^2.4.1",
|
||||
|
||||
@@ -154,18 +154,24 @@ update wrap shared msg model =
|
||||
|
||||
PickPlay id ->
|
||||
let
|
||||
makePick r wrapRound =
|
||||
let
|
||||
pick =
|
||||
if r.pick == Just id then
|
||||
Nothing
|
||||
|
||||
else
|
||||
Just id
|
||||
in
|
||||
{ r | pick = pick } |> wrapRound
|
||||
|
||||
newRound =
|
||||
case game.round of
|
||||
Round.J judging ->
|
||||
let
|
||||
pick =
|
||||
if judging.pick == Just id then
|
||||
Nothing
|
||||
makePick judging Round.J
|
||||
|
||||
else
|
||||
Just id
|
||||
in
|
||||
{ judging | pick = pick } |> Round.J
|
||||
Round.R revealing ->
|
||||
makePick revealing Round.R
|
||||
|
||||
_ ->
|
||||
game.round
|
||||
@@ -251,17 +257,30 @@ update wrap shared msg model =
|
||||
Like ->
|
||||
let
|
||||
( newRound, action ) =
|
||||
let
|
||||
like r roundWrap =
|
||||
let
|
||||
likeDetail =
|
||||
r.likeDetail
|
||||
|
||||
insert =
|
||||
r.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
|
||||
)
|
||||
in
|
||||
case game.round of
|
||||
Round.J judging ->
|
||||
( { judging
|
||||
| liked =
|
||||
judging.pick
|
||||
|> Maybe.map (\p -> Set.insert p judging.liked)
|
||||
|> Maybe.withDefault judging.liked
|
||||
}
|
||||
|> Round.J
|
||||
, judging.pick |> Maybe.map Actions.like |> Maybe.withDefault Cmd.none
|
||||
)
|
||||
like judging Round.J
|
||||
|
||||
Round.R revealing ->
|
||||
like revealing Round.R
|
||||
|
||||
_ ->
|
||||
( game.round, Cmd.none )
|
||||
@@ -455,15 +474,19 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
|
||||
Events.Timed (Events.WithTime { event, time }) ->
|
||||
case event of
|
||||
Events.StartRevealing { plays, drawn } ->
|
||||
Events.StartRevealing { plays, afterPlaying } ->
|
||||
case model.game.round of
|
||||
Round.P r ->
|
||||
let
|
||||
{ played, drawn } =
|
||||
afterPlaying
|
||||
|
||||
oldGame =
|
||||
model.game
|
||||
|
||||
newRound =
|
||||
Round.revealing
|
||||
(Just { played = played, liked = Set.empty })
|
||||
r.id
|
||||
r.czar
|
||||
r.players
|
||||
@@ -638,33 +661,62 @@ applyGameEvent wrap wrapEvent auth shared gameEvent model =
|
||||
in
|
||||
( { model | game = { game | round = newRound } }, speech )
|
||||
|
||||
Events.StartJudging { plays } ->
|
||||
Events.StartJudging { plays, afterPlaying } ->
|
||||
let
|
||||
{ played, drawn } =
|
||||
afterPlaying
|
||||
|
||||
game =
|
||||
model.game
|
||||
|
||||
newRound =
|
||||
makeNewRound r pick ld known =
|
||||
case known of
|
||||
Just k ->
|
||||
Round.judging pick
|
||||
ld
|
||||
r.id
|
||||
r.czar
|
||||
r.players
|
||||
r.call
|
||||
k
|
||||
time
|
||||
False
|
||||
|> Round.J
|
||||
|
||||
Nothing ->
|
||||
-- TODO: Error.
|
||||
game.round
|
||||
|
||||
( newRound, newHand ) =
|
||||
case game.round of
|
||||
Round.P r ->
|
||||
let
|
||||
known =
|
||||
-- TODO: Error on default here.
|
||||
plays |> Maybe.withDefault []
|
||||
picked =
|
||||
r.pick.cards |> Set.fromList
|
||||
|
||||
nH =
|
||||
model.hand
|
||||
|> List.filter (\c -> not (Set.member c.details.id picked))
|
||||
|> (\h -> h ++ (drawn |> Maybe.withDefault []))
|
||||
in
|
||||
Round.judging r.id r.czar r.players r.call known time False |> Round.J
|
||||
( makeNewRound r Nothing (Just { played = played, liked = Set.empty }) plays
|
||||
, nH
|
||||
)
|
||||
|
||||
Round.R r ->
|
||||
let
|
||||
known =
|
||||
plays |> Maybe.withDefault (r.plays |> List.filterMap Play.asKnown)
|
||||
p =
|
||||
plays
|
||||
|> Maybe.withDefault (r.plays |> List.filterMap Play.asKnown)
|
||||
|> Just
|
||||
in
|
||||
Round.judging r.id r.czar r.players r.call known time False |> Round.J
|
||||
( makeNewRound r r.pick (Just r.likeDetail) p, model.hand )
|
||||
|
||||
_ ->
|
||||
-- TODO: Error
|
||||
game.round
|
||||
-- TODO: Error.
|
||||
( game.round, model.hand )
|
||||
in
|
||||
( { model | game = { game | round = newRound } }, Cmd.none )
|
||||
( { model | game = { game | round = newRound }, hand = newHand }, Cmd.none )
|
||||
|
||||
Events.PlayerAway { player } ->
|
||||
let
|
||||
|
||||
@@ -3,6 +3,7 @@ module MassiveDecks.Game.Round exposing
|
||||
, Data
|
||||
, Id
|
||||
, Judging
|
||||
, LikeDetail
|
||||
, Pick
|
||||
, PickState(..)
|
||||
, Playing
|
||||
@@ -139,12 +140,31 @@ playing id czar players call played startedAt timedOut =
|
||||
}
|
||||
|
||||
|
||||
type alias LikeDetail =
|
||||
{ played : Maybe Play.Id
|
||||
, liked : Set Play.Id
|
||||
}
|
||||
|
||||
|
||||
defaultLikeDetail : LikeDetail
|
||||
defaultLikeDetail =
|
||||
{ played = Nothing
|
||||
, liked = Set.empty
|
||||
}
|
||||
|
||||
|
||||
type alias Revealing =
|
||||
Data { plays : List Play, lastRevealed : Maybe Play.Id, timedOut : Bool }
|
||||
Data
|
||||
{ plays : List Play
|
||||
, lastRevealed : Maybe Play.Id
|
||||
, pick : Maybe Play.Id
|
||||
, likeDetail : LikeDetail
|
||||
, timedOut : Bool
|
||||
}
|
||||
|
||||
|
||||
revealing : Id -> User.Id -> Set User.Id -> Card.Call -> List Play -> Time -> Bool -> Revealing
|
||||
revealing id czar players call plays startedAt timedOut =
|
||||
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
|
||||
@@ -152,6 +172,8 @@ revealing id czar players call plays startedAt timedOut =
|
||||
, plays = plays
|
||||
, lastRevealed = Nothing
|
||||
, startedAt = startedAt
|
||||
, pick = Nothing
|
||||
, likeDetail = likeDetail |> Maybe.withDefault defaultLikeDetail
|
||||
, timedOut = timedOut
|
||||
}
|
||||
|
||||
@@ -159,18 +181,33 @@ revealing id czar players call plays startedAt timedOut =
|
||||
{-| A round while the czar is judging a round.
|
||||
-}
|
||||
type alias Judging =
|
||||
Data { plays : List Play.Known, pick : Maybe Play.Id, liked : Set Play.Id, timedOut : Bool }
|
||||
Data
|
||||
{ plays : List Play.Known
|
||||
, pick : Maybe Play.Id
|
||||
, likeDetail : LikeDetail
|
||||
, timedOut : Bool
|
||||
}
|
||||
|
||||
|
||||
judging : Id -> User.Id -> Set User.Id -> Card.Call -> List Play.Known -> Time -> Bool -> Judging
|
||||
judging id czar players call plays startedAt timedOut =
|
||||
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 = Nothing
|
||||
, liked = Set.empty
|
||||
, pick = pick
|
||||
, likeDetail = likeDetail |> Maybe.withDefault defaultLikeDetail
|
||||
, startedAt = startedAt
|
||||
, timedOut = timedOut
|
||||
}
|
||||
|
||||
@@ -24,16 +24,25 @@ view wrap auth shared config round =
|
||||
role =
|
||||
Player.role (Round.J round) auth.claims.uid
|
||||
|
||||
( action, instruction, isCzar ) =
|
||||
{ action, msg, instruction, isCzar } =
|
||||
case role of
|
||||
Player.RCzar ->
|
||||
( Maybe.map (always Action.Judge) round.pick, Strings.RevealPlaysInstruction, True )
|
||||
{ action = Maybe.map (always Action.Judge) round.pick
|
||||
, msg = \p -> p |> PickPlay |> wrap |> Just
|
||||
, instruction = Strings.RevealPlaysInstruction
|
||||
, isCzar = True
|
||||
}
|
||||
|
||||
Player.RPlayer ->
|
||||
( Maybe.andThen (\p -> Maybe.justIf (not (Set.member p round.liked)) Action.Like) round.pick
|
||||
, Strings.WaitingForCzarInstruction
|
||||
, False
|
||||
)
|
||||
let
|
||||
canBeLiked play =
|
||||
Just play /= round.likeDetail.played && not (Set.member play round.likeDetail.liked)
|
||||
in
|
||||
{ action = Maybe.andThen (\p -> Maybe.justIf (canBeLiked p) Action.Like) round.pick
|
||||
, msg = \p -> p |> PickPlay |> wrap |> Maybe.justIf (canBeLiked p)
|
||||
, instruction = Strings.WaitingForCzarInstruction
|
||||
, isCzar = False
|
||||
}
|
||||
|
||||
picked =
|
||||
round.plays
|
||||
@@ -43,7 +52,7 @@ view wrap auth shared config round =
|
||||
|> Maybe.withDefault []
|
||||
|
||||
details =
|
||||
round.plays |> List.map (playDetails wrap shared config round.liked)
|
||||
round.plays |> List.map (playDetails shared config round.likeDetail.liked msg)
|
||||
in
|
||||
{ instruction = Just instruction
|
||||
, action = action
|
||||
@@ -56,9 +65,19 @@ view wrap auth shared config round =
|
||||
{- Private -}
|
||||
|
||||
|
||||
playDetails : (Msg -> msg) -> Shared -> Config -> Set Play.Id -> Play.Known -> Plays.Details msg
|
||||
playDetails wrap shared config liked { id, responses } =
|
||||
playDetails : Shared -> Config -> Set Play.Id -> (Play.Id -> Maybe msg) -> Play.Known -> Plays.Details msg
|
||||
playDetails shared config liked 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)
|
||||
@@ -66,4 +85,4 @@ playDetails wrap shared config liked { id, responses } =
|
||||
attrs =
|
||||
[ HtmlA.class "liked" ] |> Maybe.justIf (Set.member id liked) |> Maybe.withDefault []
|
||||
in
|
||||
Plays.Details id cards (id |> PickPlay |> wrap |> Just) attrs
|
||||
Plays.Details id cards maybeMsg (attrs ++ cls)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
module MassiveDecks.Game.Round.Revealing exposing (view)
|
||||
|
||||
import Html.Attributes as HtmlA
|
||||
import MassiveDecks.Card.Call as Call
|
||||
import MassiveDecks.Card.Model as Card
|
||||
import MassiveDecks.Card.Play exposing (Play)
|
||||
import MassiveDecks.Card.Play as Play exposing (Play)
|
||||
import MassiveDecks.Card.Response as Response
|
||||
import MassiveDecks.Game.Action.Model as Action
|
||||
import MassiveDecks.Game.Messages exposing (..)
|
||||
import MassiveDecks.Game.Model exposing (RoundView)
|
||||
import MassiveDecks.Game.Player as Player
|
||||
@@ -14,6 +16,7 @@ import MassiveDecks.Pages.Lobby.Configure.Model exposing (Config)
|
||||
import MassiveDecks.Pages.Lobby.Model exposing (Auth)
|
||||
import MassiveDecks.Strings as Strings
|
||||
import MassiveDecks.Util.Maybe as Maybe
|
||||
import Set exposing (Set)
|
||||
|
||||
|
||||
view : (Msg -> msg) -> Auth -> Shared -> Config -> Round.Revealing -> RoundView msg
|
||||
@@ -22,19 +25,38 @@ view wrap auth shared config round =
|
||||
role =
|
||||
Player.role (Round.R round) auth.claims.uid
|
||||
|
||||
( instruction, isCzar ) =
|
||||
{ msg, action, instruction, isCzar } =
|
||||
case role of
|
||||
Player.RCzar ->
|
||||
( Strings.RevealPlaysInstruction, True )
|
||||
let
|
||||
msgPick { id, responses } =
|
||||
id |> Reveal |> wrap |> Maybe.justIf (responses == Nothing)
|
||||
in
|
||||
{ msg = msgPick
|
||||
, action = Nothing
|
||||
, instruction = Strings.RevealPlaysInstruction
|
||||
, isCzar = True
|
||||
}
|
||||
|
||||
Player.RPlayer ->
|
||||
( Strings.WaitingForCzarInstruction, False )
|
||||
let
|
||||
canBeLiked id =
|
||||
(Just id /= round.likeDetail.played) && not (Set.member id round.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
|
||||
, instruction = Strings.WaitingForCzarInstruction
|
||||
, isCzar = False
|
||||
}
|
||||
|
||||
slots =
|
||||
Call.slotCount round.call
|
||||
|
||||
plays =
|
||||
round.plays |> List.map (playDetails wrap shared config slots (role == Player.RCzar))
|
||||
round.plays |> List.map (playDetails shared config round.likeDetail.liked slots msg)
|
||||
|
||||
lastRevealed =
|
||||
case round.plays |> List.filter (\p -> Just p.id == round.lastRevealed) of
|
||||
@@ -45,8 +67,8 @@ view wrap auth shared config round =
|
||||
Nothing
|
||||
in
|
||||
{ instruction = Just instruction
|
||||
, action = Nothing
|
||||
, content = plays |> Plays.view [ ( "revealing", True ), ( "is-czar", isCzar ) ] Nothing
|
||||
, action = action
|
||||
, content = plays |> Plays.view [ ( "revealing", True ), ( "is-czar", isCzar ) ] round.pick
|
||||
, fillCallWith = lastRevealed |> Maybe.withDefault []
|
||||
}
|
||||
|
||||
@@ -55,12 +77,28 @@ view wrap auth shared config round =
|
||||
{- Private -}
|
||||
|
||||
|
||||
playDetails : (Msg -> msg) -> Shared -> Config -> Int -> Bool -> Play -> Plays.Details msg
|
||||
playDetails wrap shared config slots isCzar { id, responses } =
|
||||
playDetails : Shared -> Config -> Set Play.Id -> Int -> (Play -> Maybe msg) -> Play -> Plays.Details msg
|
||||
playDetails shared config liked slots msg play =
|
||||
let
|
||||
{ id, responses } =
|
||||
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 (Reveal id |> wrap |> Maybe.justIf isCzar) []
|
||||
Plays.Details id cards maybeMsg (attrs ++ cls)
|
||||
|
||||
@@ -36,7 +36,7 @@ import MassiveDecks.Card.Source.Model as Source exposing (Source)
|
||||
import MassiveDecks.Cast.Model as Cast
|
||||
import MassiveDecks.Game.Model as Game exposing (Game)
|
||||
import MassiveDecks.Game.Player as Player exposing (Player)
|
||||
import MassiveDecks.Game.Round as Round exposing (Round)
|
||||
import MassiveDecks.Game.Round as Round exposing (LikeDetail, Round)
|
||||
import MassiveDecks.Game.Rules as Rules exposing (Rules)
|
||||
import MassiveDecks.Game.Time as Time
|
||||
import MassiveDecks.Model exposing (..)
|
||||
@@ -248,26 +248,26 @@ languageFromCode code =
|
||||
|> Maybe.withDefault (unknownValue "language code" code)
|
||||
|
||||
|
||||
lobby : Json.Decoder Lobby
|
||||
lobby =
|
||||
lobby : Maybe LikeDetail -> Json.Decoder Lobby
|
||||
lobby ld =
|
||||
Json.map5 Lobby
|
||||
(Json.field "users" users)
|
||||
(Json.field "owner" userId)
|
||||
(Json.field "config" config)
|
||||
(Json.maybe (Json.field "game" game |> Json.map Game.emptyModel))
|
||||
(Json.maybe (Json.field "game" (game ld) |> Json.map Game.emptyModel))
|
||||
(Json.maybe (Json.field "errors" (Json.list gameStateError)) |> Json.map (Maybe.withDefault []))
|
||||
|
||||
|
||||
game : Json.Decoder Game
|
||||
game =
|
||||
Json.map7 Game
|
||||
(Json.field "round" round)
|
||||
(Json.field "history" (Json.list completeRound))
|
||||
(Json.field "playerOrder" (Json.list userId))
|
||||
(Json.field "players" (Json.dict player))
|
||||
(Json.field "rules" rules)
|
||||
(Json.maybe (Json.field "winner" (Json.list userId |> Json.map Set.fromList)))
|
||||
(Json.maybe (Json.field "paused" Json.bool) |> Json.map (Maybe.withDefault False))
|
||||
game : Maybe LikeDetail -> Json.Decoder Game
|
||||
game ld =
|
||||
Json.succeed Game
|
||||
|> Json.required "round" (round ld)
|
||||
|> Json.required "history" (Json.list completeRound)
|
||||
|> Json.required "playerOrder" (Json.list userId)
|
||||
|> Json.required "players" (Json.dict player)
|
||||
|> Json.required "rules" rules
|
||||
|> Json.optional "winner" (Json.list userId |> Json.map (Set.fromList >> Just)) Nothing
|
||||
|> Json.optional "paused" Json.bool False
|
||||
|
||||
|
||||
config : Json.Decoder Configure.Config
|
||||
@@ -757,17 +757,25 @@ playRevealed =
|
||||
(Json.field "play" (Json.list response))
|
||||
|
||||
|
||||
afterPlaying : Json.Decoder Events.AfterPlaying
|
||||
afterPlaying =
|
||||
Json.succeed Events.AfterPlaying
|
||||
|> Json.optional "played" (playId |> Json.map Just) Nothing
|
||||
|> Json.optional "drawn" (Json.list response |> Json.map Just) Nothing
|
||||
|
||||
|
||||
startRevealing : Json.Decoder Events.TimedGameEvent
|
||||
startRevealing =
|
||||
Json.map2 (\ps -> \dr -> Events.StartRevealing { plays = ps, drawn = dr })
|
||||
(Json.field "plays" (Json.list playId))
|
||||
(Json.maybe (Json.field "drawn" (Json.list response)))
|
||||
Json.succeed (\ps -> \ap -> Events.StartRevealing { plays = ps, afterPlaying = ap })
|
||||
|> Json.required "plays" (Json.list playId)
|
||||
|> Json.custom afterPlaying
|
||||
|
||||
|
||||
startJudging : Json.Decoder Events.TimedGameEvent
|
||||
startJudging =
|
||||
Json.succeed (\ps -> Events.StartJudging { plays = ps })
|
||||
Json.succeed (\ps -> \ap -> Events.StartJudging { plays = ps, afterPlaying = ap })
|
||||
|> Json.optional "plays" (knownPlay |> Json.list |> Json.map Just) Nothing
|
||||
|> Json.custom afterPlaying
|
||||
|
||||
|
||||
gameEvent : Json.Decoder Events.GameEvent -> Json.Decoder Event
|
||||
@@ -818,11 +826,18 @@ gameStarted =
|
||||
|
||||
sync : Json.Decoder Event
|
||||
sync =
|
||||
Json.map4 (\ls -> \h -> \p -> \pa -> Events.Sync { state = ls, hand = h, play = p, partialTimeAnchor = pa })
|
||||
(Json.field "state" lobby)
|
||||
(Json.maybe (Json.field "hand" (Json.list response)))
|
||||
(Json.maybe (Json.field "play" (Json.list Json.string)))
|
||||
(Json.field "gameTime" Time.partialAnchorDecoder)
|
||||
let
|
||||
construct ls h p pa =
|
||||
Events.Sync { state = ls, hand = h, play = p, partialTimeAnchor = pa }
|
||||
|
||||
decodeSync ld =
|
||||
Json.succeed construct
|
||||
|> Json.required "state" (lobby ld)
|
||||
|> Json.optional "hand" (Json.list response |> Json.map Just) Nothing
|
||||
|> Json.optional "play" (Json.list Json.string |> Json.map Just) Nothing
|
||||
|> Json.required "gameTime" Time.partialAnchorDecoder
|
||||
in
|
||||
Json.maybe (Json.field "likeDetail" likeDetail) |> Json.andThen decodeSync
|
||||
|
||||
|
||||
connection : User.Connection -> Json.Decoder Event
|
||||
@@ -990,22 +1005,22 @@ styleByName name =
|
||||
unknownValue "style" name
|
||||
|
||||
|
||||
round : Json.Decoder Round
|
||||
round =
|
||||
Json.field "stage" Json.string |> Json.andThen roundByName
|
||||
round : Maybe LikeDetail -> Json.Decoder Round
|
||||
round ld =
|
||||
Json.field "stage" Json.string |> Json.andThen (roundByName ld)
|
||||
|
||||
|
||||
roundByName : String -> Json.Decoder Round
|
||||
roundByName name =
|
||||
roundByName : Maybe LikeDetail -> String -> Json.Decoder Round
|
||||
roundByName ld name =
|
||||
case name of
|
||||
"Playing" ->
|
||||
playingRound |> Json.map Round.P
|
||||
|
||||
"Revealing" ->
|
||||
revealingRound |> Json.map Round.R
|
||||
revealingRound ld |> Json.map Round.R
|
||||
|
||||
"Judging" ->
|
||||
judgingRound |> Json.map Round.J
|
||||
judgingRound ld |> Json.map Round.J
|
||||
|
||||
"Complete" ->
|
||||
completeRound |> Json.map Round.C
|
||||
@@ -1021,38 +1036,45 @@ playerSet =
|
||||
|
||||
playingRound : Json.Decoder Round.Playing
|
||||
playingRound =
|
||||
Json.map7 Round.playing
|
||||
(Json.field "id" Round.idDecoder)
|
||||
(Json.field "czar" userId)
|
||||
(Json.field "players" playerSet)
|
||||
(Json.field "call" call)
|
||||
(Json.field "played" playerSet)
|
||||
(Json.field "startedAt" Time.timeDecoder)
|
||||
(Json.maybe (Json.field "timedOut" Json.bool) |> Json.map (Maybe.withDefault False))
|
||||
Json.succeed Round.playing
|
||||
|> Json.required "id" Round.idDecoder
|
||||
|> Json.required "czar" userId
|
||||
|> Json.required "players" playerSet
|
||||
|> Json.required "call" call
|
||||
|> Json.required "played" playerSet
|
||||
|> Json.required "startedAt" Time.timeDecoder
|
||||
|> Json.optional "timedOut" Json.bool False
|
||||
|
||||
|
||||
revealingRound : Json.Decoder Round.Revealing
|
||||
revealingRound =
|
||||
Json.map7 Round.revealing
|
||||
(Json.field "id" Round.idDecoder)
|
||||
(Json.field "czar" userId)
|
||||
(Json.field "players" playerSet)
|
||||
(Json.field "call" call)
|
||||
(Json.field "plays" (Json.list play))
|
||||
(Json.field "startedAt" Time.timeDecoder)
|
||||
(Json.maybe (Json.field "timedOut" Json.bool) |> Json.map (Maybe.withDefault False))
|
||||
likeDetail : Json.Decoder Round.LikeDetail
|
||||
likeDetail =
|
||||
Json.succeed Round.LikeDetail
|
||||
|> Json.optional "played" (playId |> Json.map Just) Nothing
|
||||
|> Json.required "liked" (Json.list playId |> Json.map Set.fromList)
|
||||
|
||||
|
||||
judgingRound : Json.Decoder Round.Judging
|
||||
judgingRound =
|
||||
Json.map7 Round.judging
|
||||
(Json.field "id" Round.idDecoder)
|
||||
(Json.field "czar" userId)
|
||||
(Json.field "players" playerSet)
|
||||
(Json.field "call" call)
|
||||
(Json.field "plays" (Json.list knownPlay))
|
||||
(Json.field "startedAt" Time.timeDecoder)
|
||||
(Json.maybe (Json.field "timedOut" Json.bool) |> Json.map (Maybe.withDefault False))
|
||||
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.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.required "plays" (Json.list knownPlay)
|
||||
|> Json.required "startedAt" Time.timeDecoder
|
||||
|> Json.optional "timedOut" Json.bool False
|
||||
|
||||
|
||||
completeRound : Json.Decoder Round.Complete
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
module MassiveDecks.Pages.Lobby.Events exposing
|
||||
( Event(..)
|
||||
( AfterPlaying
|
||||
, Event(..)
|
||||
, GameEvent(..)
|
||||
, PresenceState(..)
|
||||
, TimedGameEvent(..)
|
||||
@@ -63,6 +64,12 @@ type TimedState
|
||||
| WithTime { event : TimedGameEvent, time : Time }
|
||||
|
||||
|
||||
type alias AfterPlaying =
|
||||
{ played : Maybe Play.Id
|
||||
, drawn : Maybe (List Card.Response)
|
||||
}
|
||||
|
||||
|
||||
type TimedGameEvent
|
||||
= RoundStarted
|
||||
{ id : Round.Id
|
||||
@@ -71,7 +78,7 @@ type TimedGameEvent
|
||||
, call : Card.Call
|
||||
, drawn : Maybe (List Card.Response)
|
||||
}
|
||||
| StartRevealing { plays : List Play.Id, drawn : Maybe (List Card.Response) }
|
||||
| StartJudging { plays : Maybe (List Play.Known) }
|
||||
| StartRevealing { plays : List Play.Id, afterPlaying : AfterPlaying }
|
||||
| StartJudging { plays : Maybe (List Play.Known), afterPlaying : AfterPlaying }
|
||||
| RoundFinished { winner : User.Id, playedBy : Dict Play.Id Play.Details }
|
||||
| PlayRevealed { id : Play.Id, play : List Card.Response }
|
||||
|
||||
@@ -19,19 +19,16 @@
|
||||
}
|
||||
|
||||
.judging {
|
||||
.game-card {
|
||||
.active .game-card {
|
||||
@include cards.shadow(colors.$primary);
|
||||
}
|
||||
}
|
||||
|
||||
.judging.is-czar {
|
||||
.picked {
|
||||
.side {
|
||||
&.is-czar {
|
||||
.active .game-card {
|
||||
@include cards.shadow(colors.$secondary);
|
||||
}
|
||||
.picked .game-card .side {
|
||||
border-color: colors.$secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.game-card {
|
||||
@include cards.shadow(colors.$secondary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
@use "../_colors";
|
||||
@use "../_cards";
|
||||
|
||||
.revealing.is-czar {
|
||||
.game-card.face-down {
|
||||
.revealing {
|
||||
.active .game-card {
|
||||
@include cards.shadow(colors.$primary);
|
||||
}
|
||||
|
||||
&.is-czar {
|
||||
.active .game-card {
|
||||
@include cards.shadow(colors.$secondary);
|
||||
}
|
||||
.picked .game-card .side {
|
||||
border-color: colors.$secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+3
-3
@@ -289,9 +289,9 @@
|
||||
}
|
||||
},
|
||||
"@types/wu": {
|
||||
"version": "2.1.41",
|
||||
"resolved": "https://registry.npmjs.org/@types/wu/-/wu-2.1.41.tgz",
|
||||
"integrity": "sha512-c9F5iHE1b7QF7v+/ur/orN1gkphE363aPwLA5SZxpeSma6FJq4NpMwwLgcByVIdyF6wOgVTe9kM2nWRtKVzT0A==",
|
||||
"version": "2.1.42",
|
||||
"resolved": "https://registry.npmjs.org/@types/wu/-/wu-2.1.42.tgz",
|
||||
"integrity": "sha512-+GTq3oqVD+DY7iANRfazy8YhLlKwxIG2hTCF72W+yuCbnjZ6PSkxju/3emOZ4+lzMJ7ximpY3XnCQSBjIGbYgw==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@
|
||||
"@types/qs": "^6.9.2",
|
||||
"@types/source-map-support": "^0.5.1",
|
||||
"@types/uuid": "^7.0.3",
|
||||
"@types/wu": "^2.1.41",
|
||||
"@types/wu": "^2.1.42",
|
||||
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
||||
"@typescript-eslint/parser": "^2.33.0",
|
||||
"eslint": "^7.0.0",
|
||||
|
||||
@@ -29,6 +29,7 @@ class LikeActions extends Actions.Implementation<
|
||||
if (
|
||||
lobby.game.round.verifyStage<Round.Revealing | Round.Judging>(
|
||||
action,
|
||||
"Revealing",
|
||||
"Judging"
|
||||
)
|
||||
) {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import * as Play from "../../games/cards/play";
|
||||
import * as StartRevealing from "./start-revealing";
|
||||
import * as Card from "../../games/cards/card";
|
||||
|
||||
/**
|
||||
* Indicates the czar has finished revealing the plays and is now picking a winner.
|
||||
*/
|
||||
export interface StartJudging {
|
||||
export interface StartJudging extends StartRevealing.AfterPlaying {
|
||||
event: "StartJudging";
|
||||
/**
|
||||
* The plays that are to be judged. If the revealing stage was played, this won't be included as the data will have
|
||||
@@ -12,7 +14,13 @@ export interface StartJudging {
|
||||
plays?: Play.Revealed[];
|
||||
}
|
||||
|
||||
export const of = (plays?: Play.Revealed[]): StartJudging => ({
|
||||
export const of = (
|
||||
plays?: Play.Revealed[],
|
||||
played?: Play.Id,
|
||||
drawn?: Card.Response[]
|
||||
): StartJudging => ({
|
||||
event: "StartJudging",
|
||||
plays,
|
||||
played,
|
||||
drawn,
|
||||
});
|
||||
|
||||
@@ -5,17 +5,32 @@ import * as Card from "../../games/cards/card";
|
||||
* Indicates players have finished playing into the round and now the czar
|
||||
* should reveal the plays.
|
||||
*/
|
||||
export interface StartRevealing {
|
||||
export interface StartRevealing extends AfterPlaying {
|
||||
event: "StartRevealing";
|
||||
plays: Play.Id[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Details in an event after finishing playing.
|
||||
*/
|
||||
export interface AfterPlaying {
|
||||
/**
|
||||
* The id of the play the user receiving this event played, if they did play one.
|
||||
*/
|
||||
played?: Play.Id;
|
||||
/**
|
||||
* The cards drawn by the player receiving this event.
|
||||
*/
|
||||
drawn?: Card.Response[];
|
||||
}
|
||||
|
||||
export const of = (
|
||||
plays: Play.Id[],
|
||||
played?: Play.Id,
|
||||
drawn?: Card.Response[]
|
||||
): StartRevealing => ({
|
||||
event: "StartRevealing",
|
||||
plays,
|
||||
...(drawn !== undefined ? { drawn } : {}),
|
||||
played,
|
||||
drawn,
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as Card from "../../games/cards/card";
|
||||
import { Hand } from "../../games/cards/hand";
|
||||
import * as Lobby from "../../lobby";
|
||||
import { LikeDetail } from "../../games/game/round/public";
|
||||
|
||||
/**
|
||||
* Synchronise the game state.
|
||||
@@ -10,17 +11,20 @@ export interface Sync {
|
||||
state: Lobby.Public;
|
||||
hand?: Hand;
|
||||
play?: Card.Id[];
|
||||
likeDetail?: LikeDetail;
|
||||
gameTime: number;
|
||||
}
|
||||
|
||||
export const of = (
|
||||
state: Lobby.Public,
|
||||
hand?: Hand,
|
||||
play?: Card.Id[]
|
||||
play?: Card.Id[],
|
||||
likeDetail?: LikeDetail
|
||||
): Sync => ({
|
||||
event: "Sync",
|
||||
state,
|
||||
...(hand !== undefined ? { hand } : {}),
|
||||
...(play !== undefined ? { play } : {}),
|
||||
...(likeDetail !== undefined ? { likeDetail } : {}),
|
||||
gameTime: Date.now(),
|
||||
});
|
||||
|
||||
@@ -11,7 +11,9 @@ import * as RoundStageTimerDone from "../../timeout/round-stage-timer-done";
|
||||
import * as Timeout from "../../timeout";
|
||||
import * as Event from "../../event";
|
||||
import * as StartJudging from "../../events/game-event/start-judging";
|
||||
import * as StartRevealing from "../../events/game-event/start-revealing";
|
||||
import * as Rules from "../rules";
|
||||
import * as Game from "../game";
|
||||
|
||||
export type Round = Playing | Revealing | Judging | Complete;
|
||||
|
||||
@@ -45,12 +47,13 @@ export abstract class Base<TStage extends Stage> {
|
||||
*/
|
||||
public verifyStage<TRound extends Round>(
|
||||
action: Action,
|
||||
expected: TRound["stage"]
|
||||
...expected: TRound["stage"][]
|
||||
): this is TRound {
|
||||
if (this.stage !== expected) {
|
||||
throw new IncorrectRoundStageError(action, this.stage, expected);
|
||||
if (expected.some((n) => n == this.stage)) {
|
||||
return true;
|
||||
} else {
|
||||
throw new IncorrectRoundStageError(action, this.stage, ...expected);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +185,8 @@ export class Judging extends Base<"Judging"> implements Timed {
|
||||
|
||||
public start(
|
||||
rules: Rules.Rules,
|
||||
previouslyRevealed: boolean
|
||||
previouslyRevealed: boolean,
|
||||
newCardsAndPlayedByPlayer: Map<User.Id, StartRevealing.AfterPlaying>
|
||||
): {
|
||||
timeouts?: Iterable<Timeout.After>;
|
||||
events?: Iterable<Event.Distributor>;
|
||||
@@ -191,7 +195,15 @@ export class Judging extends Base<"Judging"> implements Timed {
|
||||
const plays = previouslyRevealed
|
||||
? undefined
|
||||
: Array.from(this.revealedPlays());
|
||||
const event = Event.targetAll(StartJudging.of(plays));
|
||||
if (previouslyRevealed) {
|
||||
for (const [_, v] of newCardsAndPlayedByPlayer) {
|
||||
delete v.played;
|
||||
}
|
||||
}
|
||||
const event = Event.additionally(
|
||||
StartJudging.of(plays),
|
||||
newCardsAndPlayedByPlayer
|
||||
);
|
||||
return {
|
||||
timeouts: Util.asOptionalIterable(timeout),
|
||||
events: Util.asOptionalIterable(event),
|
||||
@@ -271,9 +283,30 @@ export class Revealing extends Base<"Revealing"> implements Timed {
|
||||
this.timedOut = timedOut;
|
||||
}
|
||||
|
||||
public start(
|
||||
game: Game.Game
|
||||
): {
|
||||
events?: Iterable<Event.Distributor>;
|
||||
timeouts?: Iterable<Timeout.After>;
|
||||
} {
|
||||
const playsToBeRevealed = Array.from(
|
||||
wu(game.round.plays).map((play) => play.id)
|
||||
);
|
||||
const events = Util.asOptionalIterable(
|
||||
Event.additionally(
|
||||
StartRevealing.of(playsToBeRevealed),
|
||||
this.getAfterPlayingDetails(game)
|
||||
)
|
||||
);
|
||||
const timeouts = Util.asOptionalIterable(
|
||||
RoundStageTimerDone.ifEnabled(game.round, game.rules.stages)
|
||||
);
|
||||
return { events, timeouts };
|
||||
}
|
||||
|
||||
public advance(
|
||||
rules: Rules.Rules,
|
||||
previouslyRevealed = true
|
||||
game: Game.Game,
|
||||
previouslyRevealed: boolean
|
||||
):
|
||||
| {
|
||||
round: Judging;
|
||||
@@ -283,7 +316,11 @@ export class Revealing extends Base<"Revealing"> implements Timed {
|
||||
| undefined {
|
||||
if (StoredPlay.allRevealed(this)) {
|
||||
const judging = new Judging(this.id, this.czar, this.call, this.plays);
|
||||
const start = judging.start(rules, previouslyRevealed);
|
||||
const start = judging.start(
|
||||
game.rules,
|
||||
previouslyRevealed,
|
||||
this.getAfterPlayingDetails(game)
|
||||
);
|
||||
return {
|
||||
round: judging,
|
||||
...start,
|
||||
@@ -293,6 +330,36 @@ export class Revealing extends Base<"Revealing"> implements Timed {
|
||||
}
|
||||
}
|
||||
|
||||
private getAfterPlayingDetails(
|
||||
game: Game.Game
|
||||
): Map<User.Id, StartRevealing.AfterPlaying> {
|
||||
const slotCount = Card.slotCount(game.round.call);
|
||||
const extraCards =
|
||||
slotCount > 2 ||
|
||||
(slotCount === 2 && game.rules.houseRules.packingHeat !== undefined)
|
||||
? slotCount - 1
|
||||
: 0;
|
||||
const newCardsAndPlayedByPlayer = new Map<
|
||||
User.Id,
|
||||
StartRevealing.AfterPlaying
|
||||
>();
|
||||
for (const play of game.round.plays) {
|
||||
const idSet = new Set(play.play.map((c) => c.id));
|
||||
const player = game.players[play.playedBy];
|
||||
if (player !== undefined) {
|
||||
player.hand = player.hand.filter((card) => !idSet.has(card.id));
|
||||
const toDraw = play.play.length - extraCards;
|
||||
const drawn = game.decks.responses.draw(toDraw);
|
||||
newCardsAndPlayedByPlayer.set(play.playedBy, {
|
||||
drawn,
|
||||
played: play.id,
|
||||
});
|
||||
player.hand.push(...drawn);
|
||||
}
|
||||
}
|
||||
return newCardsAndPlayedByPlayer;
|
||||
}
|
||||
|
||||
public waitingFor(): Set<User.Id> | null {
|
||||
return new Set(this.czar);
|
||||
}
|
||||
@@ -363,31 +430,42 @@ export class Playing extends Base<"Playing"> implements Timed {
|
||||
this.timedOut = timedOut;
|
||||
}
|
||||
|
||||
public advance(): Revealing {
|
||||
return new Revealing(
|
||||
public advance(
|
||||
game: Game.Game,
|
||||
doNotStart = false
|
||||
): {
|
||||
round: Revealing;
|
||||
events?: Iterable<Event.Distributor>;
|
||||
timeouts?: Iterable<Timeout.After>;
|
||||
} {
|
||||
const revealing = new Revealing(
|
||||
this.id,
|
||||
this.czar,
|
||||
this.call,
|
||||
Util.shuffled(this.plays)
|
||||
);
|
||||
return {
|
||||
round: revealing,
|
||||
...(doNotStart ? {} : revealing.start(game)),
|
||||
};
|
||||
}
|
||||
|
||||
public skipToJudging(
|
||||
rules: Rules.Rules
|
||||
game: Game.Game
|
||||
): {
|
||||
round: Judging;
|
||||
timeouts?: Iterable<Timeout.After>;
|
||||
events?: Iterable<Event.Distributor>;
|
||||
} {
|
||||
const revealing = this.advance();
|
||||
for (const play of revealing.plays) {
|
||||
const advanceRevealing = this.advance(game, true);
|
||||
for (const play of advanceRevealing.round.plays) {
|
||||
play.revealed = true;
|
||||
}
|
||||
const advance = revealing.advance(rules, false);
|
||||
if (advance === undefined) {
|
||||
const advanceJudging = advanceRevealing.round.advance(game, false);
|
||||
if (advanceJudging === undefined) {
|
||||
throw new Error("All plays should have been revealed automatically.");
|
||||
}
|
||||
return advance;
|
||||
return advanceJudging;
|
||||
}
|
||||
|
||||
public waitingFor(): Set<User.Id> | null {
|
||||
|
||||
@@ -23,6 +23,11 @@ export interface Playing extends Base, Timed {
|
||||
played: User.Id[];
|
||||
}
|
||||
|
||||
export interface LikeDetail {
|
||||
liked: Play.Id[];
|
||||
played?: Play.Id;
|
||||
}
|
||||
|
||||
export interface Revealing extends Base, Timed {
|
||||
stage: "Revealing";
|
||||
plays: Play.PotentiallyRevealed[];
|
||||
|
||||
@@ -139,6 +139,7 @@ export class SocketManager {
|
||||
await Change.apply(server, auth.gc, (lobby) => {
|
||||
let hand = undefined;
|
||||
let play = undefined;
|
||||
let likeDetail = undefined;
|
||||
if (lobby.game !== undefined) {
|
||||
const player = lobby.game.players[uid];
|
||||
if (player !== undefined) {
|
||||
@@ -150,6 +151,16 @@ export class SocketManager {
|
||||
if (potentialPlay !== undefined) {
|
||||
play = potentialPlay.play.map((card) => card.id);
|
||||
}
|
||||
const round = lobby.game.round;
|
||||
const stage = round.stage;
|
||||
if (stage === "Revealing" || stage === "Judging") {
|
||||
const liked = round.plays
|
||||
.filter((p) => p.likes.some((l) => l === uid))
|
||||
.map((p) => p.id);
|
||||
const played = round.plays.find((p) => p.playedBy === uid)
|
||||
?.id;
|
||||
likeDetail = { played, liked };
|
||||
}
|
||||
}
|
||||
|
||||
const user = lobby.users[uid];
|
||||
@@ -158,7 +169,7 @@ export class SocketManager {
|
||||
lobby,
|
||||
events: [
|
||||
Event.targetOnly(
|
||||
Sync.of(Lobby.censor(lobby), hand, play),
|
||||
Sync.of(Lobby.censor(lobby), hand, play, likeDetail),
|
||||
uid
|
||||
),
|
||||
],
|
||||
|
||||
@@ -7,6 +7,8 @@ import * as Timeout from "../timeout";
|
||||
import * as Util from "../util";
|
||||
import * as RoundStageTimerDone from "./round-stage-timer-done";
|
||||
import * as Rules from "../games/rules";
|
||||
import * as User from "../user";
|
||||
import * as Game from "../games/game";
|
||||
/**
|
||||
* Indicates that the round should start the revealing phase if it is appropriate
|
||||
* to do so.
|
||||
@@ -49,59 +51,18 @@ export const handle: Timeout.Handler<FinishedPlaying> = (
|
||||
|
||||
// We discard the plays first so if the deck runs out, all the cards get
|
||||
// rotated in.
|
||||
const responses = game.decks.responses;
|
||||
for (const play of game.round.plays) {
|
||||
responses.discard(play.play);
|
||||
game.decks.responses.discard(play.play);
|
||||
}
|
||||
|
||||
const slotCount = Card.slotCount(game.round.call);
|
||||
const extraCards =
|
||||
slotCount > 2 ||
|
||||
(slotCount === 2 && game.rules.houseRules.packingHeat !== undefined)
|
||||
? slotCount - 1
|
||||
: 0;
|
||||
const newCardsByPlayer = new Map();
|
||||
for (const play of game.round.plays) {
|
||||
const idSet = new Set(play.play.map((c) => c.id));
|
||||
const player = game.players[play.playedBy];
|
||||
if (player !== undefined) {
|
||||
player.hand = player.hand.filter((card) => !idSet.has(card.id));
|
||||
const toDraw = play.play.length - extraCards;
|
||||
const drawn = responses.draw(toDraw);
|
||||
newCardsByPlayer.set(play.playedBy, { drawn });
|
||||
player.hand.push(...drawn);
|
||||
}
|
||||
}
|
||||
|
||||
if (game.rules.stages.revealing === undefined) {
|
||||
const { round, events, timeouts } = game.round.skipToJudging(game.rules);
|
||||
game.round = round;
|
||||
return {
|
||||
lobby,
|
||||
events,
|
||||
timeouts,
|
||||
};
|
||||
} else {
|
||||
game.round = game.round.advance();
|
||||
|
||||
const playsToBeRevealed = Array.from(
|
||||
wu(game.round.plays).map((play) => play.id)
|
||||
);
|
||||
const events = [
|
||||
Event.additionally(
|
||||
StartRevealing.of(playsToBeRevealed),
|
||||
newCardsByPlayer
|
||||
),
|
||||
];
|
||||
|
||||
const timeouts = Util.asOptionalIterable(
|
||||
RoundStageTimerDone.ifEnabled(game.round, game.rules.stages)
|
||||
);
|
||||
|
||||
return {
|
||||
lobby,
|
||||
events: events,
|
||||
timeouts: timeouts,
|
||||
};
|
||||
}
|
||||
const { round, events, timeouts } =
|
||||
game.rules.stages.revealing === undefined
|
||||
? game.round.skipToJudging(game)
|
||||
: game.round.advance(game);
|
||||
game.round = round;
|
||||
return {
|
||||
lobby,
|
||||
events,
|
||||
timeouts,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@ export const handle: Timeout.Handler<FinishedRevealing> = (
|
||||
if (round.stage !== "Revealing") {
|
||||
return {};
|
||||
}
|
||||
const advanced = round.advance(game.rules);
|
||||
const advanced = round.advance(game, true);
|
||||
if (advanced === undefined) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user