Ninjas-0.1.0.0: src/Simulation.hs
{-# LANGUAGE RecordWildCards #-}
module Simulation where
import Data.List (findIndex)
import Graphics.Gloss.Data.Point
import Graphics.Gloss.Data.Vector
import Graphics.Gloss.Geometry.Angle
import System.Random (randomRIO)
import Character
import Parameters
import VectorUtils
data Player = Player
{ playerCharacter :: Character
, playerUsername :: String
, playerScore :: Int
, playerVisited :: [Int]
, playerSmokes :: Int
}
deriving (Show, Read, Eq)
data ServerMode = Playing | Starting Float | Stopped
deriving (Eq, Read, Show)
-- | Lift a function on Characters to one on Players
mapPlayerCharacter :: (Character -> Character) -> Player -> Player
mapPlayerCharacter f p = p { playerCharacter = f (playerCharacter p) }
canHitPoint :: Character -> Point -> Bool
canHitPoint char pt = inRange && inFront
where
inRange = attackLen <= attackDistance
-- u·v = ❘v❘ ❘u❘ cos Θ
inFront = cos attackAngle * attackLen
<= charFacing char `dotV` attackVector
attackVector = subPt pt (charPos char)
attackLen = magV attackVector
-- | Compute a random point inside a box.
randomPoint :: Point -> Point -> IO Point
randomPoint (minX,minY) (maxX,maxY) =
do x <- randomRIO (minX,maxX)
y <- randomRIO (minY,maxY)
return (x,y)
-- | Compute a random point inside the board.
randomBoardPoint :: IO Point
randomBoardPoint = randomPoint boardMin boardMax
-- | Compute a random unit vector.
randomUnitVector :: IO Vector
randomUnitVector =
do degrees <- randomRIO (0,359)
let rads = degToRad $ fromInteger degrees
return $ unitVectorAtAngle rads
-- | Construct a new character given a name, a position,
-- and a facing unit vector. When think is True,
-- the character will be scheduled to begin walking
-- after a random duration.
initServerCharacter :: Bool -> IO Character
initServerCharacter think =
do charPos <- randomBoardPoint
charFacing <- randomUnitVector
waitWaiting <- pickWaitTime think
let waitStunned = False
charState = Waiting Wait { .. }
return Character { .. }
-- | Construct a new player given an initial number
-- of smokebombs, an identifier, a username, and a
-- starting score.
initPlayer :: Int -> String -> Int -> IO Player
initPlayer smokes playerUsername playerScore =
do playerCharacter <- initServerCharacter False
let playerVisited = []
playerSmokes = smokes
return Player { .. }
-- | When True, compute a new random wait time value.
pickWaitTime ::
Bool {- ^ compute random delay -} ->
IO (Maybe Float)
pickWaitTime False = return Nothing
pickWaitTime True = fmap Just $ randomRIO (0, restTime)
-- | Determine if a point lies within a given pillar.
isInPillar ::
Point {- ^ location to test -} ->
Point {- ^ center of pillar -} ->
Bool
isInPillar p (x,y) = pointInBox p (x-halfside, y-halfside)
(x+halfside, y+halfside)
where
halfside = pillarSize / 2
-- | Determine the index, if any, of the pillar with contains a point.
whichPillar :: Point -> Maybe Int
whichPillar p = findIndex (isInPillar p) pillars
-- | Return true if the given player can use smokebombs.
hasSmokebombs :: Player -> Bool
hasSmokebombs p = playerSmokes p > 0
-- | Update a player to have one fewer smokebombs.
consumeSmokebomb :: Player -> Player
consumeSmokebomb p = p { playerSmokes = playerSmokes p - 1 }