From 555914e0ec447bc1aa1309d31a7ac2fbfe28aae2 Mon Sep 17 00:00:00 2001 From: Gareth Latty Date: Fri, 11 Dec 2015 00:48:01 +0000 Subject: [PATCH] Work on #38 - Cards show when players play. Still not quite happy with the effect. --- client/src/MassiveDecks/Actions/Action.elm | 1 + client/src/MassiveDecks/Config.elm | 4 +- client/src/MassiveDecks/Main.elm | 3 +- client/src/MassiveDecks/Models/State.elm | 6 ++ client/src/MassiveDecks/Playing.elm | 91 ++++++++++++++++--- client/src/MassiveDecks/Start.elm | 2 +- client/src/MassiveDecks/UI/Playing.elm | 19 ++-- client/src/MassiveDecks/Util.elm | 18 +++- server/app/assets/stylesheets/lobby.less | 9 ++ .../app/views/massivedecks/index.scala.html | 3 +- 10 files changed, 128 insertions(+), 28 deletions(-) diff --git a/client/src/MassiveDecks/Actions/Action.elm b/client/src/MassiveDecks/Actions/Action.elm index 3f5bbcb..2d3fbd5 100644 --- a/client/src/MassiveDecks/Actions/Action.elm +++ b/client/src/MassiveDecks/Actions/Action.elm @@ -27,3 +27,4 @@ type Action | RemoveErrorPanel Int | NextRound | SetInitialState InitialState + | AnimatePlayedCards (List Int) diff --git a/client/src/MassiveDecks/Config.elm b/client/src/MassiveDecks/Config.elm index 7aba981..a3230d1 100644 --- a/client/src/MassiveDecks/Config.elm +++ b/client/src/MassiveDecks/Config.elm @@ -33,7 +33,7 @@ update action global data = case action of (model global data, (API.newGame data.lobby.id data.secret) |> Task.map (StartGame << Result) |> API.toEffect) StartGame (Result lobbyAndHand) -> - (Playing.model global (PlayingData lobbyAndHand.lobby lobbyAndHand.hand data.secret [] Nothing), Effects.none) + (Playing.model global (PlayingData lobbyAndHand.lobby lobbyAndHand.hand data.secret [] Nothing []), Effects.none) Notification lobby -> case lobby.round of @@ -45,7 +45,7 @@ update action global data = case action of JoinLobby lobbyId secret (Result lobbyAndHand) -> case lobbyAndHand.lobby.round of - Just _ -> (Playing.model global (PlayingData lobbyAndHand.lobby lobbyAndHand.hand secret [] Nothing), Effects.none) + Just _ -> (Playing.model global (PlayingData lobbyAndHand.lobby lobbyAndHand.hand secret [] Nothing []), Effects.none) Nothing -> (model global { data | lobby = lobbyAndHand.lobby }, Effects.none) other -> diff --git a/client/src/MassiveDecks/Main.elm b/client/src/MassiveDecks/Main.elm index 4002156..876376a 100644 --- a/client/src/MassiveDecks/Main.elm +++ b/client/src/MassiveDecks/Main.elm @@ -5,6 +5,7 @@ import Effects import Html exposing (Html) import StartApp import Json.Decode exposing (decodeString) +import Random import MassiveDecks.Models.State exposing (Model, State(..), LobbyIdAndSecret, Error, Global, InitialState) import MassiveDecks.Actions.Action exposing (Action(..), APICall(..)) @@ -72,7 +73,7 @@ main = game.html start : InitialState -> (Model, Effects.Effects Action) start initialState = - (Start.model (Global [] initialState) (Start.initialData (Maybe.withDefault "" initialState.gameCode)), + (Start.model (Global [] initialState (Random.initialSeed initialState.seed)) (Start.initialData (Maybe.withDefault "" initialState.gameCode)), case initialState.existingGame of Just existingGame -> if (Just existingGame.lobbyId == initialState.gameCode) then diff --git a/client/src/MassiveDecks/Models/State.elm b/client/src/MassiveDecks/Models/State.elm index b0ef0f0..1b33c46 100644 --- a/client/src/MassiveDecks/Models/State.elm +++ b/client/src/MassiveDecks/Models/State.elm @@ -1,5 +1,8 @@ module MassiveDecks.Models.State where +import Random +import Html exposing (Attribute) + import MassiveDecks.Models.Card exposing (Hand) import MassiveDecks.Models.Game exposing (Config, Lobby, Round) import MassiveDecks.Models.Player exposing (Player, Secret) @@ -15,6 +18,7 @@ type alias Model = type alias Global = { errors : List Error , initialState : InitialState + , seed : Random.Seed } @@ -43,6 +47,7 @@ type alias PlayingData = , secret : Secret , picked : List Int , lastFinishedRound : Maybe Round + , shownPlayed : List Attribute } @@ -56,6 +61,7 @@ type alias InitialState = { url : String , gameCode : Maybe String , existingGame : Maybe LobbyIdAndSecret + , seed : Int } diff --git a/client/src/MassiveDecks/Playing.elm b/client/src/MassiveDecks/Playing.elm index f4c89b8..1c7d0cc 100644 --- a/client/src/MassiveDecks/Playing.elm +++ b/client/src/MassiveDecks/Playing.elm @@ -1,8 +1,11 @@ module MassiveDecks.Playing where +import Time import Task import Effects -import Html exposing (Html) +import Html exposing (Html, Attribute) +import Html.Attributes exposing (style) +import Random exposing (Generator, Seed, list, bool, int) import MassiveDecks.Models.Player exposing (Secret) import MassiveDecks.Models.Card as Card @@ -11,6 +14,7 @@ import MassiveDecks.Models.State exposing (State(..), Model, ConfigData, Playing import MassiveDecks.Actions.Action exposing (Action(..), APICall(..)) import MassiveDecks.UI.Playing as UI import MassiveDecks.API as API +import MassiveDecks.Util as Util update : Action -> Global -> PlayingData -> (Model, Effects.Effects Action) @@ -36,21 +40,29 @@ update action global data = case action of Play (Result lobbyAndHand) -> let - data = (updateLobby data lobbyAndHand.lobby) + (data, effect, seed) = (updateLobby data lobbyAndHand.lobby global.seed) in - (model global + (model { global | seed = seed } { data | hand = lobbyAndHand.hand , picked = [] - }, Effects.none) + }, effect) Notification lobby -> case lobby.round of - Just _ -> (model global (updateLobby data lobby), Effects.none) + Just _ -> + let + (data, effect, seed) = (updateLobby data lobby global.seed) + in + (model { global | seed = seed } data, effect) Nothing -> (configModel global (ConfigData lobby data.secret ""), Effects.none) JoinLobby lobbyId secret (Result lobbyAndHand) -> case lobbyAndHand.lobby.round of - Just _ -> (model global (updateLobby data lobbyAndHand.lobby), Effects.none) + Just _ -> + let + (data, effect, seed) = (updateLobby data lobbyAndHand.lobby global.seed) + in + (model { global | seed = seed } data, effect) Nothing -> (configModel global (ConfigData lobbyAndHand.lobby data.secret ""), Effects.none) Choose winner Request -> @@ -58,16 +70,22 @@ update action global data = case action of Choose winner (Result lobbyAndHand) -> let - data = (updateLobby data lobbyAndHand.lobby) + (data, effect, seed) = (updateLobby data lobbyAndHand.lobby global.seed) in - (model global + (model { global | seed = seed } { data | hand = lobbyAndHand.hand , picked = [] - }, Effects.none) + }, effect) NextRound -> (model global { data | lastFinishedRound = Nothing }, Effects.none) + AnimatePlayedCards toAnimate -> + let + (shownPlayed, seed) = updatePositioning toAnimate data.shownPlayed global.seed + in + (model { global | seed = seed } { data | shownPlayed = (Debug.log "shownPlayed" shownPlayed) }, Effects.none) + other -> (model global data, DisplayError ("Got an action (" ++ (toString other) ++ ") that can't be handled in the current state (Playing).") @@ -75,17 +93,62 @@ update action global data = case action of |> Effects.task) -updateLobby : PlayingData -> Lobby -> PlayingData -updateLobby data lobby = +updateLobby : PlayingData -> Lobby -> Seed -> (PlayingData, Effects.Effects Action, Seed) +updateLobby data lobby seed = let lastFinishedRound = if (Maybe.map .call data.lobby.round) == (Maybe.map .call lobby.round) then data.lastFinishedRound else data.lobby.round + oldShownPlayed = data.shownPlayed + oldShownPlayedLength = (List.length oldShownPlayed) + (shownPlayed, effects, resultSeed) = case Maybe.map .responses data.lobby.round of + Just (Card.Hidden amount) -> + let + (newShownPlayed, newSeed) = + Random.generate (list (amount - oldShownPlayedLength) initialRandomPositioning) seed + in + (List.concat [ oldShownPlayed, newShownPlayed ] + ,(Task.sleep (Time.millisecond * 250) + `Task.andThen` + \_ -> (Task.succeed (AnimatePlayedCards (Util.range oldShownPlayedLength (List.length newShownPlayed))))) + |> Effects.task + ,newSeed) + _ -> ([], Effects.none, seed) in - { data | lobby = lobby - , lastFinishedRound = lastFinishedRound - } + ({ data | lobby = lobby + , lastFinishedRound = lastFinishedRound + , shownPlayed = shownPlayed + }, effects, resultSeed) + + +updatePositioning : List Int -> List Attribute -> Seed -> (List Attribute, Seed) +updatePositioning toAnimate existing seed = + let + generator = (\index val -> if (List.member index toAnimate) then randomPositioning else Random.map (\_ -> val) bool) + generators = List.indexedMap generator (Debug.log "existing" existing) + in + Random.generate (Util.inOrder generators) seed + + +randomPositioning : Generator Attribute +randomPositioning = Random.map4 positioning (int -75 75) (int 0 50) bool (int -5 5) + + +initialRandomPositioning : Generator Attribute +initialRandomPositioning = Random.map3 (\r h l -> positioning r h l -100) (int -75 75) (int 0 50) bool + + +positioning : Int -> Int -> Bool -> Int -> Attribute +positioning rotation horizontalPos left verticalPos = + let + horizontalDirection = if left then "left" else "right" + in + style + [ ("transform", "rotate(" ++ (toString rotation) ++ "deg)") + , (horizontalDirection, (toString horizontalPos) ++ "%") + , ("top", (toString verticalPos) ++ "%") + ] model : Global -> PlayingData -> Model diff --git a/client/src/MassiveDecks/Start.elm b/client/src/MassiveDecks/Start.elm index 5608a4c..eb05d18 100644 --- a/client/src/MassiveDecks/Start.elm +++ b/client/src/MassiveDecks/Start.elm @@ -45,7 +45,7 @@ update action global data = case action of JoinLobby lobbyId secret (Result lobbyAndHand) -> case lobbyAndHand.lobby.round of - Just _ -> (Playing.modelSub global lobbyId secret (PlayingData lobbyAndHand.lobby lobbyAndHand.hand secret [] Nothing), Effects.none) + Just _ -> (Playing.modelSub global lobbyId secret (PlayingData lobbyAndHand.lobby lobbyAndHand.hand secret [] Nothing []), Effects.none) Nothing -> (Config.modelSub global lobbyId secret (Config.initialData lobbyAndHand.lobby secret), Effects.none) other -> diff --git a/client/src/MassiveDecks/UI/Playing.elm b/client/src/MassiveDecks/UI/Playing.elm index 2335a3e..6dfeaa9 100644 --- a/client/src/MassiveDecks/UI/Playing.elm +++ b/client/src/MassiveDecks/UI/Playing.elm @@ -41,11 +41,10 @@ roundContents address data round = picked = List.map snd pickedWithIndex isCzar = round.czar == data.secret.id pickedOrPlayed = case round.responses of - Revealed responses -> playedView address isCzar responses - Hidden others -> pickedView address pickedWithIndex (Card.slots round.call) + Revealed responses -> [ playedView address isCzar responses ] + Hidden _ -> pickedView address pickedWithIndex (Card.slots round.call) data.shownPlayed in - [ playArea [ call round.call picked, pickedOrPlayed] - , handView address data.picked isCzar hand ] + [ playArea (List.concat [ [ call round.call picked ], pickedOrPlayed, [ handView address data.picked isCzar hand ] ]) ] winnerContentsAndHeader : Signal.Address Action -> Round -> List Player -> (List Html, List Html) @@ -113,6 +112,10 @@ response address picked isCzar responseId contents = div (List.concat [ classes, clickHandler ]) [ div [ class "response-text" ] [ text contents ] ] +blankResponse : Attribute -> Html +blankResponse positioning = div [ class "card mui-panel", positioning ] [] + + handRender : Bool -> List Html -> Html handRender isCzar contents = let @@ -130,12 +133,14 @@ pickedResponse address (index, contents) = li [ onClick address (Withdraw index) ] [ div [ class "card response mui-panel" ] [ div [ class "response-text" ] [ text contents ] ] ] -pickedView : Signal.Address Action -> List (Int, Response) -> Int -> Html -pickedView address picked slots = +pickedView : Signal.Address Action -> List (Int, Response) -> Int -> List Attribute -> List Html +pickedView address picked slots shownPlayed = let pb = if (List.length picked < slots) then [] else [ playButton address ] in - ol [ class "picked" ] (List.concat [ (List.map (pickedResponse address) picked), pb ]) + [ ol [ class "picked" ] (List.concat [ (List.map (pickedResponse address) picked), pb ]) + , div [ class "others-picked" ] (List.map blankResponse shownPlayed) + ] playButton : Signal.Address Action -> Html diff --git a/client/src/MassiveDecks/Util.elm b/client/src/MassiveDecks/Util.elm index 3fa1a35..b30c960 100644 --- a/client/src/MassiveDecks/Util.elm +++ b/client/src/MassiveDecks/Util.elm @@ -1,5 +1,7 @@ module MassiveDecks.Util where +import Random exposing (Generator) + interleave : List a -> List a -> List a interleave list1 list2 = @@ -21,10 +23,10 @@ remove list index = get : List a -> Int -> a -get list index = case List.drop index list of +get list index = case List.drop index list of [] -> Native.Error.raise <| "Attempted to take element " ++ toString index ++ " of list " ++ toString list - (item::_) -> item + (item::_) -> item getAll : List a -> List Int -> List a @@ -35,3 +37,15 @@ getAll list indices = getAllWithIndex : List a -> List Int -> List (Int, a) getAllWithIndex list indices = getAll (List.indexedMap (,) list) indices + + +range : Int -> Int -> List Int +range first count = case count of + 0 -> [] + _ -> first :: range (first + 1) (count - 1) + + +inOrder : List (Generator a) -> Generator (List a) +inOrder generators = case generators of + [] -> Random.map (\_ -> []) Random.bool + head :: tail -> head `Random.andThen` (\value -> Random.map ((::) value) (inOrder tail)) diff --git a/server/app/assets/stylesheets/lobby.less b/server/app/assets/stylesheets/lobby.less index 5e7e229..15465d4 100644 --- a/server/app/assets/stylesheets/lobby.less +++ b/server/app/assets/stylesheets/lobby.less @@ -23,6 +23,15 @@ display: flex; flex-wrap: wrap; justify-content: center; + position: relative; + + .others-picked { + .card { + position: absolute; + z-index: -10; + transition: transform 3s, left 3s, right 3s, top 3s ease-out + } + } } .call { diff --git a/server/app/views/massivedecks/index.scala.html b/server/app/views/massivedecks/index.scala.html index 01e6696..1e99a38 100644 --- a/server/app/views/massivedecks/index.scala.html +++ b/server/app/views/massivedecks/index.scala.html @@ -29,7 +29,8 @@ var initialState = { url: "@url", gameCode: gameCode == "" ? null : gameCode, - existingGame: existingGame + existingGame: existingGame, + seed: new Date().getTime() }; var game = Elm.fullscreen(Elm.MassiveDecks.Main, { notifications: "", initialState: initialState });