Untitled
unknown
plain_text
a year ago
11 kB
12
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 baseEditor is loading...
Leave a Comment