{- Game.as; Mun Hon Cheong (mhch295@cse.unsw.edu.au) 2005 Game.as from Space Invaders in Yampa with additional functions -} module Game where import Random import Maybe (isJust,fromJust) import Array import AFRP import AFRPInternals (Event(..)) import AFRPGeometry import AFRPUtilities import Collision import Raybox import BSP import List import MD3 (AnimState) import Command import Diagnostics import IdentityList import Object import ObjectBehavior import Parser import Camera import Matrix import Visibility(aiVisTest) ------------------------------------------------------------------------------- -- Most of the functions found in the Space Invaders game programmed in Yampa game :: BSPMap -> [ILKey->Object] -> SF GameInput [ObsObjState] game bspmap objs = proc gi -> do rec oos <- game' ( case (listToILA $ objs) of x -> x) bspmap -< (gi, oos) returnA -< case (map ooObsObjState (elemsIL oos)) of y -> y game' :: IL Object -> BSPMap -> SF (GameInput, IL ObjOutput) (IL ObjOutput) game' objs bspmap = dpSwitch (route bspmap) objs (noEvent --> arr killOrSpawn) (\sfs' f -> game' (f sfs') bspmap) -- distributes the input route :: BSPMap -> (GameInput, IL ObjOutput) -> IL sf -> IL (ObjInput, sf) route bspmap (gi,oos) objs = mapIL routeAux objs where routeAux (k, obj) = case (find (\(x,y,z)-> k == x) states) of Just (x,y,z) -> (z ,obj) Nothing ->(ObjInput {oiHit = noEvent, oiMessage = noEvent, oiCollisionPos = (0,0,0), oiCollision = dummy, oiOnLand = False, oiVisibleObjs = noEvent, oiGameInput = gi} ,obj) messages = concat (map eventToList (elemsIL (fmap ooSendMessage oos))) states = (clips bspmap gi (assocsIL (fmap ooObsObjState oos)) (assocsIL (fmap ooObsObjState oos))) messages dummy = initCamera (80,611,60) (80,611,59) (0,1,0) killOrSpawn :: (a, IL ObjOutput) -> (Event (IL Object -> IL Object)) killOrSpawn (_, oos) = case (foldl (mergeBy (.)) noEvent es) of y -> y where es :: [Event (IL Object -> IL Object)] es = case ([ mergeBy (.) (ooKillReq oo `tag` (deleteIL k)) (fmap (foldl (.) id . map insertILA_) (ooSpawnReq oo)) | (k,oo) <- assocsIL oos ]) of x -> x ------------------------------------------------------------------------------- -- Currently there is no uniform access to the games facilities for the objects -- instead we are using clip to handle the input for each type of object which -- isn't so good. Should've thought up a better interface. clips :: BSPMap -> GameInput -> [(ILKey, ObsObjState)] -> [(ILKey, ObsObjState)] -> [(ILKey,(ILKey,Message))] -> [(ILKey,ObsObjState,ObjInput)] clips map gi [] ooses msgs = [] clips map gi ((k,oos):kooss) ooses msgs = case (clip map gi oos ooses k msgs) of x -> (k, oos ,x):(clips map gi kooss ooses msgs) clip :: BSPMap -> GameInput -> ObsObjState -> [(ILKey, ObsObjState)] -> ILKey -> [(ILKey,(ILKey,Message))] -> ObjInput clip map gi cam@(OOSCamera {newCam =cam1, oldCam = cam2}) ooses k msgs = let (camera, grounded) = clipCamera map cam1 cam2 in ObjInput {oiHit = listToEvent $ findCollidingObjects ooses (k,cam), oiMessage = findMessages k msgs, oiCollisionPos = (0,0,0), oiCollision = camera, oiOnLand = grounded, oiVisibleObjs = noEvent, oiGameInput = gi} clip map gi acube@(OOSAICube {oosNewCubePos = pos1, oosOldCubePos = pos2, oosCubeSize = sz, upperAnim = ua, health = h, target = t, lowerAnim = la}) ooses k msgs = let (clippedPos, grounded) = clipObject map pos1 pos2 sz in ObjInput {oiHit = listToEvent $ findCollidingObjects ooses (k,acube), oiCollision = initCamera (80,611,60) (80,611,59) (0,1,0), oiMessage = findMessages k msgs, oiCollisionPos = clippedPos, oiOnLand = grounded, oiVisibleObjs = (case (h == 100 && t == (0,0,0)) of -- if the ai has been hit increase its awareness True ->(listToEvent $ findVisibleTargets map ooses (k,acube) 600) _ ->(listToEvent $ findVisibleTargets map ooses (k,acube)1800)), oiGameInput = gi} clip map gi OOSRay {rayStart = pos1, rayEnd = pos2, rayUC = pos3, clipped = ff} ooses k msgs | ff == False = let (clippedPos, hasCol) = clipRay map pos3 pos1 (0,0,0) in ObjInput {oiHit = noEvent, oiMessage = findMessages k msgs, oiCollision = dummy, oiCollisionPos = fix hasCol pos2 pos3 pos1, oiOnLand = hasCol, oiVisibleObjs = noEvent, oiGameInput = gi} | otherwise = ObjInput {oiHit = noEvent, oiMessage = findMessages k msgs, oiCollision = dummy, oiCollisionPos = pos2, oiOnLand = True, oiVisibleObjs = noEvent, oiGameInput = gi} where fix clipped p2 p3 p1 | clipped == True = tryAccurate map 4 p2 p3 p1 | otherwise = pos3 dummy = initCamera (80,611,60) (80,611,59) (0,1,0) clip map gi projectile@(OOSProjectile {projectileNewPos = pos3, projectileOldPos = pos1}) ooses k msgs = let (clippedPos, hasCol) = clipRay map pos3 pos1 (0,0,0) in ObjInput {oiHit = listToEvent $ findCollidingObjects ooses (k,projectile), oiMessage = noEvent, oiCollision = (initCamera (80,611,60) (80,611,59) (0,1,0)), oiCollisionPos = clippedPos, oiOnLand = hasCol, oiVisibleObjs = noEvent, oiGameInput = gi} -- converts a list to an Event listToEvent :: [a] -> Event [a] listToEvent [] = noEvent listToEvent list = Event list -- converts an EVent to a list eventToList :: Event [a] -> [a] eventToList ev | isEvent ev = fromEvent ev | otherwise = [] -- get a more accurate approximation of the point where the -- ray strikes tryAccurate :: BSPMap -> Int -> Vec3 -> Vec3 -> Vec3 -> Vec3 tryAccurate map n dv1 dv2 vec | n == 0 = (middle dv1 dv2 vec) | snd (clipRay map (middle dv1 dv2 vec) vec (0,0,0)) == True = tryAccurate map (n-1) dv1 (middle dv1 dv2 vec) vec | otherwise = tryAccurate map (n-1) (middle dv1 dv2 vec) dv2 vec where middle v1 v2 v3 = vectorAdd v1 (vectorMult (vectorSub v2 v1) 0.5) findCollidingObjects :: [(ILKey,ObsObjState)] -> (ILKey, ObsObjState) -> [(ILKey,ObsObjState)] findCollidingObjects ooses (k,obj) | isProjectile obj = [(k',oos') | (k',oos') <-ooses, k /= k', isCamera oos', checkCollision obj oos'] | isCamera obj = [(k',oos') | (k',oos') <-ooses, k /= k', isProjectile oos', checkCollision obj oos'] | isAICube obj = [(k',oos') | (k',oos') <-ooses, k /= k', isRay oos', checkCollision obj oos'] checkCollision :: ObsObjState -> ObsObjState -> Bool checkCollision obj1 obj2 | isCamera obj2 && isProjectile obj1 = pointBox (cpos (oldCam(obj2))) (projectileOldPos obj1) (20,50,20) | isCamera obj1 && isProjectile obj2 = pointBox (cpos (oldCam(obj1))) (projectileOldPos obj2) (20,50,20) | isAICube obj1 && isRay obj2 = let (x,y,z) = oosOldCubePos obj1 (cx,cy,cz) = oosCubeSize obj1 in rayBox (rayStart obj2) (rayEnd obj2) (x-cx,y-cy,z-cz) (x+cx,y+cy,z+cz) findVisibleTargets :: BSPMap -> [(ILKey,ObsObjState)] -> (ILKey, ObsObjState) -> Int -> [(ILKey,ObsObjState)] findVisibleTargets bsp ooses (k,obj) range | isAICube obj = [(k',oos') | (k',oos') <-ooses, k /= k', isCamera oos', checkVisible bsp obj oos' range] | otherwise = [] checkVisible :: BSPMap -> ObsObjState -> ObsObjState -> Int -> Bool checkVisible bsp obj1 obj2 range | isAICube obj1 && isCamera obj2 = let (x,y,z) = oosOldCubePos obj1 (cx,cy,cz) = cpos (oldCam obj2) in aiVisTest bsp (x,y,z) (oosCubeAngle obj1) (cx,cy,cz) range findMessages :: ILKey -> [(ILKey,(ILKey,Message))] -> Event [(ILKey,Message)] findMessages key messages = listToEvent $ map snd (filter (\(destkey,srcmsg)->destkey == key ) messages)