packages feed

cadence-0.1.0.0: examples/hungeon/Dungeon.hs

{-# LANGUAGE DataKinds                  #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE ScopedTypeVariables        #-}
{-# LANGUAGE TypeFamilies               #-}
{-# LANGUAGE TemplateHaskell            #-}
{-# LANGUAGE TypeApplications           #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Dungeon (stepDungeon) where

import Apecs
import Linear
import Types
import qualified Data.Set as Set
import Utils
import Data.Foldable (foldl')
import Data.Maybe
import Control.Monad
import Enemy
import Cadence

handleEnemyCollisions :: Float -> System' ()
handleEnemyCollisions dT = cmapM_ $ \(Player, Position posP, v, bbp) -> do
    enemyRes <- cfold (\acc (Enemy _, Position posE, bbE, e) ->
        let
            (Position tempPosP) = stepPositionFormula dT (Position posP) v
        in
        if checkBoundaryBoxIntersection tempPosP bbp posE bbE && isNothing acc then
            Just e
        else
            acc) Nothing
    case enemyRes of
        Just e -> do
            ce <- cfold (\_ (CombatEnemy ce) -> Just ce) Nothing
            case ce of
                Just _ -> return ()
                Nothing -> do
                    et <- get e :: System' Enemy
                    let t = case enemyType et of
                            Reaper -> Texture RenTexture { textureRef = "reaper-idle", animationFrame = Just 0 }
                            Vampire -> Texture RenTexture { textureRef = "vampire-idle", animationFrame = Just 0 }
                            Skeleton -> Texture RenTexture { textureRef = "skeleton-idle", animationFrame = Just 0 }
                            GoldenReaper -> Texture RenTexture { textureRef = "golden-reaper-idle", animationFrame = Just 0 }
                    _ <- newEntity (CombatEnemy e, Position combatEnemyPos, t, Layer 2, IsVisible True)
                    set global $ CombatTurn PlayerTurn
                    startTransition (pi / 4) 1.0 ToCombat
        Nothing -> return ()

updatePlayerMovement :: System' ()
updatePlayerMovement = do
    KeysPressed ks <- get global
    -- liftIO $ putStrLn $ "Camera angle: " ++ show ca
    mTr <- cfold (\_ (Transition {}) -> Just ()) Nothing
    if isNothing mTr then
        cmapM_ $ \(Player, Velocity _, r, e) -> case r of
            Texture rt -> do
                let (V2 vx vy) = foldl' (\(V2 ax ay) dir -> case dir of
                        GkLeft -> V2 (ax - playerSpeed) ay
                        GkRight-> V2 (ax + playerSpeed) ay
                        GkUp -> V2 ax (ay + playerSpeed)
                        GkDown -> V2 ax (ay - playerSpeed)
                        _ -> V2 ax ay) (V2 0 0) (Set.toList ks)
                    newSprite
                        | vx == 0 && vy == 0 && textureRef rt /= "player-idle" = Texture rt { textureRef = "player-idle", animationFrame = Just 0 }
                        | (vx /= 0 || vy /= 0) && textureRef rt /= "player-walk" = Texture rt { textureRef = "player-walk", animationFrame = Just 0 }
                        | otherwise = Texture rt
                set e (Velocity (V2 vx vy))
                set e newSprite
            _ -> return ()
    else
        cmapM_ $ \(Player, r, e) -> case r of
            Texture rt -> do
                set e (Velocity (V2 0 0))
                set e (Texture rt { textureRef = "player-idle", animationFrame = Just 0 })
            _ -> return ()

ladderCollision :: System' ()
ladderCollision = cmapM_ $ \(Player, Position posP, bbP) -> do
    cmapM_ $ \(Ladder, Position posL, bbL) -> when (checkBoundaryBoxIntersection posP bbP posL bbL) $ do
        mTr <- cfold (\_ (Transition {}) -> Just ()) Nothing
        case mTr of
            Nothing -> startTransition (pi / 4) 1.0 ToNextLevel
            Just _ -> return ()

heartCollision :: System' ()
heartCollision = cmapM_ $ \(Player, Position posP, bbP, Health hp, ep) -> do
    cmapM_ $ \(Heart, Position posH, bbH, eh) -> when (checkBoundaryBoxIntersection posP bbP posH bbH) $ do
        set ep $ Health $ min 100 (hp + 50)
        destroy eh (Proxy @(Heart, Position, BoundaryBox, Item, Renderable, Layer, IsVisible))

stepDungeon :: Float -> System' ()
stepDungeon dT = do
    updatePlayerMovement
    stepEnemyAI
    blockPlayer dT
    stepPosition dT
    handleEnemyCollisions dT
    ladderCollision
    heartCollision

-- Block the player from moving into walls
blockPlayer :: Float -> System' ()
blockPlayer t = cmapM $ \(Player, Position posP, Velocity (V2 vx vy), bbp) -> do
    -- Get the next position for the player
    let (Position tempPos) = stepPositionFormula t (Position posP) (Velocity (V2 vx vy))
    -- For each wall, check for collision and adjust velocity accordingly
    cfoldM (\acc (Wall, Position posW, bbw) -> do
        let
            top = checkBoundaryBoxTopIntersection tempPos bbp posW bbw
            bottom = checkBoundaryBoxBottomIntersection tempPos bbp posW bbw
            left = checkBoundaryBoxLeftIntersection tempPos bbp posW bbw
            right = checkBoundaryBoxRightIntersection tempPos bbp posW bbw
            (Velocity (V2 avx avy)) = acc
        if (top && vy < 0) || (bottom && vy > 0) then
            return $ Velocity (V2 avx 0)
        else if (left && vx > 0) || (right && vx < 0) then
            return $ Velocity (V2 0 avy)
        else
            return acc) (Velocity (V2 vx vy))