Untitled
{-| 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