hfov-0.5: FOV.hsc
{-# OPTIONS -fffi -fglasgow-exts #-}
-- | HFOV is a library for calculating field of view in a 2D raster grid, such
-- as those found in roguelike games.
--
-- Thanks to Greg McIntyre for the original C library to which this Haskell
-- library binds. It can be found at <http://libfov.sourceforge.net/wiki>.
module FOV (
Settings,
Shape(..),
Direction(..),
newSettings, -- IO Settings
setShape, -- Settings -> Shape -> IO ()
setOpaqueApply, -- Settings -> Bool -> IO ()
circle,
beam
) where
import Foreign.Ptr
import Foreign.ForeignPtr
#include "fov.h"
type OpacityTestFn a = Ptr a -> Int -> Int -> IO Bool
foreign import ccall "wrapper" mkOpacityTestFn :: OpacityTestFn a -> IO (FunPtr (OpacityTestFn a))
type ApplyFn map src = Ptr map -> Int -> Int -> Int -> Int -> Ptr src -> IO ()
foreign import ccall "wrapper" mkApplyFn :: ApplyFn a b -> IO (FunPtr (ApplyFn a b))
data SettingsRaw
-- | Opaque data structure for holding information about FOV calculation.
newtype Settings = Settings (ForeignPtr SettingsRaw)
-- | Shape of the field.
data Shape
-- | Limit the field of view to a circle radius R by precalculating the
-- circle shape. This consumes memory at the rate of 4*(R+2) bytes per R
-- used in calls to fovCircle. Each radius is only calculated once.
= CirclePrecalculate
-- | Limit the field to a circle shape calculated on the fly.
| Circle
-- | Limit the field to an octagon with maximum radius R.
| Octagon
-- | Limit the field to an RxR square.
| Square
data Direction = East | NorthEast | North | NorthWest | West | SouthWest | South | SouthEast
rawshape :: (Num t) => Shape -> t
rawshape CirclePrecalculate = #const FOV_SHAPE_CIRCLE_PRECALCULATE
rawshape Square = #const FOV_SHAPE_SQUARE
rawshape Circle = #const FOV_SHAPE_CIRCLE
rawshape Octagon = #const FOV_SHAPE_OCTAGON
rawdirection :: (Num t) => Direction -> t
rawdirection East = #const FOV_EAST
rawdirection NorthEast = #const FOV_NORTHEAST
rawdirection North = #const FOV_NORTH
rawdirection NorthWest = #const FOV_NORTHWEST
rawdirection West = #const FOV_WEST
rawdirection SouthWest = #const FOV_SOUTHWEST
rawdirection South = #const FOV_SOUTH
rawdirection SouthEast = #const FOV_SOUTHEAST
foreign import ccall unsafe new_fov_settings :: IO (Ptr SettingsRaw)
foreign import ccall unsafe fov_settings_set_shape :: Ptr SettingsRaw -> Int -> IO ()
foreign import ccall unsafe fov_settings_set_opaque_apply :: Ptr SettingsRaw -> Int -> IO ()
foreign import ccall unsafe fov_settings_set_apply_lighting_function :: Ptr SettingsRaw -> FunPtr (ApplyFn a b) -> IO ()
foreign import ccall unsafe fov_settings_set_opacity_test_function :: Ptr SettingsRaw -> FunPtr (OpacityTestFn a) -> IO ()
foreign import ccall unsafe "&fov_settings_free" fov_settings_free :: FunPtr (Ptr SettingsRaw -> IO ())
foreign import ccall fov_circle :: Ptr SettingsRaw -> Ptr map -> Ptr src -> Int -> Int -> Int -> IO ()
foreign import ccall fov_beam :: Ptr SettingsRaw -> Ptr map -> Ptr src -> Int -> Int -> Int -> Int -> Float -> IO ()
-- | Create a new FOV settings structure.
newSettings :: IO Settings
newSettings = do
s <- new_fov_settings >>= newForeignPtr fov_settings_free
return $ Settings s
-- | Set the shape of the field of view. The default is CirclePrecalculate.
setShape :: Settings -> Shape -> IO ()
setShape (Settings fps) sh = withForeignPtr fps $ \s -> do
fov_settings_set_shape s (rawshape sh)
-- | Sets whether or not to apply lighting to opaque squares.
setOpaqueApply :: Settings -> Bool -> IO ()
setOpaqueApply (Settings fps) a = withForeignPtr fps $ \s -> do
fov_settings_set_opaque_apply s (if a then (#const FOV_OPAQUE_APPLY) else (#const FOV_OPAQUE_NOAPPLY))
-- | Cast a 360 degree field of view.
circle :: Settings -- ^The FOV settings structure to use
-> (Int,Int) -- ^The centre of the field
-> Int -- ^The radius
-> (Int -> Int -> IO ()) -- ^The function to be called in order to apply light to a certain position
-> (Int -> Int -> IO Bool) -- ^The function to determine the opacity of a certain cell
-> IO ()
circle (Settings fps) (x,y) r apply opaque = withForeignPtr fps $ \s -> do
fov_settings_set_apply_lighting_function s =<< mkApplyFn (\_ x' y' _ _ _ -> apply x' y')
fov_settings_set_opacity_test_function s =<< mkOpacityTestFn (\_ x' y' -> opaque x' y')
fov_circle s nullPtr nullPtr x y r
-- | Cast a beam.
beam :: Settings -- ^The FOV settings structure to use
-> (Int,Int) -- ^Origin of the beam
-> Int -- ^Length (radius) of the beam
-> Direction -- ^Direction of the centre of the beam
-> Float -- ^Width of the beam (in degrees)
-> (Int -> Int -> IO ()) -- ^Light application function
-> (Int -> Int -> IO Bool) -- ^Opacity test function
-> IO ()
beam (Settings fps) (x,y) r dir angle apply opaque = withForeignPtr fps $ \s -> do
fov_settings_set_apply_lighting_function s =<< mkApplyFn (\_ x' y' _ _ _ -> apply x' y')
fov_settings_set_opacity_test_function s =<< mkOpacityTestFn (\_ x' y' -> opaque x' y')
fov_beam s nullPtr nullPtr x y r (rawdirection dir) angle