start creating amin server loop
This commit is contained in:
parent
bf2310a011
commit
7516364193
5 changed files with 181 additions and 12 deletions
|
@ -19,7 +19,7 @@ data Wizard = Wizard
|
||||||
-- ^ Player's Mana (magic fuel)
|
-- ^ Player's Mana (magic fuel)
|
||||||
, wizardWands :: [Wand]
|
, wizardWands :: [Wand]
|
||||||
-- ^ Player's wands (read: weapons)
|
-- ^ Player's wands (read: weapons)
|
||||||
, wizardEffect :: [Effect]
|
, wizardEffects :: [Effect]
|
||||||
-- ^ 'Effect's affecting the player
|
-- ^ 'Effect's affecting the player
|
||||||
}
|
}
|
||||||
deriving (Show, Eq)
|
deriving (Show, Eq)
|
||||||
|
@ -49,12 +49,6 @@ data Tile
|
||||||
| Wall -- ^ obstacle
|
| Wall -- ^ obstacle
|
||||||
deriving (Show, Eq)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
data ServerOptions = ServerOptions
|
|
||||||
{ serOptMapWidth :: Int -- ^ Map width
|
|
||||||
, serOptMapHeight :: Int -- ^ Map height
|
|
||||||
}
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
data Arena = Arena
|
data Arena = Arena
|
||||||
{ arenaMap :: ServerMap
|
{ arenaMap :: ServerMap
|
||||||
, arenaSpawners :: [Spawner]
|
, arenaSpawners :: [Spawner]
|
||||||
|
@ -97,3 +91,9 @@ data Affliction
|
||||||
| Frozen -- ^ Unable to perform any action
|
| Frozen -- ^ Unable to perform any action
|
||||||
| Shielded -- ^ invulnerable until next hit or time runs out.
|
| Shielded -- ^ invulnerable until next hit or time runs out.
|
||||||
deriving (Show, Eq)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
|
harmingAfflictions :: [ Affliction ]
|
||||||
|
harmingAfflictions =
|
||||||
|
[ Burning
|
||||||
|
, Frozen
|
||||||
|
]
|
||||||
|
|
|
@ -1,4 +1,126 @@
|
||||||
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
module Main where
|
module Main where
|
||||||
|
|
||||||
|
import Control.Concurrent (threadDelay)
|
||||||
|
|
||||||
|
import Control.Monad.Loops
|
||||||
|
|
||||||
|
import Control.Monad.RWS.Strict
|
||||||
|
|
||||||
|
import qualified Data.ByteString.Char8 as B8
|
||||||
|
|
||||||
|
import Data.String (fromString)
|
||||||
|
|
||||||
|
import Data.Time
|
||||||
|
|
||||||
|
import Data.Yaml
|
||||||
|
|
||||||
|
import Options.Applicative
|
||||||
|
|
||||||
|
-- internal imports
|
||||||
|
|
||||||
|
import Library.Types
|
||||||
|
|
||||||
|
import Server.Types
|
||||||
|
import Server.Map (generateArena)
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = putStrLn "Hello, Haskell!"
|
main = do
|
||||||
|
putStrLn "Hello, Wizards!"
|
||||||
|
(Options optSettingLoc) <- execParser opts
|
||||||
|
raw <- B8.readFile optSettingLoc
|
||||||
|
case decodeEither' raw of
|
||||||
|
Left msg ->
|
||||||
|
error (optSettingLoc <> ":" <>
|
||||||
|
" error: " <>
|
||||||
|
fromString (prettyPrintParseException msg)
|
||||||
|
)
|
||||||
|
Right (Settings {..}) -> do
|
||||||
|
arena <- generateArena setMapRows setMapColumns setSpawnerProbability
|
||||||
|
putStrLn "generated arena:"
|
||||||
|
print (arenaMap arena)
|
||||||
|
putStrLn "-----------------------------------------------------------------------------------"
|
||||||
|
putStrLn "starting game…"
|
||||||
|
now <- getCurrentTime
|
||||||
|
let initRead = ReaderContainer (arenaMap arena) setFPS
|
||||||
|
initState = StateContainer (arenaSpawners arena) [] (ServerState False False) now
|
||||||
|
(finalState, finalWrite) <- execRWST runGame initRead initState
|
||||||
|
print finalWrite
|
||||||
|
where
|
||||||
|
opts = info (options <**> helper)
|
||||||
|
( fullDesc
|
||||||
|
<> progDesc "Run the \"wizard-wipeout\" Server."
|
||||||
|
<> header "wizard-wipeout - Last Mage standing"
|
||||||
|
)
|
||||||
|
|
||||||
|
runGame :: Game
|
||||||
|
runGame = do
|
||||||
|
whileM_
|
||||||
|
(not . serverStop <$> gets scServerState)
|
||||||
|
(do
|
||||||
|
before <- gets scServerLastTick
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
let delta = realToFrac $ diffUTCTime now before
|
||||||
|
updateSpawners delta
|
||||||
|
updateWizards delta
|
||||||
|
fps <- asks rcFPS
|
||||||
|
let remainingTime = recip (fromIntegral fps) - delta
|
||||||
|
when (remainingTime > 0) $
|
||||||
|
liftIO $ threadDelay $ floor $ remainingTime * 10 ^ 6
|
||||||
|
)
|
||||||
|
|
||||||
|
updateSpawners :: Float -> Game
|
||||||
|
updateSpawners dt =
|
||||||
|
modify' (\sc@(StateContainer spawners _ _ _) ->
|
||||||
|
let newSpawners = map
|
||||||
|
(\spawner -> do
|
||||||
|
let newTTL = spawnerReloadTTL spawner - dt
|
||||||
|
if newTTL < 0 && spawnerState spawner == SpawnerEmpty
|
||||||
|
then spawner
|
||||||
|
{ spawnerReloadTTL = fromIntegral $ spawnerReloadTime spawner
|
||||||
|
, spawnerState = SpawnerFull
|
||||||
|
}
|
||||||
|
else spawner
|
||||||
|
{ spawnerReloadTTL = spawnerReloadTTL spawner - dt
|
||||||
|
}
|
||||||
|
)
|
||||||
|
spawners
|
||||||
|
in sc
|
||||||
|
{ scSpawners = newSpawners
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
updateWizards :: Float -> Game
|
||||||
|
updateWizards dt =
|
||||||
|
modify' (\sc@(StateContainer _ wizards _ _) ->
|
||||||
|
let newWizards = map
|
||||||
|
(\wizard@(Wizard _ _ health _ _ effects) ->
|
||||||
|
let newEffects = foldl
|
||||||
|
(\acc effect@(Effect _ ttl) ->
|
||||||
|
if ttl - dt < 0
|
||||||
|
then acc
|
||||||
|
else effect
|
||||||
|
{ effectTTL = ttl - dt
|
||||||
|
} : acc
|
||||||
|
)
|
||||||
|
[]
|
||||||
|
effects
|
||||||
|
effectDamage = foldl
|
||||||
|
(\acc (Effect kind _) ->
|
||||||
|
if kind `elem` harmingAfflictions
|
||||||
|
then acc + 1
|
||||||
|
else acc
|
||||||
|
)
|
||||||
|
0
|
||||||
|
effects
|
||||||
|
in wizard
|
||||||
|
{ wizardEffects = newEffects
|
||||||
|
, wizardHealth = health - effectDamage
|
||||||
|
}
|
||||||
|
)
|
||||||
|
wizards
|
||||||
|
in sc
|
||||||
|
{ scPlayers = newWizards
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
|
@ -30,7 +30,10 @@ generateArena arenaRows arenaColumns spawnerChance = do
|
||||||
Spawner
|
Spawner
|
||||||
(wands !! randomWandIndex)
|
(wands !! randomWandIndex)
|
||||||
randomReloadTime
|
randomReloadTime
|
||||||
(V2 (fromIntegral r) (fromIntegral c) + V2 0.5 0.5) : acc
|
SpawnerFull
|
||||||
|
0
|
||||||
|
(V2 (fromIntegral r) (fromIntegral c) + V2 0.5 0.5)
|
||||||
|
: acc
|
||||||
else
|
else
|
||||||
pure acc
|
pure acc
|
||||||
_ -> pure acc
|
_ -> pure acc
|
||||||
|
|
|
@ -1,19 +1,53 @@
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
module Server.Options where
|
module Server.Types where
|
||||||
|
|
||||||
|
import Control.Monad.RWS.Strict
|
||||||
|
|
||||||
import qualified Data.Aeson as Aeson
|
import qualified Data.Aeson as Aeson
|
||||||
|
|
||||||
|
import Data.Time.Clock
|
||||||
|
|
||||||
import GHC.Generics
|
import GHC.Generics
|
||||||
|
|
||||||
|
import Options.Applicative as O
|
||||||
|
|
||||||
|
import Library.Types
|
||||||
|
|
||||||
data Settings = Settings
|
data Settings = Settings
|
||||||
{ setSocketPath :: FilePath
|
{ setSocketPath :: FilePath
|
||||||
, setMapRows :: Int
|
, setMapRows :: Int
|
||||||
, setMapColumns :: Int
|
, setMapColumns :: Int
|
||||||
, setSpawnerProbability :: Float
|
, setSpawnerProbability :: Float
|
||||||
|
, setFPS :: Int
|
||||||
}
|
}
|
||||||
deriving (Show, Generic)
|
deriving (Show, Generic)
|
||||||
|
|
||||||
instance Aeson.FromJSON Settings
|
instance Aeson.FromJSON Settings
|
||||||
|
|
||||||
data Options = Options
|
newtype Options = Options
|
||||||
{ optConfLoc :: FilePath
|
{ optConfLoc :: FilePath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options :: O.Parser Options
|
||||||
|
options = Options
|
||||||
|
<$> strOption
|
||||||
|
( long "configuration"
|
||||||
|
<> short 'c'
|
||||||
|
<> metavar "FILEPATH"
|
||||||
|
<> help "Location of the configuration YAML-file"
|
||||||
|
)
|
||||||
|
|
||||||
|
data ReaderContainer = ReaderContainer
|
||||||
|
{ rcMap :: ServerMap
|
||||||
|
, rcFPS :: Int
|
||||||
|
}
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
data StateContainer = StateContainer
|
||||||
|
{ scSpawners :: [ Spawner ]
|
||||||
|
, scPlayers :: [ Wizard ]
|
||||||
|
, scServerState :: ServerState
|
||||||
|
, scServerLastTick :: UTCTime
|
||||||
|
}
|
||||||
|
|
||||||
|
type Game = RWST ReaderContainer String StateContainer IO ()
|
||||||
|
|
|
@ -33,7 +33,10 @@ executable wizard-wipeout-client
|
||||||
-- other-modules:
|
-- other-modules:
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: base ^>=4.17.2.1
|
build-depends: base ^>=4.17.2.1
|
||||||
|
, monad-loops
|
||||||
|
, mtl
|
||||||
, network
|
, network
|
||||||
|
, vty
|
||||||
, wizard-wipeout
|
, wizard-wipeout
|
||||||
hs-source-dirs: src-client
|
hs-source-dirs: src-client
|
||||||
default-language: GHC2021
|
default-language: GHC2021
|
||||||
|
@ -42,14 +45,21 @@ executable wizard-wipeout-server
|
||||||
import: warnings
|
import: warnings
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
other-modules: Server.Map
|
other-modules: Server.Map
|
||||||
Server.Map.Snippets
|
|
||||||
Server.Types
|
Server.Types
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: base ^>=4.17.2.1
|
build-depends: base ^>=4.17.2.1
|
||||||
|
, aeson
|
||||||
|
, bytestring
|
||||||
, linear
|
, linear
|
||||||
, matrix
|
, matrix
|
||||||
|
, monad-loops
|
||||||
|
, mtl
|
||||||
, network
|
, network
|
||||||
|
, optparse-applicative
|
||||||
|
, text
|
||||||
|
, time
|
||||||
, random
|
, random
|
||||||
, wizard-wipeout
|
, wizard-wipeout
|
||||||
|
, yaml
|
||||||
hs-source-dirs: src-server
|
hs-source-dirs: src-server
|
||||||
default-language: GHC2021
|
default-language: GHC2021
|
||||||
|
|
Loading…
Reference in a new issue