Initializing repos with a basic websocket handler for a few basic messages
This commit is contained in:
commit
abe5a5dc85
26 changed files with 770 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
dist/
|
||||||
|
.cabal-sandbox
|
||||||
|
cabal.sandbox.config
|
5
ChangeLog.md
Normal file
5
ChangeLog.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Revision history for pokeNeige
|
||||||
|
|
||||||
|
## 0.1.0.0 -- YYYY-mm-dd
|
||||||
|
|
||||||
|
* First version. Released on an unsuspecting world.
|
30
LICENSE
Normal file
30
LICENSE
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
Copyright (c) 2018, Tissevert
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following
|
||||||
|
disclaimer in the documentation and/or other materials provided
|
||||||
|
with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of Tissevert nor the names of other
|
||||||
|
contributors may be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2
Setup.hs
Normal file
2
Setup.hs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
import Distribution.Simple
|
||||||
|
main = defaultMain
|
54
pokeNeige.cabal
Normal file
54
pokeNeige.cabal
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
-- Initial pokeNeige.cabal generated by cabal init. For further
|
||||||
|
-- documentation, see http://haskell.org/cabal/users-guide/
|
||||||
|
|
||||||
|
name: pokeNeige
|
||||||
|
version: 0.1.0.0
|
||||||
|
synopsis: A web pokemon game
|
||||||
|
-- description:
|
||||||
|
homepage: https://git.marvid.fr/PokeNeige
|
||||||
|
license: BSD3
|
||||||
|
license-file: LICENSE
|
||||||
|
author: Tissevert
|
||||||
|
maintainer: tissevert+devel@marvid.fr
|
||||||
|
-- copyright:
|
||||||
|
category: Game
|
||||||
|
build-type: Simple
|
||||||
|
extra-source-files: ChangeLog.md
|
||||||
|
cabal-version: >=1.10
|
||||||
|
|
||||||
|
executable pokeNeige
|
||||||
|
main-is: Main.hs
|
||||||
|
other-modules: Area
|
||||||
|
, Area.Climate
|
||||||
|
, Area.NaturalElements
|
||||||
|
, Area.Tile
|
||||||
|
, Automaton
|
||||||
|
, Character
|
||||||
|
, Config
|
||||||
|
, Game
|
||||||
|
, Item
|
||||||
|
, Message
|
||||||
|
, Message.Client
|
||||||
|
, Message.Server
|
||||||
|
, Pokemon
|
||||||
|
, Pokemon.Move
|
||||||
|
, Pokemon.Species
|
||||||
|
, Pokemon.Stats
|
||||||
|
, Pokemon.Status
|
||||||
|
, Pokemon.Type
|
||||||
|
, Tool.Array
|
||||||
|
-- other-extensions:
|
||||||
|
build-depends: base >=4.11 && <4.12
|
||||||
|
, aeson
|
||||||
|
, bytestring
|
||||||
|
, containers
|
||||||
|
, http-types
|
||||||
|
, mtl
|
||||||
|
, vector
|
||||||
|
, wai
|
||||||
|
, wai-websockets
|
||||||
|
, warp
|
||||||
|
, websockets
|
||||||
|
ghc-options: -Wall
|
||||||
|
hs-source-dirs: src
|
||||||
|
default-language: Haskell2010
|
52
src/Area.hs
Normal file
52
src/Area.hs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE OverloadedLists #-}
|
||||||
|
module Area (
|
||||||
|
Area(..)
|
||||||
|
, Key
|
||||||
|
, dex
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson (ToJSON(..), FromJSON(..), genericParseJSON, genericToEncoding, defaultOptions)
|
||||||
|
import Data.Vector (Vector)
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
import Area.Tile (Content(..), Tile(..), Terrain(..))
|
||||||
|
import Area.Climate (Climate(..))
|
||||||
|
|
||||||
|
data Area = Area {
|
||||||
|
matrix :: Vector (Vector Tile)
|
||||||
|
} deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Area where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Area where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
||||||
|
|
||||||
|
newtype Key = Key Int deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Key where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Key where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
||||||
|
|
||||||
|
dex :: Vector Area
|
||||||
|
dex = [
|
||||||
|
Area {
|
||||||
|
matrix = [
|
||||||
|
[ground, ground]
|
||||||
|
, [ground, water]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
where
|
||||||
|
ground = Tile {
|
||||||
|
terrain = Normal
|
||||||
|
, skin = "tiles/ground.png"
|
||||||
|
, climate = Mild
|
||||||
|
, content = Empty
|
||||||
|
}
|
||||||
|
water = Tile {
|
||||||
|
terrain = Water
|
||||||
|
, skin = "tiles/water.png"
|
||||||
|
, climate = Mild
|
||||||
|
, content = Empty
|
||||||
|
}
|
14
src/Area/Climate.hs
Normal file
14
src/Area/Climate.hs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
module Area.Climate (
|
||||||
|
Climate(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson (ToJSON(..), FromJSON(..), genericToEncoding, genericParseJSON, defaultOptions)
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
|
||||||
|
data Climate = Mild | Rain | Sand | Sun | Snow deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Climate where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Climate where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
17
src/Area/NaturalElements.hs
Normal file
17
src/Area/NaturalElements.hs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
module Area.NaturalElements (
|
||||||
|
NaturalElements(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson (ToJSON(..), FromJSON(..), genericToEncoding, genericParseJSON, defaultOptions)
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
|
||||||
|
data NaturalElements = NaturalElements {
|
||||||
|
name :: String
|
||||||
|
, skin :: FilePath
|
||||||
|
} deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON NaturalElements where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON NaturalElements where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
46
src/Area/Tile.hs
Normal file
46
src/Area/Tile.hs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
module Area.Tile (
|
||||||
|
Content(..)
|
||||||
|
, Terrain(..)
|
||||||
|
, Tile(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson (ToJSON(..), FromJSON(..), genericToEncoding, genericParseJSON, defaultOptions)
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
import Area.NaturalElements (NaturalElements)
|
||||||
|
import Area.Climate (Climate)
|
||||||
|
import qualified Character (Key)
|
||||||
|
import qualified Item (Key)
|
||||||
|
import qualified Pokemon (T)
|
||||||
|
|
||||||
|
data Terrain = Normal | Water deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Terrain where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Terrain where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
||||||
|
|
||||||
|
data Content =
|
||||||
|
Empty
|
||||||
|
| Item Item.Key
|
||||||
|
| Character Character.Key
|
||||||
|
| Pokemon Pokemon.T
|
||||||
|
| NaturalElements NaturalElements
|
||||||
|
deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Content where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Content where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
||||||
|
|
||||||
|
data Tile = Tile {
|
||||||
|
terrain :: Terrain
|
||||||
|
, skin :: FilePath
|
||||||
|
, content :: Content
|
||||||
|
, climate :: Climate
|
||||||
|
} deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Tile where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Tile where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
30
src/Automaton.hs
Normal file
30
src/Automaton.hs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
module Automaton (
|
||||||
|
run
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Monad.State (get, put)
|
||||||
|
import Control.Monad.IO.Class (liftIO)
|
||||||
|
import Data.Aeson (encode)
|
||||||
|
import Data.ByteString.Lazy.Char8 (ByteString, putStrLn)
|
||||||
|
import Prelude hiding (log, putStrLn)
|
||||||
|
import Game (Game)
|
||||||
|
import Message (Connected, receive, send)
|
||||||
|
import qualified Message.Client as Client (Message(..))
|
||||||
|
import qualified Message.Server as Server (Message(..))
|
||||||
|
|
||||||
|
edges :: Game -> Client.Message -> Connected Game
|
||||||
|
edges = undefined
|
||||||
|
|
||||||
|
log :: ByteString -> Connected ()
|
||||||
|
log line =
|
||||||
|
liftIO $ putStrLn line
|
||||||
|
|
||||||
|
run :: Connected ()
|
||||||
|
run = do
|
||||||
|
message <- Message.receive
|
||||||
|
game <- get
|
||||||
|
newGame <- edges game message
|
||||||
|
log $ encode newGame
|
||||||
|
put newGame
|
||||||
|
send $ Server.Update newGame
|
||||||
|
run
|
42
src/Character.hs
Normal file
42
src/Character.hs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE OverloadedLists #-}
|
||||||
|
module Character (
|
||||||
|
Character(..)
|
||||||
|
, Key
|
||||||
|
, dex
|
||||||
|
, stub
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson (ToJSON(..), FromJSON(..), genericToEncoding, genericParseJSON, defaultOptions)
|
||||||
|
import Data.Vector (Vector)
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
import qualified Pokemon (T)
|
||||||
|
import Tool.Array (Array, malloc)
|
||||||
|
|
||||||
|
data Character = Character {
|
||||||
|
name :: String
|
||||||
|
, skin :: FilePath
|
||||||
|
, pokemons :: Array Pokemon.T
|
||||||
|
} deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Character where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Character where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
||||||
|
|
||||||
|
newtype Key = Key Int deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Key where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Key where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
||||||
|
|
||||||
|
dex :: Vector Character
|
||||||
|
dex = []
|
||||||
|
|
||||||
|
stub :: Character
|
||||||
|
stub = Character {
|
||||||
|
name = ""
|
||||||
|
, skin = ""
|
||||||
|
, pokemons = malloc 6
|
||||||
|
}
|
8
src/Config.hs
Normal file
8
src/Config.hs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module Config (
|
||||||
|
listenPort
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Network.Wai.Handler.Warp (Port)
|
||||||
|
|
||||||
|
listenPort :: Port
|
||||||
|
listenPort = 3000
|
67
src/Game.hs
Normal file
67
src/Game.hs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
module Game (
|
||||||
|
Game(..)
|
||||||
|
, stub
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson (ToJSON(..), FromJSON(..), genericToEncoding, genericParseJSON, defaultOptions)
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
|
||||||
|
import Character (Character)
|
||||||
|
import qualified Character (stub)
|
||||||
|
import qualified Area (Key)
|
||||||
|
|
||||||
|
data Position = Position {
|
||||||
|
area :: Area.Key
|
||||||
|
, x :: Int
|
||||||
|
, y :: Int
|
||||||
|
} deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Position where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Position where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
||||||
|
|
||||||
|
data Turn = Player | Opponent deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Turn where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Turn where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
||||||
|
|
||||||
|
data InitProcess = Name | Gender | OpponentsName deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON InitProcess where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON InitProcess where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
||||||
|
|
||||||
|
data State =
|
||||||
|
At Position
|
||||||
|
| Fighting {
|
||||||
|
at :: Position
|
||||||
|
, opponent :: Character
|
||||||
|
, turn :: Turn
|
||||||
|
}
|
||||||
|
| Initializing InitProcess deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON State where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON State where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
||||||
|
|
||||||
|
data Game = Game {
|
||||||
|
character :: Character
|
||||||
|
, state :: State
|
||||||
|
} deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Game where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Game where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
||||||
|
|
||||||
|
stub :: Game
|
||||||
|
stub = Game {
|
||||||
|
character = Character.stub
|
||||||
|
, state = Initializing Name
|
||||||
|
}
|
32
src/Item.hs
Normal file
32
src/Item.hs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE OverloadedLists #-}
|
||||||
|
module Item (
|
||||||
|
Item(..)
|
||||||
|
, Key
|
||||||
|
, Type(..)
|
||||||
|
, dex
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson (ToJSON(..), FromJSON(..), genericToEncoding, genericParseJSON, defaultOptions)
|
||||||
|
import Data.Vector (Vector)
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
import qualified Pokemon (T)
|
||||||
|
|
||||||
|
data Type = Hold | Transform (Pokemon.T -> Pokemon.T) | Special
|
||||||
|
|
||||||
|
data Item = Item {
|
||||||
|
name :: String
|
||||||
|
, skin :: FilePath
|
||||||
|
, type_ :: Type
|
||||||
|
, inBattle :: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
newtype Key = Key Int deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Key where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Key where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
||||||
|
|
||||||
|
dex :: Vector Item
|
||||||
|
dex = []
|
39
src/Main.hs
Normal file
39
src/Main.hs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
|
module Main where
|
||||||
|
|
||||||
|
import Control.Monad.Reader (runReaderT)
|
||||||
|
import Control.Monad.State (evalStateT)
|
||||||
|
import Network.HTTP.Types.Status (badRequest400)
|
||||||
|
import Network.Wai (responseLBS)
|
||||||
|
import qualified Network.Wai.Handler.Warp as Warp (run)
|
||||||
|
import Network.Wai.Handler.WebSockets (websocketsOr)
|
||||||
|
import Network.WebSockets (ServerApp, acceptRequest, defaultConnectionOptions)
|
||||||
|
import qualified Automaton (run)
|
||||||
|
import Config (listenPort)
|
||||||
|
import qualified Game (stub)
|
||||||
|
import Message (receive, send)
|
||||||
|
import qualified Message.Client as Client (Message(..))
|
||||||
|
import qualified Message.Server as Server (Message(..))
|
||||||
|
|
||||||
|
makeApp :: IO ServerApp
|
||||||
|
makeApp =
|
||||||
|
return $ \pending -> do
|
||||||
|
connection <- acceptRequest pending
|
||||||
|
putStrLn "New connection"
|
||||||
|
message <- runReaderT receive connection
|
||||||
|
game <- case message of
|
||||||
|
Client.NewGame -> do
|
||||||
|
let newGame = Game.stub
|
||||||
|
runReaderT (send (Server.Init newGame)) connection
|
||||||
|
putStrLn "New game"
|
||||||
|
return newGame
|
||||||
|
Client.Resume {Client.game} -> putStrLn "Loading game" >> return game
|
||||||
|
evalStateT (runReaderT Automaton.run connection) game
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = do
|
||||||
|
app <- makeApp
|
||||||
|
Warp.run Config.listenPort $ websocketsOr defaultConnectionOptions app blockNonWS
|
||||||
|
where
|
||||||
|
blockNonWS _ = ( $ responseLBS badRequest400 [] "Use a websocket")
|
26
src/Message.back.hs
Normal file
26
src/Message.back.hs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
module Message (
|
||||||
|
Connected
|
||||||
|
, receive
|
||||||
|
, send
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Monad.Reader (ReaderT, ask, lift)
|
||||||
|
import Network.WebSockets (Connection, receiveData, sendTextData)
|
||||||
|
import Data.Aeson (encode, eitherDecode')
|
||||||
|
import qualified Message.Client as Client (Message)
|
||||||
|
import qualified Message.Server as Server (Message(..))
|
||||||
|
|
||||||
|
type Connected a = ReaderT Connection IO a
|
||||||
|
|
||||||
|
send :: Server.Message -> Connected ()
|
||||||
|
send message = do
|
||||||
|
connection <- ask
|
||||||
|
lift $ sendTextData connection $ encode message
|
||||||
|
|
||||||
|
receive :: Connected Client.Message
|
||||||
|
receive = do
|
||||||
|
connection <- ask
|
||||||
|
received <- lift $ receiveData connection
|
||||||
|
case eitherDecode' received of
|
||||||
|
Left message -> send (Server.Error message) >> receive
|
||||||
|
Right message -> return message
|
37
src/Message.hs
Normal file
37
src/Message.hs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{- LANGUAGE MultiParamTypeClasses #-}
|
||||||
|
module Message (
|
||||||
|
Connected
|
||||||
|
, receive
|
||||||
|
, send
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Monad.Reader (MonadReader, ReaderT, ask)
|
||||||
|
import Control.Monad.State (StateT)
|
||||||
|
import Control.Monad.IO.Class (MonadIO(..))
|
||||||
|
import Network.WebSockets (Connection, receiveData, sendTextData)
|
||||||
|
import Data.Aeson (encode, eitherDecode')
|
||||||
|
import Game (Game)
|
||||||
|
import qualified Message.Client as Client (Message)
|
||||||
|
import qualified Message.Server as Server (Message(..))
|
||||||
|
|
||||||
|
type Connected a = ReaderT Connection (StateT Game IO) a
|
||||||
|
|
||||||
|
{-
|
||||||
|
class ReaderIO t c where
|
||||||
|
f :: t a -> t c
|
||||||
|
liftIO :: (a -> IO b) -> a -> t b
|
||||||
|
-}
|
||||||
|
|
||||||
|
send :: (MonadReader Connection t, MonadIO t) => Server.Message -> t ()
|
||||||
|
send message = do
|
||||||
|
connection <- ask
|
||||||
|
liftIO $ sendTextData connection $ encode message
|
||||||
|
|
||||||
|
receive :: (MonadReader Connection t, MonadIO t) => t Client.Message
|
||||||
|
receive = do
|
||||||
|
connection <- ask
|
||||||
|
received <- liftIO $ receiveData connection
|
||||||
|
case eitherDecode' received of
|
||||||
|
Left message -> send (Server.Error message) >> receive
|
||||||
|
Right message -> return message
|
17
src/Message/Client.hs
Normal file
17
src/Message/Client.hs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
module Message.Client (
|
||||||
|
Message(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson (FromJSON(..), genericParseJSON, defaultOptions)
|
||||||
|
import Game (Game)
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
|
||||||
|
data Message =
|
||||||
|
NewGame
|
||||||
|
| Resume {
|
||||||
|
game :: Game
|
||||||
|
} deriving (Generic)
|
||||||
|
|
||||||
|
instance FromJSON Message where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
22
src/Message/Server.hs
Normal file
22
src/Message/Server.hs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
module Message.Server (
|
||||||
|
Message(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson (ToJSON(..), genericToEncoding, defaultOptions)
|
||||||
|
import Game (Game)
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
|
||||||
|
data Message =
|
||||||
|
Init {
|
||||||
|
game :: Game
|
||||||
|
}
|
||||||
|
| Update {
|
||||||
|
game :: Game
|
||||||
|
}
|
||||||
|
| Error {
|
||||||
|
error :: String
|
||||||
|
} deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Message where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
33
src/Pokemon.hs
Normal file
33
src/Pokemon.hs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{- LANGUAGE FlexibleInstances #-}
|
||||||
|
module Pokemon (
|
||||||
|
T(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson (ToJSON(..), FromJSON(..), genericToEncoding, genericParseJSON, defaultOptions)
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
|
||||||
|
import qualified Pokemon.Species as Species (Key)
|
||||||
|
import Pokemon.Status (Status)
|
||||||
|
import qualified Pokemon.Move as Move (Key)
|
||||||
|
import Tool.Array (Array)
|
||||||
|
|
||||||
|
data Gender = M | F | NB deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Gender where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Gender
|
||||||
|
|
||||||
|
data T = T {
|
||||||
|
nick :: Maybe String
|
||||||
|
, species :: Species.Key
|
||||||
|
, gender :: Gender
|
||||||
|
, level :: Int
|
||||||
|
, status :: Status
|
||||||
|
, moves :: Array Move.Key
|
||||||
|
} deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON T where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON T where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
57
src/Pokemon/Move.hs
Normal file
57
src/Pokemon/Move.hs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE OverloadedLists #-}
|
||||||
|
module Pokemon.Move (
|
||||||
|
Effect(..)
|
||||||
|
, Key
|
||||||
|
, Move(..)
|
||||||
|
, dex
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson (ToJSON(..), FromJSON(..), genericToEncoding, genericParseJSON, defaultOptions)
|
||||||
|
import Data.Vector (Vector)
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
import Area.Climate (Climate)
|
||||||
|
import Pokemon.Status (Status)
|
||||||
|
import Pokemon.Type (Type(..))
|
||||||
|
import Pokemon.Stats (Stats)
|
||||||
|
import qualified Pokemon.Stats as Stats (modify, precision)
|
||||||
|
|
||||||
|
data Effect =
|
||||||
|
Blow { damage :: Int }
|
||||||
|
| StatusChange { changeStatus :: Status -> Maybe Status }
|
||||||
|
| StatsChange { changeStats :: Stats -> Maybe Stats }
|
||||||
|
| Flee
|
||||||
|
| Expell
|
||||||
|
| Switch
|
||||||
|
| Corner
|
||||||
|
| Climatic Climate
|
||||||
|
|
||||||
|
data Move = Move {
|
||||||
|
name :: String
|
||||||
|
, type_ :: Type
|
||||||
|
, precision :: Int
|
||||||
|
, effects :: [Effect]
|
||||||
|
}
|
||||||
|
|
||||||
|
newtype Key = Key Int deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Key where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Key where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
||||||
|
|
||||||
|
dex :: Vector Move
|
||||||
|
dex = [
|
||||||
|
Move {
|
||||||
|
name = "charge"
|
||||||
|
, type_ = Normal
|
||||||
|
, precision = 100
|
||||||
|
, effects = [ Blow { damage = 30 } ]
|
||||||
|
}
|
||||||
|
, Move {
|
||||||
|
name = "mimi-queue"
|
||||||
|
, type_ = Normal
|
||||||
|
, precision = 80
|
||||||
|
, effects = [ StatsChange { changeStats = Stats.modify Stats.precision (\x -> x-10) 30 } ]
|
||||||
|
}
|
||||||
|
]
|
27
src/Pokemon/Species.hs
Normal file
27
src/Pokemon/Species.hs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
module Pokemon.Species (
|
||||||
|
Key
|
||||||
|
, Species(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson (ToJSON(..), FromJSON(..), genericToEncoding, genericParseJSON, defaultOptions)
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
|
||||||
|
import Pokemon.Type (Type)
|
||||||
|
import Pokemon.Move (Move)
|
||||||
|
import Data.Map (Map)
|
||||||
|
|
||||||
|
data Species = Species {
|
||||||
|
name :: String
|
||||||
|
, number :: Int
|
||||||
|
, type_ :: Type
|
||||||
|
, skin :: FilePath
|
||||||
|
, moves :: Map Int Move
|
||||||
|
}
|
||||||
|
|
||||||
|
newtype Key = Key Int deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Key where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Key where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
68
src/Pokemon/Stats.hs
Normal file
68
src/Pokemon/Stats.hs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
{-# LANGUAGE OverloadedLists #-}
|
||||||
|
module Pokemon.Stats (
|
||||||
|
Proto(..)
|
||||||
|
, Stat
|
||||||
|
, Stats
|
||||||
|
, attack
|
||||||
|
, defense
|
||||||
|
, speed
|
||||||
|
, precision
|
||||||
|
, specialAttack
|
||||||
|
, specialDefense
|
||||||
|
, index
|
||||||
|
, get
|
||||||
|
, modify
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Vector (Vector, (!), (//))
|
||||||
|
|
||||||
|
newtype Stat = Stat Int
|
||||||
|
|
||||||
|
attack :: Stat
|
||||||
|
attack = Stat 0
|
||||||
|
|
||||||
|
defense :: Stat
|
||||||
|
defense = Stat 1
|
||||||
|
|
||||||
|
speed :: Stat
|
||||||
|
speed = Stat 2
|
||||||
|
|
||||||
|
precision :: Stat
|
||||||
|
precision = Stat 3
|
||||||
|
|
||||||
|
specialAttack :: Stat
|
||||||
|
specialAttack = Stat 4
|
||||||
|
|
||||||
|
specialDefense :: Stat
|
||||||
|
specialDefense = Stat 5
|
||||||
|
|
||||||
|
data Proto = Proto {
|
||||||
|
attack_ :: Int
|
||||||
|
, defense_ :: Int
|
||||||
|
, speed_ :: Int
|
||||||
|
, precision_ :: Int
|
||||||
|
, specialAttack_ :: Int
|
||||||
|
, specialDefense_ :: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
newtype Stats = Stats (Vector Int)
|
||||||
|
|
||||||
|
index :: Proto -> Stats
|
||||||
|
index proto = Stats [
|
||||||
|
(attack_ proto)
|
||||||
|
, (defense_ proto)
|
||||||
|
, (speed_ proto)
|
||||||
|
, (precision_ proto)
|
||||||
|
, (specialAttack_ proto)
|
||||||
|
, (specialDefense_ proto)
|
||||||
|
]
|
||||||
|
|
||||||
|
get :: Stats -> Stat -> Int
|
||||||
|
get (Stats v) (Stat s) = v ! s
|
||||||
|
|
||||||
|
modify :: Stat -> (Int -> Int) -> Int -> Stats -> Maybe Stats
|
||||||
|
modify stat@(Stat s) f threshold stats@(Stats v) =
|
||||||
|
let result = f $ stats `get` stat in
|
||||||
|
if result < threshold
|
||||||
|
then Nothing
|
||||||
|
else Just . Stats $ v // [(s, result)]
|
14
src/Pokemon/Status.hs
Normal file
14
src/Pokemon/Status.hs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
module Pokemon.Status (
|
||||||
|
Status(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson (ToJSON(..), FromJSON(..), genericToEncoding, genericParseJSON, defaultOptions)
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
|
||||||
|
data Status = Normal | Sleeping | Frozen | Poisoned | Burning | Confused | InLove deriving (Generic)
|
||||||
|
|
||||||
|
instance ToJSON Status where
|
||||||
|
toEncoding = genericToEncoding defaultOptions
|
||||||
|
instance FromJSON Status where
|
||||||
|
parseJSON = genericParseJSON defaultOptions
|
5
src/Pokemon/Type.hs
Normal file
5
src/Pokemon/Type.hs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module Pokemon.Type (
|
||||||
|
Type(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
data Type = Water | Fire | Eletric | Plant | Rock | Ground | Psy | Ice | Fly | Normal | Ghost
|
23
src/Tool/Array.hs
Normal file
23
src/Tool/Array.hs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
module Tool.Array (
|
||||||
|
Array
|
||||||
|
, (!)
|
||||||
|
, clear
|
||||||
|
, malloc
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Vector (Vector, (!?), (//))
|
||||||
|
import qualified Data.Vector as Vector (replicate)
|
||||||
|
|
||||||
|
type Array a = Vector (Maybe a)
|
||||||
|
|
||||||
|
(!) :: Array a -> Int -> Maybe a
|
||||||
|
t ! k =
|
||||||
|
case t !? k of
|
||||||
|
Nothing -> Nothing
|
||||||
|
Just a -> a
|
||||||
|
|
||||||
|
clear :: Array a -> Int -> Array a
|
||||||
|
clear t k = t // [(k, Nothing)]
|
||||||
|
|
||||||
|
malloc :: Int -> Array a
|
||||||
|
malloc size = Vector.replicate size Nothing
|
Loading…
Reference in a new issue