Ninjas-0.1.0.0: src/Character.hs
{-# LANGUAGE RecordWildCards #-}
module Character where
import Graphics.Gloss.Data.Point
import Graphics.Gloss.Data.Vector
import VectorUtils
import Parameters
data Character = Character
{ charPos :: Point
, charFacing :: Vector -- Unit vector
, charState :: State
}
deriving (Read, Show, Eq)
data WalkInfo = Walk
{ walkTarget :: Point
-- Cached, so that we don't recompute all the time.
, walkDist :: Float
, walkVelocity :: Vector
}
deriving (Show, Read, Eq)
data WaitInfo = Wait
{ waitWaiting :: Maybe Float
, waitStunned :: Bool
}
deriving (Show, Read, Eq)
data State
= Walking WalkInfo
| Waiting WaitInfo
| Dead
| Attacking Float
deriving (Show, Read, Eq)
data ThinkTask = ChooseWait | ChooseDestination
-- | Compute a new character given a number of elapsed seconds. An optional
-- update task will be returned if the server should compute a new
-- goal for the character. Clients will ignore this task and wait for the
-- server to send an update.
stepCharacter ::
Float {- ^ elapsed seconds -} ->
Character ->
(Character, Bool, Maybe ThinkTask) -- (new character, did we change states, AI task)
stepCharacter elapsed c =
case state of
Walking w
| walkDist w < step -> done (Just ChooseWait)
| otherwise ->
working (Walking w { walkDist = walkDist w - step })
c { charPos = newPos }
where step = elapsed * speed
newPos = addPt (mulSV elapsed (walkVelocity w)) (charPos c)
Waiting w ->
case waitWaiting w of
Nothing -> working state c
Just todo
| todo < elapsed -> done (Just ChooseDestination)
| otherwise ->
working (Waiting w { waitWaiting = Just (todo - elapsed) }) c
Attacking delay
| elapsed > delay -> done Nothing
| otherwise -> working (Attacking (delay - elapsed)) c
Dead -> working state c
where
state = charState c
done next = (c { charState = Waiting Wait { waitWaiting = Nothing
, waitStunned = False } }
, True
, next)
working s n = (n { charState = s}, False, Nothing)
-- | Update a Character to have the goal of walking to a given point.
walkingCharacter :: Point -> Character -> Character
walkingCharacter walkTarget c = c { charState = state
, charFacing = facing }
where
state = Walking Walk { .. }
facing | walkDist > 0.001 = mulSV (1 / walkDist) path
| otherwise = charFacing c
walkVelocity = mulSV speed facing
path = subPt walkTarget (charPos c)
walkDist = magV path
-- | Update an character to have the goal of waiting for an optional
-- duration and optionally to be drawn as stunned. If a duration
-- is specified the character will be updated automatically by the server.
-- characters without a duration are typically players.
waitingCharacter :: Maybe Float -> Bool -> Character -> Character
waitingCharacter waitWaiting waitStunned c =
c { charState = Waiting Wait { .. } }
-- | Update an character to be dead
deadCharacter :: Character -> Character
deadCharacter c = c { charState = Dead }
-- | Update an character to be stunned for the default stun duration.
stunnedCharacter :: Character -> Character
stunnedCharacter = waitingCharacter (Just stunTime) True
-- | Update an character to be in the attacking state for the
-- default attack duration.
attackingCharacter :: Character -> Character
attackingCharacter c = c { charState = Attacking attackDelay }
isStunned :: Character -> Bool
isStunned c =
case charState c of
Waiting Wait { waitStunned = True } -> True
_ -> False