Compare commits
9 commits
main
...
APILanguag
Author | SHA1 | Date | |
---|---|---|---|
5c9e0c9061 | |||
3e7c0a88f1 | |||
539b74990e | |||
bcaa606021 | |||
d2b8c2ee47 | |||
fbad40afdc | |||
e3f4ce697d | |||
9d15a80ff5 | |||
d54d97b84d |
17 changed files with 91 additions and 133 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,3 +1 @@
|
||||||
/dist*
|
/dist/*
|
||||||
cabal.project.local
|
|
||||||
.ghc*
|
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
# Revision history for hanafudapi
|
# Revision history for hanafudapi
|
||||||
|
|
||||||
## 0.2.3.0 -- 2019-08-24
|
|
||||||
|
|
||||||
* Huge refactoring to use the new APILanguage that basically vampirized Game module which become more of a toolbox for the Automaton
|
|
||||||
* Fix a couple race conditions in JS client and server encountered when developping and testing Hannah the bot soon to come
|
|
||||||
|
|
||||||
## 0.2.2.0 -- 2019-08-12
|
## 0.2.2.0 -- 2019-08-12
|
||||||
|
|
||||||
* Handle the end of games
|
* Handle the end of games
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
-- documentation, see http://haskell.org/cabal/users-guide/
|
-- documentation, see http://haskell.org/cabal/users-guide/
|
||||||
|
|
||||||
name: hanafuda-webapp
|
name: hanafuda-webapp
|
||||||
version: 0.2.3.0
|
version: 0.2.2.0
|
||||||
synopsis: A webapp for the Haskell hanafuda library
|
synopsis: A webapp for the Haskell hanafuda library
|
||||||
-- description:
|
-- description:
|
||||||
homepage: https://git.marvid.fr/hanafuda
|
homepage: https://framagit.org/hanafuda
|
||||||
license: BSD3
|
license: BSD3
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: Tissevert
|
author: Sasha
|
||||||
maintainer: tissevert+devel@marvid.fr
|
maintainer: sasha+frama@marvid.fr
|
||||||
-- copyright:
|
-- copyright:
|
||||||
category: Web
|
category: Web
|
||||||
build-type: Simple
|
build-type: Simple
|
||||||
|
@ -17,7 +17,7 @@ extra-source-files: ChangeLog.md
|
||||||
cabal-version: >=1.10
|
cabal-version: >=1.10
|
||||||
source-repository head
|
source-repository head
|
||||||
type: git
|
type: git
|
||||||
location: https://git.marvid.fr/hanafuda/webapp
|
location: https://framagit.org/hanafuda/api
|
||||||
|
|
||||||
executable hanafudapi
|
executable hanafudapi
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
|
@ -26,7 +26,7 @@ executable hanafudapi
|
||||||
, Config
|
, Config
|
||||||
, Messaging
|
, Messaging
|
||||||
, Game
|
, Game
|
||||||
, RW
|
, Data
|
||||||
, Server
|
, Server
|
||||||
, Session
|
, Session
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
|
@ -34,8 +34,8 @@ executable hanafudapi
|
||||||
, bytestring
|
, bytestring
|
||||||
, containers >= 0.5.9
|
, containers >= 0.5.9
|
||||||
, unordered-containers
|
, unordered-containers
|
||||||
, hanafuda >= 0.3.3
|
, hanafuda >= 0.3.0
|
||||||
, hanafuda-APILanguage >= 0.1.0
|
, hanafuda-APILanguage
|
||||||
, http-types
|
, http-types
|
||||||
, aeson
|
, aeson
|
||||||
, mtl
|
, mtl
|
||||||
|
|
|
@ -12,13 +12,13 @@ module App (
|
||||||
, update_
|
, update_
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Data.Map ((!))
|
||||||
import Control.Concurrent (MVar, modifyMVar, putMVar, readMVar, takeMVar)
|
import Control.Concurrent (MVar, modifyMVar, putMVar, readMVar, takeMVar)
|
||||||
import Control.Monad.Reader (ReaderT(..), ask, asks, lift)
|
import Control.Monad.Reader (ReaderT(..), ask, asks, lift)
|
||||||
import Data.Map ((!))
|
|
||||||
import Hanafuda.KoiKoi (PlayerID)
|
|
||||||
import Network.WebSockets (Connection)
|
import Network.WebSockets (Connection)
|
||||||
import qualified Server (T(..))
|
import Hanafuda.KoiKoi (PlayerID)
|
||||||
import qualified Session (T(..))
|
import qualified Session (T(..))
|
||||||
|
import qualified Server (T(..))
|
||||||
|
|
||||||
data Context = Context {
|
data Context = Context {
|
||||||
mServer :: MVar Server.T
|
mServer :: MVar Server.T
|
||||||
|
|
|
@ -3,20 +3,20 @@ module Automaton (
|
||||||
start
|
start
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified App (Context(..), T, current, debug, get, server, try, update_)
|
|
||||||
import Control.Monad.Reader (asks)
|
import Control.Monad.Reader (asks)
|
||||||
|
import qualified Data (RW(..))
|
||||||
import Data.Map (Map, (!?))
|
import Data.Map (Map, (!?))
|
||||||
import qualified Game (new, play)
|
import qualified Game (new, play)
|
||||||
import qualified Hanafuda.KoiKoi as KoiKoi (
|
import qualified Hanafuda.KoiKoi as KoiKoi (
|
||||||
Game, GameBlueprint(..), GameID, Step(..)
|
Game, GameBlueprint(..), GameID, Step(..)
|
||||||
)
|
)
|
||||||
|
import qualified Session (Status(..), T(..), Update)
|
||||||
|
import qualified Server (endGame, get, logIn, logOut, update, room)
|
||||||
|
import qualified App (Context(..), T, current, debug, get, server, try, update_)
|
||||||
import qualified Hanafuda.Message as Message (FromClient(..), T(..))
|
import qualified Hanafuda.Message as Message (FromClient(..), T(..))
|
||||||
import qualified Messaging (
|
import qualified Messaging (
|
||||||
broadcast, get, notifyPlayers, relay, send, sendTo, update
|
broadcast, get, notifyPlayers, relay, send, sendTo, update
|
||||||
)
|
)
|
||||||
import qualified RW (RW(..))
|
|
||||||
import qualified Server (endGame, get, logIn, logOut, update, room)
|
|
||||||
import qualified Session (Status(..), T(..), Update)
|
|
||||||
|
|
||||||
receive :: Session.Status -> Message.FromClient -> App.T ()
|
receive :: Session.Status -> Message.FromClient -> App.T ()
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ receive (Session.LoggedIn True) invitation@(Message.Invitation {Message.to}) = d
|
||||||
case Session.status session of
|
case Session.status session of
|
||||||
Session.LoggedIn True -> do
|
Session.LoggedIn True -> do
|
||||||
from <- asks App.playerID
|
from <- asks App.playerID
|
||||||
App.update_ (Server.update to (RW.set $ Session.Answering from :: Session.Update))
|
App.update_ (Server.update to (Data.set $ Session.Answering from :: Session.Update))
|
||||||
Messaging.broadcast $ Messaging.update {Message.paired = [from, to]}
|
Messaging.broadcast $ Messaging.update {Message.paired = [from, to]}
|
||||||
(Messaging.relay invitation $ Messaging.sendTo [to])
|
(Messaging.relay invitation $ Messaging.sendTo [to])
|
||||||
setSessionStatus (Session.Waiting to)
|
setSessionStatus (Session.Waiting to)
|
||||||
|
@ -58,7 +58,7 @@ receive (Session.Answering to) message@(Message.Answer {Message.accept}) = do
|
||||||
else do
|
else do
|
||||||
Messaging.broadcast $ Messaging.update {Message.alone = [for, to]}
|
Messaging.broadcast $ Messaging.update {Message.alone = [for, to]}
|
||||||
return $ Session.LoggedIn True
|
return $ Session.LoggedIn True
|
||||||
App.update_ $ Server.update to (RW.set newStatus :: Session.Update)
|
App.update_ $ Server.update to (Data.set newStatus :: Session.Update)
|
||||||
setSessionStatus newStatus
|
setSessionStatus newStatus
|
||||||
_ -> sendError "They're not waiting for your answer"
|
_ -> sendError "They're not waiting for your answer"
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ receive (Session.Playing gameID) played@(Message.Play {}) = do
|
||||||
Messaging.notifyPlayers newGame logs
|
Messaging.notifyPlayers newGame logs
|
||||||
|
|
||||||
receive (Session.Playing gameID) Message.Quit = do
|
receive (Session.Playing gameID) Message.Quit = do
|
||||||
games <- (RW.get <$> App.server :: App.T (Map KoiKoi.GameID KoiKoi.Game))
|
games <- (Data.get <$> App.server :: App.T (Map KoiKoi.GameID KoiKoi.Game))
|
||||||
case games !? gameID of
|
case games !? gameID of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
playerID <- asks App.playerID
|
playerID <- asks App.playerID
|
||||||
|
@ -93,7 +93,7 @@ sendError = Messaging.send . Message.Error
|
||||||
setSessionStatus :: Session.Status -> App.T ()
|
setSessionStatus :: Session.Status -> App.T ()
|
||||||
setSessionStatus newStatus = do
|
setSessionStatus newStatus = do
|
||||||
playerID <- asks App.playerID
|
playerID <- asks App.playerID
|
||||||
App.update_ $ Server.update playerID $ (RW.set newStatus :: Session.Update)
|
App.update_ $ Server.update playerID $ (Data.set newStatus :: Session.Update)
|
||||||
App.debug $ show newStatus
|
App.debug $ show newStatus
|
||||||
|
|
||||||
loop :: App.T ()
|
loop :: App.T ()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
module RW (
|
module Data (
|
||||||
RW(..)
|
RW(..)
|
||||||
) where
|
) where
|
||||||
|
|
|
@ -10,12 +10,12 @@ import Control.Monad.Reader (lift)
|
||||||
import Control.Monad.Writer (runWriterT)
|
import Control.Monad.Writer (runWriterT)
|
||||||
import Data.Map (mapWithKey)
|
import Data.Map (mapWithKey)
|
||||||
import qualified Hanafuda (empty)
|
import qualified Hanafuda (empty)
|
||||||
import Hanafuda.KoiKoi (Game, GameBlueprint(..), GameID, Mode(..), PlayerID)
|
|
||||||
import qualified Hanafuda.KoiKoi as KoiKoi (
|
import qualified Hanafuda.KoiKoi as KoiKoi (
|
||||||
Action, Move(..), play, new
|
Action, Move(..), play, new
|
||||||
)
|
)
|
||||||
import Hanafuda.Message (PublicGame)
|
import Hanafuda.KoiKoi (Game, GameBlueprint(..), GameID, Mode(..), PlayerID)
|
||||||
import qualified Hanafuda.Player (Player(..), Players(..))
|
import qualified Hanafuda.Player (Player(..), Players(..))
|
||||||
|
import Hanafuda.Message (PublicGame)
|
||||||
import qualified Server (register)
|
import qualified Server (register)
|
||||||
|
|
||||||
new :: (PlayerID, PlayerID) -> App.T GameID
|
new :: (PlayerID, PlayerID) -> App.T GameID
|
||||||
|
|
22
src/Main.hs
22
src/Main.hs
|
@ -2,21 +2,21 @@
|
||||||
{-# LANGUAGE NamedFieldPuns #-}
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
module Main where
|
module Main where
|
||||||
|
|
||||||
import qualified App (Context(..), T, update_)
|
import Network.Wai.Handler.Warp (run)
|
||||||
import qualified Automaton (start)
|
import Network.HTTP.Types.Status (badRequest400)
|
||||||
import qualified Config (listenPort)
|
import Network.WebSockets (ServerApp, acceptRequest, defaultConnectionOptions)
|
||||||
|
import Network.Wai.Handler.WebSockets (websocketsOr)
|
||||||
|
import Network.Wai (responseLBS)
|
||||||
|
import Control.Monad.Reader (ReaderT(..), asks)
|
||||||
import Control.Concurrent (newMVar, modifyMVar)
|
import Control.Concurrent (newMVar, modifyMVar)
|
||||||
import Control.Exception (finally)
|
import Control.Exception (finally)
|
||||||
import Control.Monad.Reader (ReaderT(..), asks)
|
import qualified Config (listenPort)
|
||||||
|
import qualified Session (open)
|
||||||
|
import qualified Server (disconnect, new, register)
|
||||||
|
import qualified App (Context(..), T, update_)
|
||||||
import qualified Hanafuda.Message as Message (FromClient(..))
|
import qualified Hanafuda.Message as Message (FromClient(..))
|
||||||
import Messaging (broadcast, relay)
|
import Messaging (broadcast, relay)
|
||||||
import Network.HTTP.Types.Status (badRequest400)
|
import qualified Automaton (start)
|
||||||
import Network.Wai (responseLBS)
|
|
||||||
import Network.Wai.Handler.Warp (run)
|
|
||||||
import Network.Wai.Handler.WebSockets (websocketsOr)
|
|
||||||
import Network.WebSockets (ServerApp, acceptRequest, defaultConnectionOptions)
|
|
||||||
import qualified Server (disconnect, new, register)
|
|
||||||
import qualified Session (open)
|
|
||||||
|
|
||||||
exit :: App.T ()
|
exit :: App.T ()
|
||||||
exit = do
|
exit = do
|
||||||
|
|
|
@ -13,20 +13,20 @@ module Messaging (
|
||||||
, update
|
, update
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified App (Context(..), T, connection, debug, server)
|
|
||||||
import Control.Monad.Reader (asks, lift)
|
|
||||||
import Data.Aeson (eitherDecode', encode)
|
|
||||||
import Data.ByteString.Lazy.Char8 (unpack)
|
|
||||||
import Data.Foldable (forM_)
|
|
||||||
import Data.List (intercalate)
|
import Data.List (intercalate)
|
||||||
|
import Data.Foldable (forM_)
|
||||||
import Data.Map (keys)
|
import Data.Map (keys)
|
||||||
import qualified Hanafuda.KoiKoi as KoiKoi (Action, Game, GameBlueprint(..), PlayerID)
|
import Data.Aeson (eitherDecode', encode)
|
||||||
import Hanafuda.Message (FromClient(..), T(..))
|
|
||||||
import qualified Hanafuda.Message as Message (T)
|
|
||||||
import Network.WebSockets (receiveData, sendTextData)
|
import Network.WebSockets (receiveData, sendTextData)
|
||||||
|
import Data.ByteString.Lazy.Char8 (unpack)
|
||||||
|
import Control.Monad.Reader (asks, lift)
|
||||||
import qualified Game (export)
|
import qualified Game (export)
|
||||||
import qualified Server (T(..), get)
|
|
||||||
import qualified Session (T(..))
|
import qualified Session (T(..))
|
||||||
|
import qualified Server (T(..), get)
|
||||||
|
import qualified App (Context(..), T, connection, debug, server)
|
||||||
|
import qualified Hanafuda.KoiKoi as KoiKoi (Action, Game, GameBlueprint(..), PlayerID)
|
||||||
|
import qualified Hanafuda.Message as Message (T)
|
||||||
|
import Hanafuda.Message (FromClient(..), T(..))
|
||||||
|
|
||||||
sendTo :: [KoiKoi.PlayerID] -> Message.T -> App.T ()
|
sendTo :: [KoiKoi.PlayerID] -> Message.T -> App.T ()
|
||||||
sendTo playerIDs obj = do
|
sendTo playerIDs obj = do
|
||||||
|
|
|
@ -23,7 +23,7 @@ import qualified Data.Set as Set (delete, empty, insert)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import Hanafuda.KoiKoi (Game, GameID, PlayerID)
|
import Hanafuda.KoiKoi (Game, GameID, PlayerID)
|
||||||
import Hanafuda.Message (PlayerStatus(..), Room)
|
import Hanafuda.Message (PlayerStatus(..), Room)
|
||||||
import qualified RW (RW(..))
|
import qualified Data (RW(..))
|
||||||
import qualified Session (Status(..), T(..), Update)
|
import qualified Session (Status(..), T(..), Update)
|
||||||
|
|
||||||
type Names = Set Text
|
type Names = Set Text
|
||||||
|
@ -37,19 +37,19 @@ data T = T {
|
||||||
, games :: Games
|
, games :: Games
|
||||||
}
|
}
|
||||||
|
|
||||||
instance RW.RW Names T where
|
instance Data.RW Names T where
|
||||||
get = names
|
get = names
|
||||||
set names server = server {names}
|
set names server = server {names}
|
||||||
|
|
||||||
instance RW.RW Players T where
|
instance Data.RW Players T where
|
||||||
get = players
|
get = players
|
||||||
set players server = server {players}
|
set players server = server {players}
|
||||||
|
|
||||||
instance RW.RW Sessions T where
|
instance Data.RW Sessions T where
|
||||||
get = sessions
|
get = sessions
|
||||||
set sessions server = server {sessions}
|
set sessions server = server {sessions}
|
||||||
|
|
||||||
instance RW.RW Games T where
|
instance Data.RW Games T where
|
||||||
get = games
|
get = games
|
||||||
set games server = server {games}
|
set games server = server {games}
|
||||||
|
|
||||||
|
@ -72,31 +72,31 @@ new = T {
|
||||||
, games = Map.empty
|
, games = Map.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
register :: forall a b. (Enum a, Ord a, RW.RW (Map a b) T) => b -> T -> (T, a)
|
register :: forall a b. (Enum a, Ord a, Data.RW (Map a b) T) => b -> T -> (T, a)
|
||||||
register x server =
|
register x server =
|
||||||
let playerID = maybe (toEnum 0) (\(n, _) -> succ n) $ lookupMax $ (RW.get server :: Map a b) in
|
let playerID = maybe (toEnum 0) (\(n, _) -> succ n) $ lookupMax $ (Data.get server :: Map a b) in
|
||||||
(RW.update (insert playerID x) server, playerID)
|
(Data.update (insert playerID x) server, playerID)
|
||||||
|
|
||||||
get :: forall a b. (Ord a, RW.RW (Map a b) T) => a -> T -> b
|
get :: forall a b. (Ord a, Data.RW (Map a b) T) => a -> T -> b
|
||||||
get playerID server = (RW.get server :: Map a b) ! playerID
|
get playerID server = (Data.get server :: Map a b) ! playerID
|
||||||
|
|
||||||
update :: forall a b. (Ord a, RW.RW (Map a b) T) => a -> (b -> b) -> T -> T
|
update :: forall a b. (Ord a, Data.RW (Map a b) T) => a -> (b -> b) -> T -> T
|
||||||
update playerID updator =
|
update playerID updator =
|
||||||
RW.update (adjust updator playerID :: Map a b -> Map a b)
|
Data.update (adjust updator playerID :: Map a b -> Map a b)
|
||||||
|
|
||||||
disconnect :: PlayerID -> T -> T
|
disconnect :: PlayerID -> T -> T
|
||||||
disconnect playerID =
|
disconnect playerID =
|
||||||
RW.update (delete playerID :: Sessions -> Sessions) . logOut playerID
|
Data.update (delete playerID :: Sessions -> Sessions) . logOut playerID
|
||||||
|
|
||||||
endGame :: GameID -> T -> T
|
endGame :: GameID -> T -> T
|
||||||
endGame playerID =
|
endGame playerID =
|
||||||
RW.update (delete playerID :: Games -> Games)
|
Data.update (delete playerID :: Games -> Games)
|
||||||
|
|
||||||
logIn :: Text -> PlayerID -> T -> Either String T
|
logIn :: Text -> PlayerID -> T -> Either String T
|
||||||
logIn name playerID server =
|
logIn name playerID server =
|
||||||
RW.update (Set.insert name) .
|
Data.update (Set.insert name) .
|
||||||
RW.update (insert playerID name) .
|
Data.update (insert playerID name) .
|
||||||
update playerID (RW.set $ Session.LoggedIn True :: Session.Update) <$>
|
update playerID (Data.set $ Session.LoggedIn True :: Session.Update) <$>
|
||||||
if name `member` names server
|
if name `member` names server
|
||||||
then Left "This name is already registered"
|
then Left "This name is already registered"
|
||||||
else Right server
|
else Right server
|
||||||
|
@ -106,7 +106,7 @@ logOut playerID server =
|
||||||
maybe
|
maybe
|
||||||
server
|
server
|
||||||
(\playerName ->
|
(\playerName ->
|
||||||
RW.update (delete playerID :: Players -> Players) $
|
Data.update (delete playerID :: Players -> Players) $
|
||||||
update playerID (RW.set $ Session.LoggedIn False :: Session.Update) $
|
update playerID (Data.set $ Session.LoggedIn False :: Session.Update) $
|
||||||
RW.update (Set.delete playerName :: Names -> Names) server)
|
Data.update (Set.delete playerName :: Names -> Names) server)
|
||||||
(players server !? playerID)
|
(players server !? playerID)
|
||||||
|
|
|
@ -7,9 +7,9 @@ module Session (
|
||||||
, open
|
, open
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Hanafuda.KoiKoi (GameID, PlayerID)
|
|
||||||
import Network.WebSockets (Connection)
|
import Network.WebSockets (Connection)
|
||||||
import qualified RW (RW(..))
|
import Hanafuda.KoiKoi (GameID, PlayerID)
|
||||||
|
import qualified Data (RW(..))
|
||||||
|
|
||||||
data Status =
|
data Status =
|
||||||
LoggedIn Bool
|
LoggedIn Bool
|
||||||
|
@ -24,7 +24,7 @@ data T = T {
|
||||||
}
|
}
|
||||||
type Update = T -> T
|
type Update = T -> T
|
||||||
|
|
||||||
instance RW.RW Status T where
|
instance Data.RW Status T where
|
||||||
get = status
|
get = status
|
||||||
set status session = session {status}
|
set status session = session {status}
|
||||||
|
|
||||||
|
|
47
www/game.js
47
www/game.js
|
@ -9,7 +9,6 @@ function Game(modules) {
|
||||||
};
|
};
|
||||||
var sets = buildSets();
|
var sets = buildSets();
|
||||||
var selected = null;
|
var selected = null;
|
||||||
var turnedCard = null;
|
|
||||||
var queue = [];
|
var queue = [];
|
||||||
|
|
||||||
function buildSets() {
|
function buildSets() {
|
||||||
|
@ -175,8 +174,7 @@ function Game(modules) {
|
||||||
movingCards.push([sets[side].hand, dest, card]);
|
movingCards.push([sets[side].hand, dest, card]);
|
||||||
} else {
|
} else {
|
||||||
var cardSet = {};
|
var cardSet = {};
|
||||||
cardSet[card.name] = turnedCard || new TurnedCard(card.name);
|
cardSet[card.name] = turnedCard(card.name);
|
||||||
turnedCard = null;
|
|
||||||
movingCards.push([{card: cardSet, dom: deck}, dest, card]);
|
movingCards.push([{card: cardSet, dom: deck}, dest, card]);
|
||||||
}
|
}
|
||||||
return movingCards;
|
return movingCards;
|
||||||
|
@ -191,7 +189,7 @@ function Game(modules) {
|
||||||
|
|
||||||
function moveCard(fromSet, toSet, card) {
|
function moveCard(fromSet, toSet, card) {
|
||||||
var from, originalCard;
|
var from, originalCard;
|
||||||
var slot = modules.dom.make('li', {class: ['card', 'slot']});
|
var slot = modules.dom.make('li', {class: ['card', 'slot']});
|
||||||
if (fromSet.card[card.name] != undefined) {
|
if (fromSet.card[card.name] != undefined) {
|
||||||
originalCard = fromSet.card[card.name].dom;
|
originalCard = fromSet.card[card.name].dom;
|
||||||
delete fromSet.card[card.name];
|
delete fromSet.card[card.name];
|
||||||
|
@ -302,11 +300,18 @@ function Game(modules) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function turnedCard(cardName) {
|
||||||
|
var card = new Card(cardName);
|
||||||
|
card.dom.id = "turned";
|
||||||
|
deck.appendChild(card.dom);
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
function setTurned(cardName) {
|
function setTurned(cardName) {
|
||||||
turnedCard = new TurnedCard(cardName);
|
turnedCard(cardName);
|
||||||
if(status.playing) {
|
if(status.playing) {
|
||||||
selected = turnedCard;
|
selected = cardName;
|
||||||
showCandidates(modules.hanafuda.Card[cardName], true);
|
showCandidates(modules.hanafuda.Card[selected], true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,12 +319,6 @@ function Game(modules) {
|
||||||
matchingInRiver(card).forEach(function(riverCard) {riverCard.setCandidate(yes);});
|
matchingInRiver(card).forEach(function(riverCard) {riverCard.setCandidate(yes);});
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSelected(yes) {
|
|
||||||
selected = yes ? this : null;
|
|
||||||
this.dom.classList.toggle('selected', yes);
|
|
||||||
showCandidates(this.value, yes);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Card(name) {
|
function Card(name) {
|
||||||
this.value = modules.hanafuda.Card[name];
|
this.value = modules.hanafuda.Card[name];
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
@ -344,8 +343,9 @@ function Game(modules) {
|
||||||
var card = this;
|
var card = this;
|
||||||
return function() {
|
return function() {
|
||||||
if(card.candidate) {
|
if(card.candidate) {
|
||||||
var withCard = selected.name;
|
var withCard = selected;
|
||||||
selected.setSelected(false);
|
selected = null;
|
||||||
|
showCandidates(card.value, false);
|
||||||
play(
|
play(
|
||||||
status.step == 'ToPlay' ? {capture: [withCard, card.name]} : {choose: card.name}
|
status.step == 'ToPlay' ? {capture: [withCard, card.name]} : {choose: card.name}
|
||||||
);
|
);
|
||||||
|
@ -358,15 +358,6 @@ function Game(modules) {
|
||||||
this.dom.classList.toggle("candidate", yes);
|
this.dom.classList.toggle("candidate", yes);
|
||||||
}
|
}
|
||||||
|
|
||||||
function TurnedCard() {
|
|
||||||
Card.apply(this, arguments);
|
|
||||||
this.dom.id = "turned";
|
|
||||||
deck.appendChild(this.dom);
|
|
||||||
}
|
|
||||||
|
|
||||||
TurnedCard.prototype.onClick = Card.prototype.onClick;
|
|
||||||
TurnedCard.prototype.setSelected = setSelected;
|
|
||||||
|
|
||||||
function HandCard() {
|
function HandCard() {
|
||||||
Card.apply(this, arguments);
|
Card.apply(this, arguments);
|
||||||
}
|
}
|
||||||
|
@ -376,7 +367,7 @@ function Game(modules) {
|
||||||
return function() {
|
return function() {
|
||||||
if(status.playing && status.step == "ToPlay") {
|
if(status.playing && status.step == "ToPlay") {
|
||||||
if(selected != undefined) {
|
if(selected != undefined) {
|
||||||
selected.setSelected(false);
|
sets.you.hand.card[selected].setSelected(false);
|
||||||
} else {
|
} else {
|
||||||
card.play();
|
card.play();
|
||||||
}
|
}
|
||||||
|
@ -384,7 +375,11 @@ function Game(modules) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
HandCard.prototype.setSelected = setSelected;
|
HandCard.prototype.setSelected = function(yes) {
|
||||||
|
selected = yes ? this.name : null;
|
||||||
|
this.dom.classList.toggle('selected', yes);
|
||||||
|
showCandidates(this.value, yes);
|
||||||
|
}
|
||||||
|
|
||||||
HandCard.prototype.play = function() {
|
HandCard.prototype.play = function() {
|
||||||
var matching = matchingInRiver(this.value);
|
var matching = matchingInRiver(this.value);
|
||||||
|
|
|
@ -70,6 +70,6 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="dialog">
|
<div id="dialog">
|
||||||
</div>
|
</div>
|
||||||
<p id="error"></p>
|
<p id="debug"></p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -5,7 +5,7 @@ window.addEventListener('load', function() {
|
||||||
var i18n = I18n({translations: translations});
|
var i18n = I18n({translations: translations});
|
||||||
var fun = Fun();
|
var fun = Fun();
|
||||||
var screen = Screen({dom: dom, i18n: i18n});
|
var screen = Screen({dom: dom, i18n: i18n});
|
||||||
var messaging = Messaging({screen: screen});
|
var messaging = Messaging();
|
||||||
var session = Session({messaging: messaging});
|
var session = Session({messaging: messaging});
|
||||||
var room = Room({dom: dom, messaging: messaging, session: session, fun: fun});
|
var room = Room({dom: dom, messaging: messaging, session: session, fun: fun});
|
||||||
var statusHandler = StatusHandler();
|
var statusHandler = StatusHandler();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
function Messaging(modules) {
|
function Messaging(screen) {
|
||||||
var ws = new WebSocket(window.location.origin.replace(/^http/, 'ws') + '/play/');
|
var ws = new WebSocket(window.location.origin.replace(/^http/, 'ws') + '/play/');
|
||||||
var debug = getParameters().debug;
|
var debug = getParameters().debug;
|
||||||
var doLog = debug != undefined && debug.match(/^(?:1|t(?:rue)?|v(?:rai)?)$/i);
|
var doLog = debug != undefined && debug.match(/^(?:1|t(?:rue)?|v(?:rai)?)$/i);
|
||||||
|
@ -53,7 +53,7 @@ function Messaging(modules) {
|
||||||
if(route != undefined && route.callbacks != undefined) {
|
if(route != undefined && route.callbacks != undefined) {
|
||||||
route.callbacks.forEach(function(f) {f(o);});
|
route.callbacks.forEach(function(f) {f(o);});
|
||||||
} else {
|
} else {
|
||||||
console.log("No route found for " + event.data);
|
debug.textContent = event.data;
|
||||||
}
|
}
|
||||||
o.direction = 'client < server';
|
o.direction = 'client < server';
|
||||||
log(o);
|
log(o);
|
||||||
|
@ -69,7 +69,6 @@ function Messaging(modules) {
|
||||||
ws.addEventListener('message', messageListener);
|
ws.addEventListener('message', messageListener);
|
||||||
ws.addEventListener('open', ping);
|
ws.addEventListener('open', ping);
|
||||||
addEventListener(["Pong"], ping);
|
addEventListener(["Pong"], ping);
|
||||||
addEventListener(["Error"], function(o) {modules.screen.error(o.error);});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function send(o) {
|
function send(o) {
|
||||||
|
|
|
@ -33,22 +33,3 @@ body > div.on {
|
||||||
#dialog button {
|
#dialog button {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#error {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
top: 1em;
|
|
||||||
right: 1em;
|
|
||||||
max-width: 20em;
|
|
||||||
border: 1px solid #e0afac;
|
|
||||||
padding: 1em;
|
|
||||||
border-radius: 0.5em;
|
|
||||||
background: bisque;
|
|
||||||
cursor: pointer;
|
|
||||||
margin: 0;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#error.on {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
function Screen(modules) {
|
function Screen(modules) {
|
||||||
var current = document.querySelector("body > div.on");
|
var current = document.querySelector("body > div.on");
|
||||||
var errorBox = document.getElementById('error');
|
|
||||||
errorBox.addEventListener('click', function() {
|
|
||||||
errorBox.className = "";
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
error: error,
|
|
||||||
dialog: dialog,
|
dialog: dialog,
|
||||||
select: select
|
select: select
|
||||||
};
|
};
|
||||||
|
@ -40,9 +35,4 @@ function Screen(modules) {
|
||||||
layer.appendChild(dialog);
|
layer.appendChild(dialog);
|
||||||
layer.className = "on";
|
layer.className = "on";
|
||||||
}
|
}
|
||||||
|
|
||||||
function error(message) {
|
|
||||||
errorBox.textContent = message;
|
|
||||||
errorBox.className = "on";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue