tracer/src/NPC.hs

177 lines
4.9 KiB
Haskell
Raw Normal View History

2018-04-14 09:18:37 +00:00
module NPC where
import Affection as A
import qualified Data.Matrix as M
import Data.Ecstasy as E
2018-04-14 16:43:05 +00:00
import Control.Monad.IO.Class (MonadIO(..))
import Control.Concurrent.MVar
2018-05-18 18:05:21 +00:00
import Control.Concurrent (forkOS)
2018-04-14 16:43:05 +00:00
2018-04-14 09:18:37 +00:00
import Linear
import System.Random
import NanoVG hiding (V2(..))
-- internal imports
2018-04-14 11:34:28 +00:00
import Navigation
import Util
2018-04-14 09:18:37 +00:00
import Types.UserData
2018-04-14 11:34:28 +00:00
import Types.Interior
import Types.Map
2018-04-14 16:43:05 +00:00
import Types.ReachPoint
2018-04-14 09:18:37 +00:00
drawNPCs
:: Context
2018-05-15 17:27:54 +00:00
-> UserData
2018-04-14 09:18:37 +00:00
-> [V2 Double]
-> Double
-> Double
-> Int
-> Int
-> Maybe ImgId
2018-05-15 17:27:54 +00:00
-> IO ()
drawNPCs ctx ud npcposs prow pcol row col img = do
2018-04-14 09:18:37 +00:00
let fnpcposs = filter
(\(V2 nr nc) ->
let x = realToFrac $ 640 + ((nc - pcol) + (nr - prow)) * 32
y = realToFrac $ 360 + ((nr - prow) - (nc - pcol)) * 16
in ((realToFrac x > -tileWidth && realToFrac y > -tileHeight) &&
(realToFrac x < 1280 && realToFrac (y - (74 - realToFrac tileHeight)) < 720)) &&
((all (\m -> prow > (fromIntegral (floor prow :: Int)) + m) minrs &&
all (\m -> pcol < (fromIntegral (floor pcol :: Int)) + m) mincs) ||
(all (\m -> prow > (fromIntegral (floor prow :: Int)) + m) minrs &&
all (\m -> pcol < (fromIntegral (floor pcol :: Int)) + m) maxcs)) &&
(floor nr == row && floor nc == col)
)
2018-04-14 09:18:37 +00:00
npcposs
2018-05-15 17:27:54 +00:00
mapM_
2018-04-14 09:18:37 +00:00
(\(V2 nr nc) -> do
let x = realToFrac $ 640 + ((nc - pcol) + (nr - prow)) * 32
y = realToFrac $ 360 + ((nr - prow) - (nc - pcol)) * 16
beginPath ctx
circle ctx x y 5
closePath ctx
fillColor ctx (rgba 255 0 0 255)
fill ctx
)
fnpcposs
where
tileWidth = 64 :: Double
tileHeight = 32 :: Double
mb = imgObstacle img
minrs = Prelude.map (fst . matmin) mb
maxrs = Prelude.map (fst . matmax) mb
mincs = Prelude.map (snd . matmin) mb
maxcs = Prelude.map (snd . matmax) mb
2018-04-14 09:18:37 +00:00
2018-04-14 11:34:28 +00:00
placeNPCs
:: M.Matrix (Maybe ImgId)
2018-04-14 16:43:05 +00:00
-> M.Matrix TileState
2018-04-14 11:34:28 +00:00
-> [ReachPoint]
-> [Graph]
-> Int
-> Affection UserData [V2 Double]
2018-04-14 16:43:05 +00:00
placeNPCs imgmat tilemat rp gr count =
2018-04-14 09:18:37 +00:00
doPlace 1 []
where
doPlace :: Int -> [V2 Double] -> Affection UserData [V2 Double]
doPlace nr acc = do
if nr <= count
then do
r <- liftIO $ randomRIO (1, M.nrows imgmat)
c <- liftIO $ randomRIO (1, M.ncols imgmat)
2018-04-14 11:34:28 +00:00
if null (imgObstacle $ imgmat M.! (r, c)) &&
2018-04-14 16:43:05 +00:00
tilemat M.! (r, c) == Hall
2018-04-14 09:18:37 +00:00
then doPlace (nr + 1) ((V2 (fromIntegral r) (fromIntegral c)) : acc)
2018-04-14 16:43:05 +00:00
else do
i <- liftIO $ randomRIO (0, length nonexits - 1)
doPlace
(nr + 1)
((fmap fromIntegral $ pointCoord (nonexits !! i)) : acc)
2018-04-14 09:18:37 +00:00
else
return acc
2018-04-14 11:34:28 +00:00
applRooms row col =
2018-04-14 16:43:05 +00:00
(filter (\r -> graphIsRoom r && inBounds (V2 row col) (bounds r)) gr)
nonexits =
filter
(\p ->
pointType p /= RoomExit
)
rp
updateNPCs
2018-04-22 09:59:14 +00:00
:: MonadIO m
2018-04-14 16:43:05 +00:00
=> M.Matrix (Maybe ImgId)
-> [ReachPoint]
-> Double
-> SystemT Entity m ()
updateNPCs imgmat rp dt =
2018-05-17 11:06:13 +00:00
emap allEnts $ do
2018-04-14 16:43:05 +00:00
with npcState
with vel
with pos
2018-05-17 11:06:13 +00:00
npcState' <- query npcState
2018-04-14 16:43:05 +00:00
case npcState' of
NPCStanding ttl future -> do
2018-04-14 16:43:05 +00:00
let nttl = ttl - dt
if nttl > 0
then
2018-05-17 11:06:13 +00:00
return $ unchanged
{ npcState = Set $ NPCStanding nttl future
2018-04-14 16:43:05 +00:00
, vel = Set $ V2 0 0
}
else do
mpath <- liftIO $ tryTakeMVar future
case mpath of
Just path ->
2018-05-17 11:06:13 +00:00
return $ unchanged
{ npcState = Set $ NPCWalking path
}
Nothing ->
2018-05-17 11:06:13 +00:00
return $ unchanged
{ npcState = Set $ NPCStanding 1 future
}
2018-04-14 16:43:05 +00:00
NPCWalking path -> do
2018-05-17 11:06:13 +00:00
pos' <- query pos
2018-04-14 16:43:05 +00:00
if not (null path)
then do
let itarget = V2 0.5 0.5 + (fromIntegral <$> head path) :: V2 Double
if distance pos' itarget < 0.1
then
2018-05-17 11:06:13 +00:00
return $ unchanged
2018-04-14 16:43:05 +00:00
{ npcState = Set $ NPCWalking (tail path)
}
else
2018-05-17 11:06:13 +00:00
return $ unchanged
2018-05-13 20:01:05 +00:00
{ vel = Set $ (* 2) <$> signorm (itarget - pos')
2018-04-14 16:43:05 +00:00
}
else do
ttl <- liftIO $ randomRIO (5, 30)
future <- liftIO $ newEmptyMVar
2018-05-18 18:05:21 +00:00
_ <- liftIO $ forkOS $ getPath (fmap floor pos') future rp imgmat
2018-05-17 11:06:13 +00:00
return $ unchanged
{ npcState = Set $ NPCStanding ttl future
2018-04-14 16:43:05 +00:00
}
getPath
:: V2 Int
-> MVar [V2 Int]
-> [ReachPoint]
-> M.Matrix (Maybe ImgId)
-> IO ()
getPath pos' mvar rp imgmat = do
let seekRP = filter (\p -> pointType p /= RoomExit) rp
ntargeti <- randomRIO (0, length seekRP - 1)
let ntarget = pointCoord (seekRP !! ntargeti)
path = astarAppl imgmat ntarget pos'
logIO A.Verbose ("seeking path from " ++ show pos' ++ " to " ++ show ntarget)
case path of
Nothing -> getPath pos' mvar rp imgmat
Just p -> putMVar mvar p