Para este projeto, você deverá aplicar os princípios da arquitetura SOLID e os princípios de POO em uma estrutura de jogos de interpretação de papéis, mais conhecidos como jogos RPG (Role Playing Game).
-
Durante a execução dos testes, serão criados arquivos
.jsno repositório do projeto:-
Ao final da execução de cada teste é rodado um script que apaga todos os arquivos
.jsdo repositório (com exceção dos que já vão com o projeto); -
O script utiliza o binário
finddo linux;
-
🐋 Rodando no Docker vs Localmente
Rode o serviço
nodecom o comandodocker-compose up -d.
- Esse serviço irá inicializar um container chamado
trybers_and_dragons. - A partir daqui você pode rodar o container
trybers_and_dragonsvia CLI ou abri-lo no VS Code.
Use o comando
docker exec -it trybers_and_dragons bash.
- Ele te dará acesso ao terminal interativo do container criado pelo compose, que está rodando em segundo plano.
Instale as dependências [Caso existam] com
npm install
⚠ Atenção ⚠ Caso opte por utilizar o Docker, TODOS os comandos disponíveis no package.json (npm start, npm test, npm run dev, ...) devem ser executados DENTRO do container, ou seja, no terminal que aparece após a execução do comando docker exec citado acima.
⚠ Atenção ⚠ O git dentro do container não vem configurado com suas credenciais. Ou faça os commits fora do container, ou configure as suas credenciais do git dentro do container.
⚠ Atenção ⚠ Não rode o comando npm audit fix! Ele atualiza várias dependências do projeto, e essa atualização gera conflitos com o avaliador.
✨ Dica: A extensão Remote - Containers (que estará na seção de extensões recomendadas do VS Code) é indicada para que você possa desenvolver sua aplicação no container Docker direto no VS Code, como você faz com seus arquivos locais.
Instale as dependências [Caso existam] com
npm install
⚠ Atenção ⚠ Não rode o comando npm audit fix! Ele atualiza várias dependências do projeto, e essa atualização gera conflitos com o avaliador.
✨ Dica: Para rodar o projeto desta forma, obrigatoriamente você deve ter o node instalado em seu computador.
✨ Dica: O avaliador espera que a versão do node utilizada seja a 16.
👀 Observações importantes:
-
O projeto deve ser desenvolvido na ordem dos requisitos (do 1 ao 13);
-
As importações e exportações dos arquivos devem ser feitas exatamente como estão sendo solicitadas e os nomes dos arquivos/diretórios também devem seguir à risca o que é pedido no
README.md; -
Sempre que encontrar o símbolo
⚠️ pare e leia com muita atenção o que é pedido; -
Atente ao vocabulário usado no projeto, ele é super importante para te ajudar na hora de suas pesquisas. Qualquer dúvida procure a pessoa instrutora de sua turma no
Slackou nasmentorias; -
Preste atenção às convenções, isso tem ligação direta com as boas práticas de código e de comunicação entre equipes;
-
Ao longo do projeto algumas refatorações serão necessárias para que ele funcione como é esperado;
-
Dentro do diretório
src/Battleexiste um arquivoBattle.tscom uma classe abstrata de batalha criada. Os arquivos deste diretório estão comentados e podem ser usados de exemplo para a construção do projeto. Durante a execução do projeto, no requisito 6 - Crie a interfaceFighter, será pedido para você descomentar os arquivos. Só descomente quando chegar lá, senão haverá erro de lint; -
Preste atenção ao padrão do projeto, onde cada diretório possui um arquivo
index.tsexportando as informações necessárias, tomando cuidado para não ter problemas na execução dos testes.
🛠 Testes
Para executar os testes localmente, digite no terminal o comando npm test.
Especialmente no início, quando a maioria dos testes está falhando, a saída após executar os testes é bastante poluída. Você pode utilizar duas formas:
- Desabilitar temporariamente um teste utilizando a função
skipjunto à funçãodescribe. Como o nome indica, esta função "pula" um teste:
describe.skip('...', () => {})- Utilizar o comando
npm run test:fast. Através deste comando, todos os testes serão executados, entretanto, quando algum teste não for aprovado, a execução dos testes será imediatamente finalizada.
🗣 Nos dê feedbacks sobre o projeto!
Ao finalizar e submeter o projeto, não se esqueça de avaliar sua experiência preenchendo o formulário. Leva menos de 3 minutos!
FORMULÁRIO DE AVALIAÇÃO DE PROJETO
🗂 Compartilhe seu portfólio!
Você sabia que o LinkedIn é a principal rede social profissional e compartilhar o seu aprendizado lá é muito importante para quem deseja construir uma carreira de sucesso? Compartilhe esse projeto no seu LinkedIn, marque o perfil da Trybe (@trybe) e mostre para a sua rede toda a sua evolução.
🐉 Contextualizando 🐲
No universo de Trybers and Dragons - T&D, quase todos os seres que andam por essas terras pertencem a uma raça definida.
As diversas raças (como, por exemplo, Élfica, Orc ou Dwarf) definem as características das personagens dentro do jogo desde a sua criação, como os seus pontos de vida e a sua destreza. No entanto, existem seres bestiais denominados monstros que não possuem uma raça específica, mas podem lutar.
Alguns seres também possuem uma energia e, ao treinarem o uso da energia, passam a possuir um arquétipo. De modo geral, os arquétipos definem a vocação de uma personagem, suas habilidades e visão de mundo: como encaram as situações, exploram masmorras ou enfrentam monstros. Como exemplos de arquétipos presentes em T&D, podemos citar guerreiro, mago e necromante.
Boa parte dos seres podem ser considerados lutadores, bastando para isso possuir alguns atributos específicos. Em muitas ocasiões podem acontecer lutas entre personagens diversas, bem como entre personagens e monstros.
Agora, cabe a você, nobre dev, explorar essas terras e cumprir as quests que surgirão ao longo da sua incrível jornada leitura do README.
Now, follow the blind the dungeon master!
➕ Detalhes
No universo de Trybers and Dragons - T&D, quase todos os seres racionais têm uma raça e, embora todas as raças de personagens sejam humanoides, cada uma tem as suas particularidades.
A raça influencia desde a aparência geral até fatores como longevidade média, talento em determinadas habilidades ou mesmo a presença de algum sentido mais aguçado nos habitantes desse universo.
Para entender melhor um pouco da incrível diversidade que temos e as características únicas de algumas das raças de T&D, vamos começar nossa jornada com a missão de criar a classe abstrata Race.
Para que você tenha sucesso nesta quest, é importante saber que:
- O arquivo deve ser criado no diretório
src/Races/e se chamarRace.ts; - A classe
Racedeve ter os atributos privados:nameedexterity, ambos inicializados em seu construtor;- O atributo
namedever ser do tipostring; - O atributo
dexteritydever ser do tiponumber; nameedexteritydevem ser recebidos como parâmetros e inicializados no construtor.
- O atributo
- Os atributos da classe
Racepodem ser lidos, mas não podem ser alterados:namedeve retornar o tipostring;dexteritydeve retornar o tiponumber.
- A classe
Racedeve ter um método estático chamadocreatedRacesInstances, que retorna umnumber;- Esse número corresponde à quantidade de instâncias criadas a partir das classes estendidas da classe
Race; - Cada raça terá seu número máximo de instâncias, que será definido dentro de cada classe especializada;
- Na classe
Race, o método deve lançar um erro com a mensagemNot implemented.
- Esse número corresponde à quantidade de instâncias criadas a partir das classes estendidas da classe
- A classe
Racedeve ter um getter abstrato chamadomaxLifePointsque retorna umnumber;- Esse número corresponde à quantidade máxima de pontos de vida da raça;
- Cada raça terá seu número máximo de pontos, que será definido dentro de cada classe especializada;
- Na classe
Racedeve estar apenas a assinatura do método.
Dica: use a convenção de atributos privados para criar os atributos com
_e os getters para expor os atributos sem o_.
⚠️ Atenção:
- Para que os testes funcionem corretamente, a classe
Racedeve ser exportada de forma padrão (comexport default);- Deve ser criado o arquivo chamado
index.tsdentro do diretóriosrc/Races/;- A classe
Racedeve ser importada dentro deste arquivo e exportada também de forma padrão, da mesma forma que no diretóriosrc/Battle/.
🔎 O que será verificado
🐲 Para a classe Race:
- A classe
Raceexiste; - A classe
Raceé abstrata; - O método
maxLifePointsda classeRaceé abstrato; - O método
maxLifePointsao ser implementado retorna um valor numérico; - O atributo
nameda classeRacepode ser lido; - O atributo
nameda classeRaceNÃO pode ser alterado; - O atributo
dexterityda classeRacepode ser lido; - O atributo
dexterityda classe Race NÃO pode ser redefinido; - O método
createdRacesInstancesdeve existir e ser estático; - O método
createdRacesInstancesdeve lançar um erro com a mensagem "Not implemented".
➕ Detalhes
Já foi dito anteriormente que há uma diversidade de raças neste universo e agora chegou a hora de você saber mais a respeito de algumas delas. Nesta segunda quest, você irá criar classes para quatro raças que existem no mundo de T&D.
Antes de prosseguir com a missão, é muito importante saber que:
- Os arquivos devem ser criados no diretório
src/Races/; - Todas as raças devem estender da classe abstrata
Race; - As classes
Dwarf,Elf,HalflingeOrcdevem ser criadas em arquivos com exatamente esses nomes. - Cada raça deve possuir um número máximo de pontos de vida (
maxLifePoints), que deve ser inicializado em seu construtor:- A raça
Dwarfdeve receber80pontos de vida; - A raça
Elfdeve receber99pontos de vida; - A raça
Halflingdeve receber60pontos de vida; - A raça
Orcdeve receber74pontos de vida.
- A raça
- Não se esqueça de implementar o(s) método(s) necessário(s) após estender a classe abstrata
Race; - Não se esqueça de fazer a sobrescrita (
override) do(s) método(s) necessário(s).
⚠️ Atenção:
- Assim como no requisito anterior, cada uma das classes criadas (
Dwarf,Elf,HalflingeOrc) para este requisito deve ser exportada de forma padrão (comexport default).- As classes (
Dwarf,Elf,HalflingeOrc) devem ser importadas dentro desrc/Races/index.tse exportadas de forma explícita (export { class1, class2, classN }).- Não se esqueça de implementar o método
createdRacesInstancesnas classes herdeiras;
🔎 O que será verificado
🐲 Para as classe que herdam de Race:
- A classe
Dwarfexiste; - A classe
Dwarfherda deRace; - O atributo
nameda classeDwarfpode ser lido; - O atributo
dexterityda classeDwarfpode ser lido; - O método
createdRacesInstancesretorna o número correto de instâncias criadas da classeDwarf; - O atributo
maxLifePointsda classeDwarfexiste e é igual a 80; - A classe
Elfexiste; - A classe
Elfherda deRace; - O atributo
nameda classeElfpode ser lido; - O atributo
dexterityda classeElfpode ser lido; - O método
createdRacesInstancesretorna o número correto de instâncias criadas da classeElf; - O atributo
maxLifePointsda classeElfexiste e é igual a 99; - A classe
Halflingexiste; - A classe
Halflingherda deRace; - O atributo
nameda classeHalflingpode ser lido; - O atributo
dexterityda classeHalflingpode ser lido; - O método
createdRacesInstancesretorna o número correto de instâncias criadas da classeHalfling; - O atributo
maxLifePointsda classeHalflingexiste e é igual a 60; - A classe
Orcexiste; - A classe
Orcherda deRace; - O atributo
nameda classeOrcpode ser lido; - O atributo
dexterityda classeOrcpode ser lido; - O método
createdRacesInstancesretorna o número correto de instâncias criadas da classeOrc; - O atributo
maxLifePointsda classeOrcexiste e é igual a 74;
➕ Detalhes
Energia é um atributo vital para a maioria dos seres. No contexto de Trybers and Dragons, a energia gasta ao se andar, nadar, escalar ou lutar é chamada de "stamina" .
Contudo, esse universo também abriga seres capazes de usar magia. Nesses casos, a energia gasta é chamada de "mana".
Sua próxima missão é tornar possível o uso destes dois tipos de energia: "stamina" e "mana". Para isso:
- Crie uma
interfacechamadaEnergy, para isso:- Crie o arquivo
Energy.tsna raiz do diretóriosrc/. - A interface deverá possuir os atributos:
type_, do tipoEnergyType; ✨✨- Esse novo tipo
podedeve receber os valores:'mana'ou'stamina'; - O tipo
EnergyTypetambém deve ser exportado.
- Esse novo tipo
amount, do tiponumber.
- Crie o arquivo
✨ Dica de mestre: ✨
- Para implementar a
interface Energy, é necessário criar um tipo novo, otype EnergyType;
⚠️ Atenção:
- Para que os testes funcionem corretamente, a interface
Energydeve ser exportada de forma padrão ( comexport default).EnergyTypetambém deve ser exportado, mas este de forma explícita (export).
🔎 O que será verificado
🐲 Para a interface Energy:
- É possível criar uma variável com o tipo
EnergyTypee atribuir a ela o valor'mana'; - É possível criar uma variável com o tipo
EnergyTypee atribuir a ela o valor'stamina'; - É possível criar uma variável com o tipo da interface
Energye atribuir a ela o valor{ amount: 10, type_: 'stamina'}; - É possível criar uma variável com o tipo da interface
Energye atribuir a ela o valor{ amount: 45, type_: 'mana'}; - Não é possível criar uma variável com o tipo
EnergyTypee atribuir a ela um valor diferente de'mana'ou'stamina'; - Não é possível criar uma variável com o tipo da interface
Energysem atribuir a ela umamount; - Não é possível criar uma variável com o tipo da interface
Energysem atribuir a ela umtype_.
➕ Detalhes
Dentro do nosso universo, os seres têm talentos especiais e cada um desses talentos tem o seu nome dentro de T&D.
Aqui vamos ter alguns atributos super legais e necessários, que representarão o nome, a potência do seu ataque especial e o custo energético para utilizá-lo. Por isso, sua próxima quest será criar a classe abstrata Archetype.
Para que você tenha sucesso nesta quest, é importante saber que:
- O arquivo
Archetype.tsdeve ser criado no diretóriosrc/Archetypes/; - A classe
Archetypedeve ter os atributos privados:name,special,cost, que serão inicializados em seu construtor;- O atributo
namedever ser do tipostring; - O atributo
specialdever ser do tiponumber; - O atributo
costdever ser do tiponumber; namedeve ser recebido como parâmetro e inicializado no construtor;specialecostdevem ser apenas inicializados no construtor com o valor0.
- O atributo
- Os atributos da classe
Archetypepodem ser lidos, mas não podem ser alterados:namedeve retornar o tipostring;specialdeve retornar o tiponumber;costdeve retornar o tiponumber.
- A classe
Archetypedeve ter um método estático chamadocreatedArchetypeInstancesque retorna umnumber:- Esse número corresponde à quantidade de instâncias criadas a partir das classes estendidas da classe abstrata
Archetype; - Cada arquétipo terá seu número máximo de instâncias, que será definido dentro de suas classes especializadas;
- Na classe abstrata
Archetype, o método deve apenas lançar um erro com a mensagemNot implemented.
- Esse número corresponde à quantidade de instâncias criadas a partir das classes estendidas da classe abstrata
- A classe
Archetypedeve ter um getter abstrato chamadoenergyTypeque retorna umaEnergyType:- Esse tipo EnergyType corresponde ao tipo de energia que este arquétipo deve ter. (
manaoustamina) - Cada arquétipo terá o seu tipo de energia, que será definido dentro de suas classes especializadas;
- A classe abstrata
Archetypedeve conter apenas a assinatura do método.
- Esse tipo EnergyType corresponde ao tipo de energia que este arquétipo deve ter. (
⚠️ Atenção:
- Para que os testes funcionem corretamente, a classe
Archetypedeve ser exportada de forma padrão ( comexport default);- Um arquivo
index.tsdeve ser criado dentro do diretóriosrc/Archetypes/;- A classe
Archetypedeve ser importada dentro deste arquivo e exportada também de forma padrão, como feito comRace.
🔎 O que será verificado
🐲 Para a classe Archetype:
- A classe
Archetypeexiste; - A classe
Archetypeé abstrata; - O atributo
nameda classeArchetypepode ser lido; - O atributo
nameda classeArchetypenão pode ser alterado; - O atributo
specialda classeArchetypepode ser lido; - O atributo
costda classeArchetypepode ser lido; - O tipo do retorno do método
energyTypeéEnergyType;
➕ Detalhes
Como você pode imaginar, há diversos arquétipos diferentes no mundo de Trybers and Dragons, cada um com as suas peculiaridades e alinhamentos. Agora, chegou a hora de você conhecer alguns desses arquétipos. E o que poderia ser melhor para isso do que criar classes para eles? Para isto, atenção às instruções a seguir:
- Os arquivos devem ser criados no diretório
src/Archetypes/; - Todos os arquétipos devem estender da classe abstrata
Archetype. - No momento, vamos nos ater a quatro arquétipos muito comuns aos seres deste universo: (eles devem estar em quatro arquivos com os mesmos nomes)
Mage🧙♀️;Necromancer☠️;Warrior⚔️;Ranger🍃.
- Cada arquétipo possui a habilidade de causar danos em seus inimigos de forma diferente, e essa habilidade deve ser inicializada em seu construtor
- Os arquétipos
Mage🧙♀️ eNecromancer☠️ causam dano por meio de magia, através do uso demana; - Os arquétipos
Warrior⚔️ eRanger🍃 causam dano por meio de sua força, usandostamina.
- Os arquétipos
- Não se esqueça de implementar o(s) método(s) necessário(s) após estender a classe abstrata
Archetype; - Não se esqueça de fazer a sobrescrita (
override) do(s) método(s) necessário(s);
⚠️ Atenção:
- Assim como no requisito anterior, cada uma das classes criadas (
Mage,Necromancer,WarrioreRanger) para este requisito deve ser exportada de forma padrão ( comexport default);- Novamente, as classes (
Mage,Necromancer,WarrioreRanger) devem ser importadas dentro desrc/Archetypes/index.tse exportadas de forma explícita (export { class1, class2, classN }).- Não se esqueça de implementar o método
createdArchetypeInstancesnas classes herdeiras;
🔎 O que será verificado
🐲 Para as classes que herdam de Archetype:
- A classe
Mageexiste; - A classe
Mageherda deArchetype; - O atributo
nameda classeMagepode ser lido; - O método
energyTypeda ClasseMageexiste e retorna umEnergyType; - O método
createdArchetypeInstancesdeve retornar o número correto de instâncias criadas da classeMage; - A classe
Necromancerexiste; - A classe
Necromancerherda deArchetype; - O atributo
nameda classeNecromancerpode ser lido; - O atributo
energyTypeda classeNecromancerpode ser lido; - O método
createdArchetypeInstancesdeve retornar o número correto de instâncias criadas da classeNecromancer; - A classe
Rangerexiste; - A classe
Rangerherda deArchetype; - O atributo
nameda classeRangerpode ser lido; - O atributo
energyTypeda classeRangerpode ser lido; - O método
createdArchetypeInstancesdeve retornar o número correto de instâncias criadas da classeRanger; - A classe
Warriorexiste; - A classe
Warriorherda deArchetype; - O atributo
nameda classeWarriorpode ser lido; - O atributo
energyTypeda classeWarriorpode ser lido; - O método
createdArchetypeInstancesdeve retornar o número correto de instâncias criadas da classeWarrior;
➕ Detalhes
Um universo tão rico e cheio de diferentes seres, com diferentes alinhamentos, convicções e personalidades pode não ser um lugar sempre amigável. Por isso, seus habitantes têm que ser capazes de se defender ou de inventar artimanhas para se livrarem de brigas, confusões e armadilhas. Sendo assim, podemos dizer que todos os seres de T&D são, em essência, lutadores.
Para fixar bem esse conceito, preparamos para você a missão especial de criar a interface Fighter. Mas não se preocupe! Não deixaremos você dar mais nem um passo sem as informações necessárias para tirar isso de letra! Observe as orientações abaixo:
- Crie uma
interfacechamadaFighter; - O arquivo
Fighter.tsdeve ser criado no diretóriosrc/Fighter/; - A interface deverá possuir os atributos:
lifePoints, do tiponumber;strength, do tiponumber;defense, do tiponumber;energy, do tipoEnergy. ✨✨
- A interface deverá possuir os métodos:
attack(), que recebe umenemydo tipoFightercomo parâmetro e não possui retorno (void);special(), que recebe umenemydo tipoFightercomo parâmetro e não possui retorno (void); ✨✨levelUp(), que não recebe parâmetro e não possui retorno (void);receiveDamage(), que recebe umattackPointsdo tiponumbercomo parâmetro e retorne umnumber.
✨ Dica de mestre: ✨
- O atributo
energye o métodospecial()devem ser opcionais;- Pesquise sobre:
Optional PropertiesouOptional parametersem interfaces;
- Pesquise sobre:
- Agora você pode descomentar os trechos de código dos arquivos do diretório
Battle; (Battle.tseindex.ts).
⚠️ Atenção:
- Para que os testes funcionem corretamente, a interface
Fighterdeve ser exportada de forma padrão (comexport default);- Um arquivo chamado
index.tsdeve ser criado dentro do diretóriosrc/Fighter/;- A interface
Fighterdeve ser importada dentro deste arquivo e exportada também de forma padrão, como feito em requisitos anteriores.
🔎 O que será verificado
🐲 Para a interface Fighter:
- A interface
Fighterexiste; - A interface
Fighterpossui o atributolifePoints; - A interface
Fighterpossui o atributostrength; - A interface
Fighterpossui o atributodefense; - A interface
Fighterpossui o métodoattack(), que recebe umenemydo tipoFighter; - A interface
Fighterpossui o métodospecial(), que recebe umenemydo tipoFighter - A interface
Fighterpossui o métodoreceiveDamage(), que recebe umattackPointsdo tipo number; - O atributo
energydeverá ser do tipoEnergy, definido no arquivosrc/Energy.ts; - A interface
Fighterpossui o métodolevelUp(), que não recebe parâmetros nem retorna nada;
➕ Detalhes
Maravilha! Agora já temos tanto as nossas raças quanto os nossos arquétipos e interfaces definidos. Mas antes de sair por aí entrando em tavernas e calabouços, temos outra quest: criar uma personagem!
Cada personagem será composta tanto por uma raça quanto por um arquétipo. Essa classe reunirá um conjunto de características que terão o poder de fazer desse ser o mais único possível. Além disso, personagens devem possuir tudo o que se espera de alguém que luta.
As dicas para completar essa quest são:
- O arquivo deve ser criado na raiz do diretório
src/e se chamarCharacter.ts; - A classe deve implementar a interface
Fighter; - A classe
Characterdeve ter os atributos privados:race,archetype,maxLifePoints,lifePoints,strength,defense,dexterityeenergy, todos inicializados em seu construtor;- O atributo
racedeve ser do tipoRace; - O atributo
archetypedeve ser do tipoArchetype; - O atributo
maxLifePointsdeve ser do tiponumber; - O atributo
lifePointsdeve ser do tiponumber; - O atributo
strengthdeve ser do tiponumber; - O atributo
defensedeve ser do tiponumber; - O atributo
dexteritydeve ser do tiponumber; - O atributo
energydeve ser do tipoEnergy; - O atributo
namedeve ser recebido como parâmetro no construtor e deve ser usado para dar nome à sua personagem. - Devem ser inicializados no construtor:
dexteritycom um valor aleatório de no mínimo 1 e no máximo 10 pontos. ✨✨;racepor padrão com uma instância deElf(A destreza deElfdeve ser a mesma definida emdexterity);archetypepor padrão com uma instância deMage;maxLifePointspor padrão com metade domaxLifePointsda raça instanciada;lifePointspor padrão com o mesmo valor demaxLifePointsda classe;strength,defensecom valores aleatórios de no mínimo 1 e no máximo 10 pontos; ✨✨energypor padrão:type_com o mesmo valor do arquétipo instanciado;amountcom um valor aleatório de no mínimo 1 e no máximo 10 pontos. ✨✨
- O atributo
- Os atributos da classe
Characterpodem ser lidos mas não podem ser alterados:racedeve retornar o tipoRace;archetypedeve retornar o tipoArchetypelifePointsdeve retornar o tiponumber;strengthdeve retornar o tiponumber;defensedeve retornar o tiponumber;dexteritydeve retornar o tiponumber;energydeve retornar o tipoEnergy.- ✨ Lembre-se que
energyé um objeto, portanto se você retornar ele diretamente o javascript permite que as propriedades desse objetos sejam alteradas, mesmoenergysendo privado.
- ✨ Lembre-se que
- A classe
Charactertambém deve implementar os métodos estendidos dainterface Fighter;receiveDamage 😵este método recebe por parâmetro um valor (attackPoints) e as regras são:- Para calcular o dano recebido (
damage), o valor da defesa (defense) do personagem deve ser subtraído do valor do ataque recebido (attackPoints); - Se o dano calculado (
damage) for maior que0, você perde esse valor em pontos de vida (lifePoints). Se o dano calculado (damage) for igual ou menor a zero, você deve perder apenas1ponto de vida (lifePoints); - Ao receber o ataque e perder pontos de vida (
lifePoints), e se sua vida chegar a0ou menos, você deve fixá-la com o valor-1; - Ao final sempre retorne o valor atualizado de seus pontos de vida.
- Para calcular o dano recebido (
attack 🪄este método recebe por parâmetro uma pessoa inimiga (enemy) e as regras são:- Toda vez que acontecer um ataque, o inimigo recebido por parâmetro recebe um dano;
- Este dano deve ser equivalente a força (
strength) de quem ataca.
levelUp 🆙este método não recebe parâmetro e as regras são:- Sempre que este método for chamado os atributos
maxLifePoints,strength,dexterityedefenseterão um incremento de no mínimo 1 e no máximo 10 pontos; ✨✨ - Assim como os atributos anteriores o montante de energia (
amountdentro deenergy) deve ser alterado também, ele deve ficar cheio, valendo exatamente10; - O atributo
maxLifePointsdo Character nunca poderá ser maior que omaxLifePointsde sua raça (race). Se, ao incrementar o valor demaxLifePointsdo Character esse valor ficar maior do que omaxLifePointsda raça, ele deve receber o valor igual ao do da raça. Exemplo: se omaxLifePointsda raça é 100, e o do Character é 95, e ao fazer o levelUp ele ficaria 8 pontos maior, isso daria 103, que é maior do que o da raça, portanto você deveria deixar em 100. - Ao final, o atributo
lifePointstambém deve ser atualizado, recebendo o novo valor demaxLifePoints(de acordo com as regras anteriores).
- Sempre que este método for chamado os atributos
special ⚡este método não recebe parâmetro e as regras é você quem decide:- Aqui você pode expandir sua mente e realizar a lógica que achar mais interessante para um ataque especial, use tudo que aprendeu no mundo de T&D! 🐲
- Esta parte do requisito não esta sendo avalida é apenas para você se divertir aprendendo. 💚
✨ Dica de mestre: ✨
- Para gerar valores aleatórios, use a função
getRandomIntfornecida no arquivosrc/utils.ts.
⚠️ Atenção:
- Para que os testes funcionem corretamente, a classe
Characterdeve ser exportada de forma padrão ( comexport default).
🔎 O que será verificado
🐲 Para a classe Character:
- A classe
Characterexiste; - A classe
Characterimplementa a interfaceFighter; Characterpossui umaRace;Characterpossui umArchetype;Characterpossui um atributolifePoints, que pode ser lido, mas não pode ser setado;Characterpossui um atributostrength, que pode ser lido, mas não pode ser setado;Characterpossui um atributodefense, que pode ser lido, mas não pode ser setado;Characterpossui um atributoenergy, que pode ser lido, mas não pode ser setado nem ter um de seus valores internos alterados;Characterpossui um atributodexterity, que pode ser lido, mas não pode ser setado;Characterpode subir de nível através do métodolevelUp, e seus atributos (amount,maxLifePoints,strength,dexterity,defense) terão um incremento;Characterpode receber danos através do métodoreceiveDamage;Character1pode atacarCharacter2;
➕ Detalhes
Uau, o nosso universo de T&D está ficando fabuloso! No entanto, nem todo mundo que luta possui capacidades avançadas, como ter uma defesa ou realizar ataques especiais. Dito isto, vamos para mais uma quest: criar a interface lutador simples
As dicas para completar essa quest são:
- Crie uma
interfacechamadaSimpleFighter; - O arquivo
SimpleFighter.tsdeve ser criado no diretóriosrc/Fighter/. - A interface deverá possuir os atributos:
lifePoints, do tiponumber;strength, do tiponumber.
- A interface deverá possuir os métodos:
attack()que recebe umenemydo tipoSimpleFightercomo parâmetro e não possui retorno (void);receiveDamage()que recebe umattackPointsdo tiponumbercomo parâmetro e retorne umnumber;
- Aqui é um bom momento para treinarmos algumas skills deste bloco e aplicar uma refatoração, além disso você acaba adiantando uma parte do próximo requisito ✨. Utilize a segregação de interfaces, volte e observe nossa
interface Fighter.
⚠️ Atenção:
- Para que os testes funcionem corretamente, a interface
SimpleFighterdeve ser exportada de forma padrão (comexport default);- A interface
SimpleFighterdeve ser importada dentro desrc/Fighter/index.tse deve ser exportada de forma explícita (export { SimpleFighter }), como feito em requisitos anteriores.
🔎 O que será verificado
🐲 Para a interface SimpleFighter:
- A interface
SimpleFighterexiste; - A interface
SimpleFighterpossui o atributolifePoints; - A interface
SimpleFighterpossui o atributostrength; - A interface
SimpleFighterpossui o métodoattack, que recebe umenemydo tipoSimpleFighter; - A interface
SimpleFighterpossui o métodoreceiveDamage, que recebe umattackPointsdo tiponumber;
➕ Detalhes
Se existem seres que implementam a interface Fighter, deve existir seres que implementam a interface SimpleFighter também, não é ? Estes são os Monsters, criaturas bestiais que apenas atacam outros seres. Então, sua próxima quest é: criar a classe Monster!
O que você deve saber para seguir em frente:
- O arquivo deve ser criado na raiz do diretório
src/e chamarMonster.ts; - A classe deve implementar a interface
SimpleFighter; - A classe
Monsterdeve ter os atributos privadoslifePointsestrength, ambos inicializados em seu construtor:- Os atributos
lifePointsestrengthdevem ser do tiponumber; - Devem ser inicializados no construtor:
lifePointspor padrão com o valor de85;strengthpor padrão com o valor de63.
- Os atributos
- Os atributos da classe
Monsterpodem ser lidos mas não podem ser alterados:lifePointsestrengthdevem retornar o tiponumber.
- A classe
Monstertambém deve implementar os métodos estendidos dainterface SimpleFighter:receiveDamage 😵este método recebe por parâmetro um valor (attackPoints) e as regras são:- Este valor deve ser decrescido de seus pontos de vida (
lifePoints), assim causando um dano (damage); - Ao receber o ataque, sua vida nunca poderá chegar a
0, se isto acontecer seuslifePointsdevem valer-1; - Ao final o método deve retornar o valor atualizado dos pontos de vida.
- Este valor deve ser decrescido de seus pontos de vida (
attack 🪄este método recebe por parâmetro uma pessoa inimiga (enemy) e as regras são:- Toda vez que acontecer um ataque, o inimigo recebido por parâmetro recebe um dano;
- Este dano deve ser calculado a partir de
attackPointsequivalentes à força (strength) de quem ataca.
✨ Dica de mestre: ✨
- Aqui vamos precisar que os métodos de
Fighterque recebiam um inimigo do tipoFighteragora possam receber umSimpleFighter. Assim umFighterpode atacar umMonster😄.
⚠️ Atenção:
- Para que os testes funcionem corretamente, a classe
Monsterdeve ser exportada de forma padrão ( comexport default).
🔎 O que será verificado
🐲 Para a classe Monster:
- A classe
Monsterexiste; - A classe
Monsterimplementa a interfaceSimpleFighter; Monsterpossui um atributolifePoints, que pode ser lido, mas não pode ser setado;Monsterpossui um atributostrength, que pode ser lido, mas não pode ser setado;Monsterpode receber danos através do métodoreceiveDamage, fazendo com que seuslifePointsdiminuam;Monsterpode atacar umCharacter, e oCharacterreceberá dano;Characterpode atacar umMonster, e oMonsterreceberá de dano;
➕ Detalhes
A ideia do mundo de T&D ser completamente pacífico provavelmente já deve ter desaparecido da sua mente depois das suas últimas quests.
Nesse mundo, existem lutas, muitas delas inclusive épicas, denominadas Battles (batalhas). Sua representação geral/abstrata já foi fornecida anteriormente, entretanto, existem tipos específicos de batalhas. Uma dessas batalhas chamamos de PVP, batalhas entre personagens (ou player versus player), que só podem acontecer entre personagens lutadores (Fighters). 🧙♀️ ⚔️ 🧙♂️
Sua quest agora é justamente criar a classe PVP, então, você que lute ! 🗡️😂 Brincadeira! Estamos aqui para te ajudar e por isso trazemos abaixo algumas dicas preciosas para garantir a sua vitória neste requisito:
- O arquivo deve ser criado no diretório
src/Battle/e se chamarPVP.ts; - A classe
PVPdeve herdar deBattle; - A classe
Battlejá esta criada, dê uma espiada nela; 🧐 - Na criação de uma instância de
PVPé esperado que em seu construtor sejam recebidos doisCharacterslutadores, ambos inicializados lá; - Não se esqueça de fazer a sobrescrita (
override) do(s) método(s) necessário(s). ✨✨
✨ Dica de mestre: ✨
-
Use um dos players para ser parâmetro do
superna inicialização e use o métodofightdo super para dar o veredito da batalha, ou seja, sesuper.fight()retornar 1 o player quer foi usado como parâmetro dosuperna inicialização ganhou, e se retornar -1 a vitória foi do player que não foi o parâmetro dosuper; -
Aqui
podemosdevemos sobrescrever o métodofight;- No método
fightsobrescrito, implemente uma lógica de ataque entre personagens lutadores da classe; - As personagens
devem batalharaté uma das duas serderrotada, em outras palavras, a batalha só deverá terminar, quando alguma personagem ter seus pontos de vida (lifePoints) igual a-1;
- No método
-
Se necessário, refatore o que já foi feito com as interfaces
FightereSimpleFighterpara se adequarem melhor à sua nova implementação de batalha; -
Não esqueça de descomentar os trechos de código dos arquivos do diretório
Battlecomo citado nas "Dica de mestre" do requisito 6 - Crie a interfaceFighter.
⚠️ Atenção:
- Para que os testes funcionem corretamente, a classe
PVPdeve ser exportada de forma padrão (comexport default);- Novamente, dentro de
src/Battle/index.ts, a classe (PVP) deve ser importada, porém esta deve ser exportada de forma normal (export { PVP }), como feito em requisitos anteriores.
🔎 O que será verificado
🐲 Para a classe PVP:
- A classe
PVPexiste e pode ser criada uma nova instância, passando doisCharacterslutadores; - A classe
PVPpode ser utilizada onde a classeBattleé esperada e uma personagem que chamou várias vezes o levelUp e possui melhores atributos tem maiores chances de vencer; - A classe
PVPpode receber tanto doisCharactersquanto duas instâncias de uma implementação diferente deFighter;
➕ Detalhes
Nem todas as batalhas são entre personagens lutadoras (Character), afinal, há perigos à solta que espreitam ao escurecer, em densas florestas ou em calabouços profundos.
Monstros representam alguns destes perigos, assim, temos as batalhas do tipo PVE(player versus environment), em que personagens (sempre do tipo Fighter) podem lutar contra um ou mais monstros assustadores (SimpleFighter). Parece interessante, não é? Tornar isso possível é a sua próxima quest! 🧙♀️ ⚔️ 👾👹👻
Antes de prosseguir para essa nova batalha, leia atentamente as dicas abaixo !!! Só assim obteremos sucesso e prosperidade:
- O arquivo deve ser criado no diretório
src/Battle/e se chamarPVE.ts; - Lembre-se a classe
Battlejá esta criada; - Na criação de uma instância de
PVE.tsé esperado que em seu construtor seja recebido uma pessoa personagem lutadora (Character Fighter) e um array com pelo menos um monstro (Monster), ambos inicializados no construtor;- Como estamos falando de uma batalha player versus environment, este array de monstros também aceita instâncias de pessoas personagens lutadoras sendo elas simples ou não; (
Fighter,SimpleFighter)
- Como estamos falando de uma batalha player versus environment, este array de monstros também aceita instâncias de pessoas personagens lutadoras sendo elas simples ou não; (
- Não se esqueça de fazer a sobrescrita (
override) do(s) método(s) necessário(s);- Como na "Dica de mestre" do requisito anterior (
PVP), não esqueça de implementar uma lógica de luta para este requisito também; - Lembre-se, aqui a luta é de uma personagem contra apenas um oponete ou uma legião deles. Logo, para a batalha ser finalizada, a personagem principal, ou seus oponentes, deverão ter perdido os seus respectivos pontos de vida (
lifePoints).
- Como na "Dica de mestre" do requisito anterior (
⚠️ Atenção:
- Para que os testes funcionem corretamente, a classe
PVEdeve ser exportada de forma padrão (comexport default);- Novamente dentro de
src/Battle/index.tsa classe (PVE) deve ser importada, porém desta vez de forma normal (export { PVP }), como feito em requisitos anteriores.
🔎 O que será verificado
🐲 Para a classe PVE:
- A classe
PVEexiste e se pode ser criada uma nova instância, passando umCharactere um array com umMonster; - A classe
PVEpode ser utilizada onde a classeBattleé esperada. Além disso, uma personagem (Character) que chamou várias vezes o métodolevelUpe possui melhores atributos tem maiores chances de vencer uma luta contra somente umMonster, enquanto uma personagem com atributos menores perde uma luta contra diversosMonsters; - A classe
PVEpode receber tantoCharactere um array com umMonsterquanto implementações diferentes deFightereSimpleFighterque não sãoCharacternemMonster;
➕ Detalhes
Seria muito estranho se esse mundo se chamasse Trybers and Dragons e não existissem Dragons, não é mesmo?
Estes seres magníficos são representados como monstros aqui, mas com a característica especial de possuírem elevados valores de pontos de vida.
Nesta quest, você deve criar a classe Dragon, cuidando para garantir que:
- O arquivo deve ser criado na raiz de
src/e se chamarDragon.ts; - A classe
Dragondeve herdar deMonster; - Como citado acima, um Dragão tem elevados valores de pontos de vida, então em seu construtor defina o valor de
_lifePointssendo igual a 999; 🐲🐲
🐲 Dica de mestre: 🐲
- Aqui é interessante voltar no conteúdo do course sobre Herança e Interfaces e relembrar um pouco de Atributos protegidos;
⚠️ Atenção:
- Para que os testes funcionem corretamente, a classe
Dragondeve ser exportada de forma padrão ( comexport default).
🔎 O que será verificado
🐲 Para a classe Dragon:
- A classe
Dragonexiste; - A classe
Dragonherda deMonster; Dragondeve ter 999 no valor do atributolifePoints;
➕ Detalhes
Você já modelou todo o mundo de T&D, maravilha!
Agora repare que, por mais que a gente saiba o que são Monster, Character, Dragon, PVE, etc, nenhum desses foi visto em ação. Então a sua última quest para completar essa aventura é dar vida às suas personagens e criar algumas instâncias das classes criadas anteriormente. 🪄
Algumas dicas se fazem necessárias para completar sua última missão no mundo de T&D. Elas são:
- O arquivo deve ser criado na raiz de
src/e se chamarindex.ts; ⚠️ Preste bastante atenção nos nomes das variáveis/métodos e nas exportações pedidas deste último requisito; 😉.- Crie
3objetos do tipoCharacter:- As variáveis devem-se chamar
player1,player2eplayer3; - Execute algumas vezes o método
levelUpdoplayer1; - Ao final do arquivo
index.tsexporteplayer1,player2eplayer3.
- As variáveis devem-se chamar
- Crie
2objetos do tipoMonster:- As variáveis devem se chamar
monster1emonster2; monster1deve ser umMonsteremonster2deve ser umDragon;- Ao final do arquivo
index.tsexportemonster1emonster2.
- As variáveis devem se chamar
- Crie um objeto do tipo
PVP:- A variável deve se chamar
pvp; - Como parâmetro do construtor passe
player2eplayer3; - Ao final do arquivo
index.tsexportepvp. - NÃO execute o método
pvp.fight;
- A variável deve se chamar
- Crie um objeto do tipo
PVE:- A variável deve se chamar
pve; - Como parâmetro do construtor passe
player1e um array contendomonster1emonster2; - Ao final do arquivo
index.tsexportepve. - NÃO execute o método
pve.fight;
- A variável deve se chamar
- Crie uma função chamada
runBattles:- A função recebe por parâmetro um array de batalhas (
battles) e este array é do tipoBattle; ✨✨ - Dentro da função, crie uma repetição percorrendo este array e chame o método
fight; - Ao final do arquivo
index.tsexporterunBattles.
- A função recebe por parâmetro um array de batalhas (
✨ Última dica de mestre: ✨
- Lembre-se
Battlenão pode ser instanciada, pois é uma classe abstrata;
⚠️ Atenção:
- Para que os testes funcionem corretamente, os objetos/métodos criados em
src/index.tsdevem ser exportados como explicado no requisito;
🔎 O que será verificado
🐲 Para a criação de objetos no arquivo index:
- Existem 3 objetos do tipo
Characterno arquivoindex, exportados comoplayer1,player2eplayer3e o métodolevelUpfoi chamado algumas vezes emplayer1 - Existem 2 objetos do tipo
Monsterno arquivoindex, exportados comomonster1,monster2, sendo que o objetomonster2é umDragon; - Existe um objeto do tipo
PVP(com osCharactersplayer2eplayer3), exportados no arquivo index comopvpe nele NÃO foi executado o métodopvp.fight; - Existe um objeto do tipo
PVE(com oCharacterplayer1e com osMonstersmonster1emonster2), exportado no arquivoindexcomopvee nele NÃO foi executado o métodopve.fight; - Existe uma função chamada
runBattles, que recebe umarray de Battlese chama em seu interior o métodobattle.fight;