packages feed

chiphunk-0.1.0.0: src/Chiphunk/Low/Callback.chs

-- | Description: Collision handler definition
-- Module provides definitions for collision handlers.
module Chiphunk.Low.Callback
  ( CollisionCallback
  , CollisionHandler (..)
  , CollisionHandlerPtr
  , spaceAddCollisionHandler
  , spaceAddWildcardHandler
  , spaceAddDefaultCollisionHandler
  , modifyCollisionHandler
  , mkCallback
  , mkCallbackB
  , PostStepFunc
  , spaceAddPostStepCallback
  ) where

import Control.Applicative (liftA)
import Control.Exception.Safe
import Data.Bool (bool)
import Foreign

{# import Chiphunk.Low.Types #}

#include <chipmunk/chipmunk.h>

-- | Collision callback
type CollisionCallback ret = Arbiter -> Space -> DataPtr -> IO ret

-- | This collision handler processes collisions between objects of type @typeA@ and @typeB@.
-- Fill the desired collision callback functions- they are documented above. A user definable context pointer
-- @userData@ is included for your convenience. This pointer is provided as an argument in each callback function.
--
-- A collision handler is a set of 4 function callbacks for the different collision events that Chipmunk recognizes.
data CollisionHandler = CollisionHandler
  { chTypeA         :: !CollisionType                   -- ^ typeA
  , chTypeB         :: !CollisionType                   -- ^ typeB
  , chBeginFunc     :: !(FunPtr (CollisionCallback CPBool))
    -- ^ Two shapes just started touching for the first time this step. Return true from the callback
    -- to process the collision normally or false to cause Chipmunk to ignore the collision entirely.
    -- If you return false, the preSolve and postSolve callbacks will never be run, but you will still recieve
    -- a separate event when the shapes stop overlapping.
  , chPreSolveFunc  :: !(FunPtr (CollisionCallback CPBool))
    -- ^ Two shapes are touching during this step. Return false from the callback to make Chipmunk ignore the collision
    -- this step or true to process it normally. Additionally, you may override collision values using
    -- 'Chiphunk.Low.Arbiter.arbiterFriction', 'Chiphunk.Low.Arbiter.arbiterRestitution' or
    -- 'Chiphunk.Low.arbiterSurfaceVelocity' to provide custom friction, elasticity, or surface velocity values.
    -- See 'Arbiter' for more info.
  , chPostSolveFunc :: !(FunPtr (CollisionCallback ()))
    -- ^ Two shapes are touching and their collision response has been processed. You can retrieve the collision
    -- impulse or kinetic energy at this time if you want to use it to calculate sound volumes or damage amounts.
    -- See 'Arbiter' for more info.
  , chSeparateFunc  :: !(FunPtr (CollisionCallback ()))
    -- ^ Two shapes have just stopped touching for the first time this step. To ensure that begin/separate
    -- are always called in balanced pairs, it will also be called when removing a shape while its in contact
    -- with something or when deallocating the space.
  , cpUserData      :: !DataPtr                         -- ^ userData
  } deriving Show

instance Storable CollisionHandler where
  sizeOf _ = {# sizeof cpCollisionHandler #}
  alignment _ = {# alignof cpCollisionHandler #}
  poke p (CollisionHandler typA typB beginFunc preSolveFunc postSolveFunc separateFunc userData) = do
    {# set cpCollisionHandler->typeA #} p $ fromIntegral typA
    {# set cpCollisionHandler->typeB #} p $ fromIntegral typB
    {# set cpCollisionHandler->beginFunc #} p beginFunc
    {# set cpCollisionHandler->preSolveFunc #} p preSolveFunc
    {# set cpCollisionHandler->postSolveFunc #} p postSolveFunc
    {# set cpCollisionHandler->separateFunc #} p separateFunc
    {# set cpCollisionHandler->userData #} p userData
  peek p = CollisionHandler <$> (fromIntegral <$> {# get cpCollisionHandler->typeA #} p)
                            <*> (fromIntegral <$> {# get cpCollisionHandler->typeB #} p)
                            <*> {# get cpCollisionHandler->beginFunc #} p
                            <*> {# get cpCollisionHandler->preSolveFunc #} p
                            <*> {# get cpCollisionHandler->postSolveFunc #} p
                            <*> {# get cpCollisionHandler->separateFunc #} p
                            <*> {# get cpCollisionHandler->userData #} p

foreign import ccall unsafe "wrapper"
  mkCallback' :: CollisionCallback () -> IO (FunPtr (CollisionCallback ()))

-- | Make callback. Need to free afterwards.
mkCallback :: CollisionCallback () -> IO (FunPtr (CollisionCallback ()))
mkCallback = mkCallback'

foreign import ccall unsafe "wrapper"
  mkCallbackB' :: CollisionCallback CPBool -> IO (FunPtr (CollisionCallback CPBool))

-- | Make callback. Need to free afterwards.
mkCallbackB :: CollisionCallback Bool -> IO (FunPtr (CollisionCallback CPBool))
mkCallbackB = mkCallbackB' . liftA (liftA $ liftA $ liftA $ bool 0 1)

-- | Pointer to collision handler
{# pointer *cpCollisionHandler as CollisionHandlerPtr -> CollisionHandler #}

-- | Add a 'CollisionHandler' for specific collision type pair or return the existing handler for the type pair.
-- Whenever shapes with collision types (cpShape.collision_type) a and b collide,
-- this handler will be used to process the collision events. When a new collision handler is created,
-- the callbacks will all be set to builtin callbacks that perform the default behavior
-- (call the wildcard handlers, and accept all collisions).
{# fun unsafe cpSpaceAddCollisionHandler as spaceAddCollisionHandler
  { `Space'
  , fromIntegral `CollisionType' -- ^ a
  , fromIntegral `CollisionType' -- ^ b
  } -> `CollisionHandlerPtr' #}

-- | Add a wildcard collision handler for given collision type. This handler will be used any time an object
-- with this type collides with another object, regardless of its type. A good example is a projectile
-- that should be destroyed the first time it hits anything. There may be a specific collision handler
-- and two wildcard handlers. It’s up to the specific handler to decide if and when to call the wildcard handlers
-- and what to do with their return values. (See arbiterCallWildcard* below)
-- When a new wildcard handler is created, the callbacks will all be set to builtin callbacks
-- that perform the default behavior. (accept all collisions in begin and preSolve, or do nothing for postSolve
-- and separate.
{# fun unsafe cpSpaceAddWildcardHandler as spaceAddWildcardHandler
  { `Space'
  , fromIntegral `CollisionType' -- ^ type
  } -> `CollisionHandlerPtr' #}

-- | Return a reference to the default collision handler or that is used to process all collisions
-- that don’t have a more specific handler. The default behavior for each of the callbacks
-- is to call the wildcard handlers, ANDing their return values together if applicable.
{# fun unsafe cpSpaceAddDefaultCollisionHandler as spaceAddDefaultCollisionHandler
  { `Space'
  } -> `CollisionHandlerPtr' #}

-- | Use this helper function to modify collision handler.
--
-- @
-- spaceAddCollisionHandler s t1 t2 >>= flip modifyColliionHandler (\ch -> pure ch {chSeparateFunc = separateCollback})
-- @
modifyCollisionHandler :: CollisionHandlerPtr -> (CollisionHandler -> IO CollisionHandler) -> IO ()
modifyCollisionHandler chPtr inner = peek chPtr >>= inner >>= poke chPtr

-- | Function type used for postStep callbacks. @space@ is the space the callback was registered on,
-- @obj@ is the pointer value you supplied as the key, and @data@ is a user definable pointer you can use
-- to pass in as a context value.
type PostStepFunc
  = Space   -- ^ space
  -> Ptr () -- ^ obj
  -> Ptr () -- ^ data
  -> IO ()

foreign import ccall "wrapper"
  mkPostStep :: PostStepFunc -> IO (FunPtr PostStepFunc)

-- | Add @func@ to be called before 'spaceStep' returns. @key@ and @data@ will be passed to your function.
-- Only the first callback registered for any unique value of @key@ will be recorded.
--
-- It returns 'True' if the callback is scheduled and 'False' when the key has already been used.
--
-- __The behavior of adding a postStep callback from outside of a collision handler or query callback is undefined.__
{# fun unsafe cpSpaceAddPostStepCallback as spaceAddPostStepCallback
  { `Space'            -- ^ space
  , mk* `PostStepFunc' -- ^ func
  , `Ptr ()'           -- ^ key
  , `Ptr ()'           -- ^ data
  } -> `Bool' #}
  where
    mk f = mkPostStep f `bracket` freeHaskellFunPtr