module Graphics.UI.FunGEn.Fun_Game (
Game, IOGame, runIOGame, runIOGameM, liftIOtoIOGame, liftIOtoIOGame',
getGameState, setGameState,
getGameFlags, setGameFlags,
enableGameFlags, disableGameFlags,
enableMapDrawing, disableMapDrawing,
enableObjectsDrawing, disableObjectsDrawing,
enableObjectsMoving, disableObjectsMoving,
getObjectManagers, setObjectManagers,
getGameAttribute, setGameAttribute,
createGame, funExit,
drawMap, clearScreen, getTileFromIndex, getTileFromWindowPosition, setCurrentMapIndex,
drawAllObjects, drawObject, moveAllObjects, destroyObjects, destroyObject,
getObjectsFromGroup, addObjectsToGroup, addObjectsToNewGroup, findObjectManager,findObject,
getObjectName, getObjectGroupName, getObjectAsleep, getObjectSize,
getObjectPosition, getObjectSpeed, getObjectAttribute,
setObjectPosition, setObjectAsleep, setObjectSpeed, setObjectCurrentPicture, setObjectAttribute,
replaceObject,
reverseXSpeed, reverseYSpeed,
objectsCollision, objectsFutureCollision,
objectListObjectCollision, objectListObjectFutureCollision,
objectTopMapCollision, objectBottomMapCollision, objectRightMapCollision, objectLeftMapCollision,
pointsObjectCollision, pointsObjectListCollision,
objectTopMapFutureCollision, objectBottomMapFutureCollision, objectRightMapFutureCollision, objectLeftMapFutureCollision,
printOnPrompt, printOnScreen, printText, randomFloat, randomInt, randomDouble,
showFPS,
wait
) where
import Graphics.UI.FunGEn.Fun_Types
import Graphics.UI.FunGEn.Fun_Aux
import Graphics.UI.FunGEn.Fun_Loader
import Graphics.UI.FunGEn.Fun_Text
import Graphics.UI.FunGEn.Fun_Map
import Graphics.UI.FunGEn.Fun_Objects
import Graphics.Rendering.OpenGL
import Graphics.Rendering.OpenGL.GLU
import Graphics.UI.GLUT
import Monad
import System.Exit
import Data.IORef
import Text.Printf
data Game t s u v = Game {
gameMap :: IORef (GameMap v),
gameState :: IORef u,
gameFlags :: IORef GameFlags,
objManagers :: IORef [(ObjectManager s)],
textList :: IORef [Text],
quadricObj :: QuadricPrimitive,
windowConfig :: IORef WindowConfig,
gameAttribute :: IORef t,
pictureList :: IORef [TextureObject],
fpsInfo :: IORef (Int,Int,Float)
}
newtype IOGame t s u v a = IOG (Game t s u v -> IO (Game t s u v,a))
type GameFlags = (Bool,Bool,Bool)
bindST :: IOGame t s u v a -> (a -> IOGame t s u v b) -> IOGame t s u v b
bindST (IOG x) f =
IOG (\s -> ( x s >>= \(s',v) -> let IOG g = f v in g s'))
unitST :: a -> IOGame t s u v a
unitST v = IOG (\s -> return (s,v))
instance Monad (IOGame t s u v) where
(>>=) = bindST
return = unitST
runIOGame :: IOGame t s u v a -> Game t s u v -> IO (Game t s u v,a)
runIOGame (IOG f) g = f g
runIOGameM :: IOGame t s u v a -> Game t s u v -> IO ()
runIOGameM x g = runIOGame x g >> return ()
liftIOtoIOGame :: IO a -> IOGame t s u v a
liftIOtoIOGame p =
IOG $ \s -> (do y <- p
return (s,y))
liftIOtoIOGame' :: (a -> IO ()) -> a -> IOGame t s u v ()
liftIOtoIOGame' p q =
IOG $ \s -> (do p q
return (s,()))
getMap :: IOGame t s u v (GameMap v)
getMap = IOG ( \game -> (readIORef (gameMap game) >>= \gm -> if (isMultiMap gm)
then (return (game,getCurrentMap gm))
else (return (game,gm)) ))
getRealMap :: IOGame t s u v (GameMap v)
getRealMap = IOG ( \game -> (readIORef (gameMap game) >>= \gm -> (return (game,gm)) ))
setRealMap :: GameMap v -> IOGame t s u v ()
setRealMap m = IOG ( \game -> (writeIORef (gameMap game) m >> return (game,()) ))
getGameState :: IOGame t s u v u
getGameState = IOG ( \game -> (readIORef (gameState game) >>= \gs -> return (game,gs) ))
setGameState :: u -> IOGame t s u v ()
setGameState s = IOG ( \game -> (writeIORef (gameState game) s >> return (game,()) ))
getTextList :: IOGame t s u v [Text]
getTextList = IOG ( \game -> (readIORef (textList game) >>= \tl -> return (game,tl) ))
setTextList :: [Text] -> IOGame t s u v ()
setTextList t = IOG ( \game -> (writeIORef (textList game) t >> return (game,()) ))
getGameFlags :: IOGame t s u v GameFlags
getGameFlags = IOG ( \game -> (readIORef (gameFlags game) >>= \gf -> return (game,gf) ))
setGameFlags :: GameFlags -> IOGame t s u v ()
setGameFlags f = IOG ( \game -> (writeIORef (gameFlags game) f >> return (game,()) ))
getObjectManagers :: IOGame t s u v [(ObjectManager s)]
getObjectManagers = IOG ( \game -> (readIORef (objManagers game) >>= \om -> return (game,om) ))
setObjectManagers :: [(ObjectManager s)] -> IOGame t s u v ()
setObjectManagers o = IOG ( \game -> (writeIORef (objManagers game) o >> return (game,()) ))
getQuadric :: IOGame t s u v QuadricPrimitive
getQuadric = IOG ( \game -> return (game,quadricObj game) )
getPictureList :: IOGame t s u v [TextureObject]
getPictureList = IOG ( \game -> (readIORef (pictureList game) >>= \pl -> return (game,pl) ))
getWindowConfig :: IOGame t s u v WindowConfig
getWindowConfig = IOG ( \game -> (readIORef (windowConfig game) >>= \wc -> return (game,wc) ))
getGameAttribute :: IOGame t s u v t
getGameAttribute = IOG ( \game -> (readIORef (gameAttribute game) >>= \ga -> return (game,ga) ))
setGameAttribute :: t -> IOGame t s u v ()
setGameAttribute ga = IOG ( \game -> (writeIORef (gameAttribute game) ga >> return (game,()) ))
getFpsInfo :: IOGame t s u v (Int,Int,Float)
getFpsInfo = IOG ( \game -> (readIORef (fpsInfo game) >>= \fpsi -> return (game,fpsi) ))
setFpsInfo :: (Int,Int,Float) -> IOGame t s u v ()
setFpsInfo f = IOG ( \game -> (writeIORef (fpsInfo game) f >> return (game,()) ))
createGame :: GameMap v -> [(ObjectManager s)] -> WindowConfig -> u -> t -> FilePictureList -> IO (Game t s u v)
createGame gMap objectManagers winConf gState gAttrib filePicList = do
gM <- newIORef gMap
gS <- newIORef gState
gF <- newIORef (True,True,True)
gO <- newIORef objectManagers
gT <- newIORef []
let gQ = Sphere 0 0 0
gW <- newIORef winConf
gA <- newIORef gAttrib
picList <- loadPictures filePicList
gP <- newIORef picList
gFPS <- newIORef (0,0,0.0)
return (Game {
gameMap = gM,
gameState = gS,
gameFlags = gF,
objManagers = gO,
textList = gT,
quadricObj = gQ,
windowConfig = gW,
gameAttribute = gA,
pictureList = gP,
fpsInfo = gFPS
})
loadPictures :: [(FilePath,InvList)] -> IO [TextureObject]
loadPictures pathsAndInvLists = do
bmps <- loadBitmapList (map pathAndInv2color3List pathsAndInvLists)
texBmList <- genObjectNames (length bmps)
texStuff texBmList bmps
return texBmList
funExit :: IOGame t s u v ()
funExit = liftIOtoIOGame' exitWith ExitSuccess
drawMap :: IOGame t s u v ()
drawMap = do
m <- getMap
p <- getPictureList
(_,(winWidth, winHeight),_) <- getWindowConfig
liftIOtoIOGame $ drawGameMap m (fromIntegral winWidth, fromIntegral winHeight) p
getTileFromWindowPosition :: (Double,Double) -> IOGame t s u v (Tile v)
getTileFromWindowPosition (preX,preY) = do
m <- getMap
if (isTileMap m)
then let (tileXsize,tileYsize) = getTileMapTileSize m
(scrollX,scrollY) = getTileMapScroll m
(x,y) = (preX + scrollX,preY + scrollY)
(sX,sY) = getTileMapSize m
in if (x >= sX || y >= sY)
then error ("Fun_Game.getTileFromWindowPosition error: pixel " ++ (show (x,y)) ++ " out of map range!")
else getTileFromIndex (fromEnum (y/tileXsize),fromEnum (x/tileYsize))
else error "Fun_Game.getTileFromWindowPosition error: game map is not a tile map!"
getTileFromIndex :: (Int,Int) -> IOGame t s u v (Tile v)
getTileFromIndex (x,y) = do
m <- getMap
if (isTileMap m)
then let matrix = getTileMapTileMatrix m
(mapX,mapY) = matrixSize matrix
in if (mapX >= x && mapY >= y && x >= 0 && y >= 0)
then return ( (matrix !! (mapX x)) !! y)
else error ("Fun_Game.getTileFromIndex error: tile index " ++ (show (x,y)) ++ " out of map range!")
else error "Fun_Game.getTileFromIndex error: game map is not a tile map!"
clearScreen :: Float -> Float -> Float -> IOGame t s u v ()
clearScreen r g b = liftIOtoIOGame $ clearGameScreen r g b
setCurrentMapIndex :: Int -> IOGame t s u v ()
setCurrentMapIndex i = do
m <- getRealMap
if (isMultiMap m)
then (setRealMap (updateCurrentIndex m i))
else (error "Fun_Game.setCurrentMapIndex error: you are not working with MultiMaps!")
enableGameFlags :: IOGame t s u v ()
enableGameFlags = setGameFlags (True,True,True)
disableGameFlags :: IOGame t s u v ()
disableGameFlags = setGameFlags (False,False,False)
enableMapDrawing :: IOGame t s u v ()
enableMapDrawing = do
(_,od,om) <- getGameFlags
setGameFlags (True,od,om)
disableMapDrawing :: IOGame t s u v ()
disableMapDrawing = do
(_,od,om) <- getGameFlags
setGameFlags (False,od,om)
enableObjectsDrawing :: IOGame t s u v ()
enableObjectsDrawing = do
(md,_,om) <- getGameFlags
setGameFlags (md,True,om)
disableObjectsDrawing :: IOGame t s u v ()
disableObjectsDrawing = do
(md,_,om) <- getGameFlags
setGameFlags (md,False,om)
enableObjectsMoving :: IOGame t s u v ()
enableObjectsMoving = do
(md,od,_) <- getGameFlags
setGameFlags (md,od,True)
disableObjectsMoving :: IOGame t s u v ()
disableObjectsMoving = do
(md,od,_) <- getGameFlags
setGameFlags (md,od,False)
drawAllObjects :: IOGame t s u v ()
drawAllObjects = do
o <- getObjectManagers
q <- getQuadric
p <- getPictureList
liftIOtoIOGame $ drawGameObjects o q p
drawObject :: GameObject s -> IOGame t s u v ()
drawObject o = do
q <- getQuadric
p <- getPictureList
liftIOtoIOGame $ drawGameObject o q p
moveAllObjects :: IOGame t s u v ()
moveAllObjects = do
m <- getObjectManagers
let newManagers = moveGameObjects m
setObjectManagers newManagers
destroyObjects :: [(GameObject s)] -> IOGame t s u v ()
destroyObjects [] = return ()
destroyObjects (o:os) = destroyObject o >> destroyObjects os
destroyObject :: GameObject s -> IOGame t s u v ()
destroyObject obj = do
m <- getObjectManagers
let objName = getGameObjectName obj
mngName <- getObjectGroupName obj
let newManagers = destroyGameObject objName mngName m
setObjectManagers newManagers
getObjectsFromGroup :: String -> IOGame t s u v [(GameObject s)]
getObjectsFromGroup mngName = do
mng <- findObjectManager mngName
return (getObjectManagerObjects mng)
addObjectsToGroup :: [(GameObject s)] -> String -> IOGame t s u v ()
addObjectsToGroup objs managerName = do
manager <- findObjectManager managerName
managers <- getObjectManagers
let newManagers = addObjectsToManager objs managerName managers
setObjectManagers newManagers
addObjectsToNewGroup :: [(GameObject s)] -> String -> IOGame t s u v ()
addObjectsToNewGroup objs newMngName = do
let newManager = objectGroup newMngName objs
managers <- getObjectManagers
setObjectManagers (newManager:managers)
findObjectManager :: String -> IOGame t s u v (ObjectManager s)
findObjectManager mngName = do
objectManagers <- getObjectManagers
return (searchObjectManager mngName objectManagers)
findObject :: String -> String -> IOGame t s u v (GameObject s)
findObject objName mngName = do
objectManagers <- getObjectManagers
let m = searchObjectManager mngName objectManagers
return (searchGameObject objName m)
getObjectName :: GameObject s -> IOGame t s u v String
getObjectName o = return (getGameObjectName o)
getObjectGroupName :: GameObject s -> IOGame t s u v String
getObjectGroupName o = do managers <- getObjectManagers
let obj = findObjectFromId o managers
return (getGameObjectManagerName obj)
getObjectAsleep :: GameObject s -> IOGame t s u v Bool
getObjectAsleep o = do managers <- getObjectManagers
let obj = findObjectFromId o managers
return (getGameObjectAsleep obj)
getObjectSize :: GameObject s -> IOGame t s u v (Double,Double)
getObjectSize o = do managers <- getObjectManagers
let obj = findObjectFromId o managers
return (getGameObjectSize obj)
getObjectPosition :: GameObject s -> IOGame t s u v (Double,Double)
getObjectPosition o = do managers <- getObjectManagers
let obj = findObjectFromId o managers
return (getGameObjectPosition obj)
getObjectSpeed :: GameObject s -> IOGame t s u v (Double,Double)
getObjectSpeed o = do managers <- getObjectManagers
let obj = findObjectFromId o managers
return (getGameObjectSpeed obj)
getObjectAttribute :: GameObject s -> IOGame t s u v s
getObjectAttribute o = do managers <- getObjectManagers
let obj = findObjectFromId o managers
return (getGameObjectAttribute obj)
setObjectAsleep :: Bool -> GameObject s -> IOGame t s u v ()
setObjectAsleep asleep obj = replaceObject obj (updateObjectAsleep asleep)
setObjectPosition :: (Double,Double) -> GameObject s -> IOGame t s u v ()
setObjectPosition pos obj = replaceObject obj (updateObjectPosition pos)
setObjectSpeed :: (Double,Double) -> GameObject s -> IOGame t s u v ()
setObjectSpeed speed obj = replaceObject obj (updateObjectSpeed speed)
setObjectCurrentPicture :: Int -> GameObject s -> IOGame t s u v ()
setObjectCurrentPicture n obj = do
picList <- getPictureList
replaceObject obj (updateObjectPicture n ((length picList) 1))
setObjectAttribute :: s -> GameObject s -> IOGame t s u v ()
setObjectAttribute a obj = replaceObject obj (updateObjectAttribute a)
replaceObject :: GameObject s -> (GameObject s -> GameObject s) -> IOGame t s u v ()
replaceObject obj f = do
managerName <- getObjectGroupName obj
oldManagers <- getObjectManagers
let objectId = getGameObjectId obj
newManagers = updateObject f objectId managerName oldManagers
setObjectManagers newManagers
reverseXSpeed :: GameObject s -> IOGame t s u v ()
reverseXSpeed o = do (vX,vY) <- getObjectSpeed o
setObjectSpeed (vX,vY) o
reverseYSpeed :: GameObject s -> IOGame t s u v ()
reverseYSpeed o = do (vX,vY) <- getObjectSpeed o
setObjectSpeed (vX,vY) o
objectTopMapCollision :: GameObject s -> IOGame t s u v Bool
objectTopMapCollision o = do
asleep <- getObjectAsleep o
if asleep
then (return False)
else do m <- getMap
(_,pY) <- getObjectPosition o
(_,sY) <- getObjectSize o
let (_,mY) = getMapSize m
return (pY + (sY/2) > (realToFrac mY))
objectTopMapFutureCollision :: GameObject s -> IOGame t s u v Bool
objectTopMapFutureCollision o = do
asleep <- getObjectAsleep o
if asleep
then (return False)
else do m <- getMap
(_,pY) <- getObjectPosition o
(_,vY) <- getObjectSpeed o
(_,sY) <- getObjectSize o
let (_,mY) = getMapSize m
return (pY + (sY/2) + vY > (realToFrac mY))
objectBottomMapCollision :: GameObject s -> IOGame t s u v Bool
objectBottomMapCollision o = do
asleep <- getObjectAsleep o
if asleep
then (return False)
else do (_,pY) <- getObjectPosition o
(_,sY) <- getObjectSize o
return (pY (sY/2) < 0)
objectBottomMapFutureCollision :: GameObject s -> IOGame t s u v Bool
objectBottomMapFutureCollision o = do
asleep <- getObjectAsleep o
if asleep
then (return False)
else do (_,pY) <- getObjectPosition o
(_,sY) <- getObjectSize o
(_,vY) <- getObjectSpeed o
return (pY (sY/2) + vY < 0)
objectRightMapCollision :: GameObject s -> IOGame t s u v Bool
objectRightMapCollision o = do
asleep <- getObjectAsleep o
if asleep
then (return False)
else do m <- getMap
(pX,_) <- getObjectPosition o
(sX,_) <- getObjectSize o
let (mX,_) = getMapSize m
return (pX + (sX/2) > (realToFrac mX))
objectRightMapFutureCollision :: GameObject s -> IOGame t s u v Bool
objectRightMapFutureCollision o = do
asleep <- getObjectAsleep o
if asleep
then (return False)
else do m <- getMap
(pX,_) <- getObjectPosition o
(sX,_) <- getObjectSize o
(vX,_) <- getObjectSpeed o
let (mX,_) = getMapSize m
return (pX + (sX/2) + vX > (realToFrac mX))
objectLeftMapCollision :: GameObject s -> IOGame t s u v Bool
objectLeftMapCollision o = do
asleep <- getObjectAsleep o
if asleep
then (return False)
else do (pX,_) <- getObjectPosition o
(sX,_) <- getObjectSize o
return (pX (sX/2) < 0)
objectLeftMapFutureCollision :: GameObject s -> IOGame t s u v Bool
objectLeftMapFutureCollision o = do
asleep <- getObjectAsleep o
if asleep
then (return False)
else do (pX,_) <- getObjectPosition o
(sX,_) <- getObjectSize o
(vX,_) <- getObjectSpeed o
return (pX (sX/2) + vX < 0)
objectsCollision :: GameObject s -> GameObject s -> IOGame t s u v Bool
objectsCollision o1 o2 = do
asleep1 <- getObjectAsleep o1
asleep2 <- getObjectAsleep o2
if (asleep1 || asleep2)
then (return False)
else do (p1X,p1Y) <- getObjectPosition o1
(p2X,p2Y) <- getObjectPosition o2
(s1X,s1Y) <- getObjectSize o1
(s2X,s2Y) <- getObjectSize o2
let aX1 = p1X (s1X/2)
aX2 = p1X + (s1X/2)
aY1 = p1Y (s1Y/2)
aY2 = p1Y + (s1Y/2)
bX1 = p2X (s2X/2)
bX2 = p2X + (s2X/2)
bY1 = p2Y (s2Y/2)
bY2 = p2Y + (s2Y/2)
return ((bX1 < aX2) && (aX1 < bX2) && (bY1 < aY2) && (aY1 < bY2))
objectsFutureCollision :: GameObject s -> GameObject s -> IOGame t s u v Bool
objectsFutureCollision o1 o2 = do
asleep1 <- getObjectAsleep o1
asleep2 <- getObjectAsleep o2
if (asleep1 || asleep2)
then (return False)
else do (p1X,p1Y) <- getObjectPosition o1
(p2X,p2Y) <- getObjectPosition o2
(v1X,v1Y) <- getObjectSpeed o1
(v2X,v2Y) <- getObjectSpeed o2
(s1X,s1Y) <- getObjectSize o1
(s2X,s2Y) <- getObjectSize o2
let aX1 = p1X (s1X/2) + v1X
aX2 = p1X + (s1X/2) + v1X
aY1 = p1Y (s1Y/2) + v1Y
aY2 = p1Y + (s1Y/2) + v1Y
bX1 = p2X (s2X/2) + v2X
bX2 = p2X + (s2X/2) + v2X
bY1 = p2Y (s2Y/2) + v2Y
bY2 = p2Y + (s2Y/2) + v2Y
return ((bX1 < aX2) && (aX1 < bX2) && (bY1 < aY2) && (aY1 < bY2))
objectListObjectCollision :: [(GameObject s)] -> GameObject s -> IOGame t s u v Bool
objectListObjectCollision [] _ = return False
objectListObjectCollision (a:as) b = do
col <- objectsCollision a b
if col
then (return True)
else (objectListObjectCollision as b)
objectListObjectFutureCollision :: [(GameObject s)] -> GameObject s -> IOGame t s u v Bool
objectListObjectFutureCollision [] _ = return False
objectListObjectFutureCollision (a:as) b = do
col <- objectsFutureCollision a b
if col
then (return True)
else (objectListObjectFutureCollision as b)
pointsObjectCollision :: Double -> Double -> Double -> Double -> GameObject s -> IOGame t s u v Bool
pointsObjectCollision p1X p1Y s1X s1Y o2 = do
asleep <- getObjectAsleep o2
if asleep
then (return False)
else do (p2X,p2Y) <- getObjectPosition o2
(s2X,s2Y) <- getObjectSize o2
let aX1 = p1X (s1X/2)
aX2 = p1X + (s1X/2)
aY1 = p1Y (s1Y/2)
aY2 = p1Y + (s1Y/2)
bX1 = p2X (s2X/2)
bX2 = p2X + (s2X/2)
bY1 = p2Y (s2Y/2)
bY2 = p2Y + (s2Y/2)
return ((bX1 < aX2) && (aX1 < bX2) && (bY1 < aY2) && (aY1 < bY2))
pointsObjectListCollision :: Double -> Double -> Double -> Double -> [(GameObject s)] -> IOGame t s u v Bool
pointsObjectListCollision _ _ _ _ [] = return False
pointsObjectListCollision p1X p1Y s1X s1Y (o:os) = do
col <- pointsObjectCollision p1X p1Y s1X s1Y o
if col
then (return True)
else (pointsObjectListCollision p1X p1Y s1X s1Y os)
printOnPrompt :: Show a => a -> IOGame t s u v ()
printOnPrompt a = liftIOtoIOGame' print a
printOnScreen :: String -> BitmapFont -> (Double,Double) -> Float -> Float -> Float -> IOGame t s u v ()
printOnScreen text font pos r g b = do
t <- getTextList
setTextList ([(text,font,pos,r,g,b)] ++ t)
printText :: IOGame t s u v ()
printText = do
t <- getTextList
liftIOtoIOGame $ putGameText t
setTextList []
randomInt :: (Int,Int) -> IOGame t s u v Int
randomInt (x,y) = liftIOtoIOGame $ randInt (x,y)
randomFloat :: (Float,Float) -> IOGame t s u v Float
randomFloat (x,y) = liftIOtoIOGame $ randFloat (x,y)
randomDouble :: (Double,Double) -> IOGame t s u v Double
randomDouble (x,y) = liftIOtoIOGame $ randDouble (x,y)
showFPS :: BitmapFont -> (Double,Double) -> Float -> Float -> Float -> IOGame t s u v ()
showFPS font pos r g b = do
(framei,timebasei,fps) <- getFpsInfo
timei <- getElapsedTime
let frame = (toEnum (framei + 1)) :: Float
timebase = (toEnum timebasei) :: Float
time = (toEnum timei) :: Float
if (timei timebasei > 1000)
then setFpsInfo (0,timei,(frame*(toEnum 1000)/(timetimebase)))
else setFpsInfo ((framei + 1),timebasei,fps)
printOnScreen (printf "%.1f" fps) font pos r g b
getElapsedTime :: IOGame t s u v Int
getElapsedTime = liftIOtoIOGame $ get elapsedTime
wait :: Int -> IOGame t s u v ()
wait delay = do
printText
(framei,timebasei,fps) <- getFpsInfo
setFpsInfo (framei,(timebasei + delay),fps)
startTime <- getElapsedTime
waitAux delay startTime
waitAux :: Int -> Int -> IOGame t s u v ()
waitAux delay startTime = do
presentTime <- getElapsedTime
if (presentTime startTime > delay)
then return ()
else waitAux delay startTime