Untitled

 avatar
unknown
plain_text
a month ago
11 kB
3
Indexable
{-|
Module      : Tarefa3
Description : Mecânica do Jogo
Copyright   : Vasco Marques Machado <a109949@alunos.uminho.pt>
              Duarte Emanuel Soares Arruda <a109840@alunos.uminho.pt>


Módulo para a realização da Tarefa 3 de LI1 em 2024/25.
-}
module Tarefa3 where

import LI12425
import Tarefa1
import Tarefa2

-- | Aplica todas as outras funções responsaveis pelo desenvolvimento do jogo
atualizaJogo :: Tempo -> Jogo -> Jogo
atualizaJogo t jogo@(Jogo{torresJogo = torres, inimigosJogo = inimigos, mapaJogo = mapa, portaisJogo = portais, baseJogo = base}) = 
        jogo{torresJogo = torresFinais, inimigosJogo = inimigosFinais, portaisJogo = portaisFinais, baseJogo = baseFinal}
                where (torresFinais, inimigosAtualizados1) = atualizaTorres t torres inimigos
                      (inimigosAtualizados2) = atualizaInimigos t inimigosAtualizados1 mapa
                      (portaisFinais, inimigosAtualizados3) = atualizaPortais t portais inimigosAtualizados2
                      (inimigosFinais, baseFinal) = atualizaBase inimigosAtualizados3 base

---------------------------------------------------------------------------------------------------------------------------------------------------
-- | Responsavel por atualizar o tempo de espera da torre e aplicar o dano nos inimigos
atualizaTorres :: Tempo -> [Torre] -> [Inimigo] -> ([Torre], [Inimigo])
atualizaTorres _ [] i = ([],i)
atualizaTorres t (tr : ts) i = 
        let (torreAtualizada, inimigosAtualizados) = atualizaTorre t tr i 
            (torresRestantes, inimigosFinais) = atualizaTorres t ts inimigosAtualizados
        in (torreAtualizada : torresRestantes, inimigosFinais)

-- | Função auxiliar da atualiza torres que funciona para cada torre individualmente
atualizaTorre :: Tempo -> Torre -> [Inimigo] -> (Torre, [Inimigo])
atualizaTorre t torre@(Torre{cicloTorre = ct, tempoTorre = tt, rajadaTorre = rt, projetilTorre = pt}) inimigos
        | tt > 0 = (torre{tempoTorre = tt - t}, inimigos)
        | length (inimigosNoAlcance torre inimigos) == 0 = (torre, inimigos)
        | otherwise = (torre{tempoTorre = ct}, ia ++ ii)
                where (ia, ii) = rajadaInimigos torre (inimigosNoAlcance torre inimigos)

-- | Determina quantos inimigos é que a torre pode atacar de acordo com a rajada da torre
rajadaInimigos :: Torre -> [Inimigo] -> ([Inimigo], [Inimigo])
rajadaInimigos t i = rajadaInimigosAc t i [] 1
        where 
          rajadaInimigosAc :: Torre -> [Inimigo] -> [Inimigo] -> Int -> ([Inimigo], [Inimigo])
          rajadaInimigosAc _ [] l _ = (l, [])
          rajadaInimigosAc torre@(Torre{danoTorre = dt, rajadaTorre = rt, projetilTorre = pt}) (inimigo : ic) ia ac 
                | ac == rt = ((ia ++ [atingeInimigo torre inimigo]), ic)
                | ac < rt = rajadaInimigosAc torre ic (ia ++ [atingeInimigo torre inimigo]) (ac+1)

---------------------------------------------------------------------------------------------------------------------------------------------------
-- | Responsavel pela movimentação dos inimigos, pela aplicação dos efeitos dos projeteis neles contidos e atualiza o tempo desses mesmos projeteis
atualizaInimigos :: Tempo -> [Inimigo] -> Mapa -> [Inimigo]
atualizaInimigos t inimigos mapa = removeInimigos(atualizaTempoEfeito t (aplicaEfeitosInimigos (moveInimigos t inimigos mapa)))

-- | Deteta quando a vida do inimigo acaba e remove o inimigo do jogo
removeInimigos :: [Inimigo] -> [Inimigo]
removeInimigos [] = []
removeInimigos (inimigo@(Inimigo{vidaInimigo = vi}) : is) | vi <= 0 = removeInimigos is
                                                          | otherwise = inimigo : removeInimigos is

-- | Atualiza o tempo restante do efeito dos projeteis nos inimigos
atualizaTempoEfeito :: Tempo -> [Inimigo] -> [Inimigo]
atualizaTempoEfeito _ [] = []
atualizaTempoEfeito t (i:is) = atualizaTempoEfeitoAux t i : atualizaTempoEfeito t is

-- | Função auxiliar da atualizaTempoEfeito que funciona para cada inimigo individualmente
atualizaTempoEfeitoAux :: Tempo -> Inimigo -> Inimigo
atualizaTempoEfeitoAux t inimigo@(Inimigo{projeteisInimigo = projeteis}) = 
        let listaDeProjeteisAtualizada = removeProjeteis(map (\p -> p {duracaoProjetil = case  duracaoProjetil p of
                                                                                               Finita tp -> Finita (max 0 (tp - t))
                                                                                               Infinita -> Infinita}) projeteis)
        in (inimigo{projeteisInimigo = listaDeProjeteisAtualizada})

-- | Verifica se o projetil ainda está ativo
naoExpirou :: Projetil -> Bool
naoExpirou projetil = case duracaoProjetil projetil of
                         Infinita -> True
                         Finita t -> if t <= 0 then False else True

-- | Remove os projeteis que já não estão atutivos
removeProjeteis :: [Projetil] -> [Projetil]
removeProjeteis projeteis = filter (\p -> naoExpirou p) projeteis

-- | Aplica os efeitos dos projeteis aos inimigos
aplicaEfeitosInimigos :: [Inimigo] -> [Inimigo]
aplicaEfeitosInimigos inimigos = map aplicaEfeitosInimigo inimigos

-- | Função auxiliar da aplicaEfeitosInimigos que funciona para cada inimigo individualmente
aplicaEfeitosInimigo :: Inimigo -> Inimigo
aplicaEfeitosInimigo inimigo = foldl aplicaEfeitosInimigoAux inimigo listaEfeitos
        where listaEfeitos = projeteisInimigoConvertido (projeteisInimigo inimigo)

-- | Função auxiliar da aplicaEfeitosInimigo que deteta o tipo de projetil em questão e aplica o efeito desejado
aplicaEfeitosInimigoAux :: Inimigo -> TipoProjetil -> Inimigo
aplicaEfeitosInimigoAux (inimigo@(Inimigo{vidaInimigo = vi,velocidadeInimigo = vci})) efeito
        | efeito == Fogo = inimigo{vidaInimigo = (vi - 0.1)} 
        | efeito == Gelo = inimigo{velocidadeInimigo = (0)}
        | efeito == Resina = inimigo{velocidadeInimigo = (vci - (0.3*vci))}
        | otherwise = inimigo

-- | Responsavel pela movimentação dos inimigos
moveInimigos :: Tempo -> [Inimigo] -> Mapa -> [Inimigo]
moveInimigos t inimigos mapa = map (\inimigo -> defineDirecao t inimigo mapa) inimigos

-- | Define a direção do inimigo em função do tempo e desloca-o
defineDirecao :: Tempo -> Inimigo -> Mapa -> Inimigo
defineDirecao t inimigo@(Inimigo{posicaoInimigo = (x,y), velocidadeInimigo = vi}) mapa = case direcaoInimigo inimigo of
                             Norte -> if verificaDirecao inimigo mapa then inimigo{posicaoInimigo = (x, y + vi*t)} 
                                      else if verificaDirecao inimigo {direcaoInimigo = Este} mapa then inimigo {direcaoInimigo = Este} else inimigo {direcaoInimigo = Oeste}
                             Sul -> if verificaDirecao inimigo mapa then inimigo{posicaoInimigo = (x, y - vi*t)} 
                                    else if verificaDirecao inimigo {direcaoInimigo = Oeste} mapa then inimigo {direcaoInimigo = Oeste} else inimigo {direcaoInimigo = Este}
                             Este -> if verificaDirecao inimigo mapa then inimigo{posicaoInimigo = (x + vi*t, y)} 
                                     else if verificaDirecao inimigo {direcaoInimigo = Sul} mapa then inimigo {direcaoInimigo = Sul} else inimigo {direcaoInimigo = Norte}
                             Oeste -> if verificaDirecao inimigo mapa then inimigo{posicaoInimigo = (x - vi*t, y)} 
                                      else if verificaDirecao inimigo {direcaoInimigo = Norte} mapa then inimigo {direcaoInimigo = Norte} else inimigo {direcaoInimigo = Sul}

-- | Deteta se o inimigo deve continuar a andar na mesma direção
verificaDirecao :: Inimigo -> Mapa -> Bool
verificaDirecao inimigo@(Inimigo{posicaoInimigo = (x,y)}) mapa | direcaoInimigo inimigo == Norte && (mapa !! ((floor y) + 1) !! floor x) == Terra = True
                                                               | direcaoInimigo inimigo == Sul && (mapa !! ((floor y) - 1) !! floor x) == Terra = True
                                                               | direcaoInimigo inimigo == Este && (mapa !! floor y !! ((floor x) + 1)) == Terra = True
                                                               | direcaoInimigo inimigo == Oeste && (mapa !! floor y !! ((floor x) - 1)) == Terra = True


---------------------------------------------------------------------------------------------------------------------------------------------------
-- | Coloca os inimigos prontos a ser lançados na lista de inimigos do jogo e retira-os do portal
atualizaPortais :: Tempo -> [Portal] -> [Inimigo] -> ([Portal], [Inimigo])
atualizaPortais t portais inimigos = 
        foldl atualizaPortal ([], inimigos) portais
  where
    atualizaPortal :: ([Portal], [Inimigo]) -> Portal -> ([Portal], [Inimigo])
    atualizaPortal (portaisAtualizados, inimigos) portal =
        let (portalAtualizado, novosInimigos) = atualizaOndas t portal inimigos
        in (portaisAtualizados ++ [portalAtualizado], novosInimigos)

-- | Função auxiliar atualizaPortais que funciona para cada portal individualmente
atualizaOndas :: Tempo -> Portal -> [Inimigo] -> (Portal, [Inimigo])
atualizaOndas t portal inimigos = 
        let
                ondasAtualizadas = map atualizaEntradaOnda (ondasPortal portal)
                portalAtualizado = portal {ondasPortal = ondasAtualizadas}
                (portalFinal, inimigosNovos) = ativaInimigo portalAtualizado inimigos
        in 
                (portalFinal, inimigosNovos)
        where
                atualizaEntradaOnda :: Onda -> Onda
                atualizaEntradaOnda o = o {entradaOnda = max 0 (entradaOnda o - t)}

---------------------------------------------------------------------------------------------------------------------------------------------------
-- | Deteta quando um inimigo atinge a base e aplica o dano esperado
atualizaBase :: [Inimigo] -> Base -> ([Inimigo], Base)
atualizaBase inimigos base =
    foldl atualiza ([], base) inimigos
  where
    atualiza :: ([Inimigo], Base) -> Inimigo -> ([Inimigo], Base)
    atualiza (inimigosAtualizados, baseAtual) inimigo =
        let (inimigoAtualizado, baseAtualizada) = inimigoBase inimigo baseAtual
        in (inimigosAtualizados ++ [inimigoAtualizado], baseAtualizada)

-- | Função auxiliar da atualizaBase que funciona para cada inimigo individualmente
inimigoBase :: Inimigo -> Base -> (Inimigo, Base)
inimigoBase inimigo@(Inimigo{ataqueInimigo = ai}) base@(Base{vidaBase = vb}) 
        | floor xi == floor xb && floor yi == floor yb = (inimigo{vidaInimigo = 0}, base{vidaBase = (vb - ai)})
        | otherwise = (inimigo, base)
        where (xi, yi) = posicaoInimigo inimigo
              (xb, yb) = posicaoBase base
Leave a Comment