{-# LANGUAGE Arrows #-} module GameLoop where import Debug.Trace import Data.List import FRP.Yampa import FRP.Yampa.Geometry import FRP.Yampa.Utilities import Object import ObjectBehaviour import Message import AL import Global import BasicTypes import Helper import States import Lineup instance Show (SF a b) where show sf = "SF" gameLoop :: Param -> ALOut -> ALObj -> SF GameInput ALOut gameLoop param init objs0 = switch (process init objs0) $ \(time, possession, scoreHome, scoreAway) -> #if DEBUG_MODE trace ("Achtung Init: " ++ show init) $ #endif uncurry (gameLoop param) (lineupKickoff param init time possession scoreHome scoreAway) process :: ALOut -> ALObj -> SF GameInput (ALOut, Event (Time, Team, Int, Int)) process init objs0 = proc input -> do ticker <- repeatedly 0.3 iterateTimerEvents -< () timerEvent <- accum TimerCalculateAwayAI -< ticker rec oos <- core init objs0 -< ((timerEvent, input), oos) let kickOffEvent = case ((gameOO . elemsAL) oos) of OOSGame gTime (gScoreHome, gScoreAway) (GSGoal, GPTeamPosition gTeam _ _ _) _ _ -> Event (gTime, (otherTeam gTeam), gScoreHome + homeAdder gTeam, gScoreAway + awayAdder gTeam) _ -> NoEvent returnA -< (oos, kickOffEvent) where homeAdder gTeam = if gTeam == Home then 1 else 0 awayAdder gTeam = if gTeam == Away then 1 else 0 gameOO [] = error "GameLoop.hs/gameOO: No Game in Object Output" gameOO (o:os) = case o of ObjOutput oog@(OOSGame {}) _ _ _ -> oog _ -> gameOO os iterateTimerEvents :: TimerEvent -> TimerEvent iterateTimerEvents TimerCalculateHomeAI = TimerCalculateAwayAI iterateTimerEvents TimerCalculateAwayAI = TimerCalculateHomeAI -- rather more complex core, only necessary if non-static objects are needed -- (e.g. trigger objects) core :: ALOut -> ALObj -> SF (Input, ALOut) (ALOut) core init objs = proc (input, al) -> do al' <- iPre init -< al res <- core' objs -< (input, al') returnA -< res core' :: ALObj -> SF (Input, ALOut) (ALOut) core' objs = proc (input, al) -> do res <- dpSwitch route objs (arr killAndSpawn >>> notYet) (\sfs' f -> core' (f sfs')) -< (input, al) returnA -< res killAndSpawn :: ((Input, ALOut), ALOut) -> Event (ALObj -> ALObj) killAndSpawn ((input, _), oos) = foldl (mergeBy (.)) noEvent events where events :: [Event (ALObj -> ALObj)] events = [ mergeBy (.) (fmap (foldl (.) id . map (insertAL k)) (ooSpawnReq oo)) (ooKillReq oo `tag` (deleteAL k)) | (k, oo) <- assocsAL oos ] route :: (Input, ALOut) -> AL ObjId sf -> AL ObjId (ObjInput, sf) route (input, oos) = {-# SCC "route" #-} mapAL (\(oid, obj) -> (ObjInput (getObjMessages oid messages, getObjMessages oid collisions) gameState input, obj)) where messages = collectMessages oos AL kooss = mapAL (\(_,o) -> ooObsObjState o) oos collisions = collect (hits kooss) gameState = elemsAL . mapAL (\(oid', obj') -> vsFromObjOutput oid' obj') $ oos collectMessages :: ALOut -> [(ObjId, [MessageBody])] collectMessages = collect . concat . elemsAL . mapAL (ooMessages . snd) getObjMessages :: ObjId -> [(ObjId, [a])] -> [a] getObjMessages oid events = case lookup oid events of Just ms -> ms Nothing -> [] hits :: [(ObjId, ObsObjState)] -> [(ObjId, ObjId)] hits = {-# SCC "hits" #-} map createMessage . mirror . hitsAux where createMessage (k,k',_,_) = (k, k') hitsAux [] = [] -- Check each object 'State' against each other hitsAux ((k,oos):kooss) = [ (k, k',oos,oos') | (k', oos') <- kooss, oos `hit` oos' ] ++ hitsAux kooss hit :: ObsObjState -> ObsObjState -> Bool OOSPlayer {oosPos = p1} `hit` OOSPlayer {oosPos = p2} = distance p1 p2 < 3.0 OOSPlayer {oosPos = p1} `hit` OOSBall {oosPos = p2} = distance p1 p2 < 1.4 OOSBall {oosPos = p1} `hit` OOSPlayer {oosPos = p2} = distance p1 p2 < 1.4 _ `hit` _ = False mirror [] = [] mirror ((a,b,c,d):xs) = (a,b,c,d):(b,a,d,c):mirror xs