Sistema de Personagens de Jogo
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 107 de 107.
Desafio
FácilVamos construir um Sistema de Personagens de Jogo que reúne incorporação de structs (struct embedding), interfaces e o padrão Strategy para criar mecânicas de combate flexíveis! Você projetará diferentes tipos de personagens que podem atacar e defender de maneiras únicas, com habilidades que podem ser trocadas em tempo de execução.
Você organizará seu código em cinco arquivos:
character.go: Defina uma struct baseCharactercom campos comuns:Name(string),Health(int) eMaxHealth(int). Inclua um construtorNewCharactere os métodos:IsAlive() bool— retorna true se a saúde for maior que 0Status() string— retorna o formato:[Name]: [Health]/[MaxHealth] HP
interfaces.go: Defina as interfaces de combate que permitem o polimorfismo:- Interface
AttackercomAttack() int— retorna o dano causado - Interface
DefendercomTakeDamage(damage int) int— aplica a redução de dano e retorna o dano real recebido - Interface
AbilitycomUse() string— retorna uma descrição do efeito da habilidade
- Interface
characters.go: Crie três tipos de personagens que incorporamCharactere implementam tantoAttackerquantoDefender:Warrior— possui os camposStrength(int) eArmor(int). Attack retorna Strength como dano. TakeDamage reduz o dano recebido pela Armor (mínimo de 1 de dano recebido).Mage— possui os camposIntelligence(int) eMagicResist(int). Attack retorna Intelligence * 2 como dano. TakeDamage reduz o dano recebido por MagicResist / 2 (mínimo de 1 de dano recebido).Archer— possui os camposAgility(int) eEvasion(int). Attack retorna Agility + 5 como dano. TakeDamage: se Evasion > 10, reduz o dano em 25% (divisão inteira), caso contrário, recebe o dano total (mínimo de 1 de dano recebido).Inclua construtores para cada um:
NewWarrior,NewMage,NewArcherque recebam o nome, saúde e seus valores de atributos específicos.abilities.go: Implemente o padrão Strategy com habilidades trocáveis:Struct
FireSpellcomPower(int). SeuUse()retorna:Casts fireball for [Power] damageStruct
IceSpellcomPower(int). SeuUse()retorna:Casts ice shard for [Power] damageStruct
PowerStrikecomMultiplier(int). SeuUse()retorna:Performs power strike at [Multiplier]x damageAdicione um campo
ability(do tipoAbility) à struct Mage, com os métodosSetAbility(a Ability)eUseAbility() string. UseAbility retornaNo ability setse a habilidade for nil.main.go: Crie personagens e demonstre as mecânicas de combate.Leia o tipo de personagem (
warrior,mageouarcher), depois leia o nome do personagem, saúde e atributos específicos do tipo. Crie o personagem apropriado.Em seguida, leia o número de ações. Cada ação é uma das seguintes:
status— imprime o status do personagemattack— imprime[Name] attacks for [damage] damagedefend [amount]— aplica o dano e imprime[Name] takes [actual] damagesetability [type] [power]— apenas para magos, define a habilidade (o tipo éfire,iceoustrike)useability— apenas para magos, imprime o resultado do Use() da habilidade
As seguintes entradas serão fornecidas:
- Tipo de personagem, nome, saúde e atributos específicos do tipo
- Número de ações, seguido por cada ação em uma linha separada
Por exemplo, dado:
warrior
Thorin
100
15
8
5
status
attack
defend 20
status
defend 100Sua saída deve ser:
Thorin: 100/100 HP
Thorin attacks for 15 damage
Thorin takes 12 damage
Thorin: 88/100 HP
Thorin takes 92 damageE dado:
mage
Gandalf
80
25
10
6
attack
useability
setability fire 30
useability
setability ice 25
useability
defend 40Sua saída deve ser:
Gandalf attacks for 50 damage
No ability set
Casts fireball for 30 damage
Casts ice shard for 25 damage
Gandalf takes 35 damageE dado:
archer
Legolas
90
18
15
4
status
attack
defend 24
statusSua saída deve ser:
Legolas: 90/90 HP
Legolas attacks for 23 damage
Legolas takes 18 damage
Legolas: 72/90 HPObserve como a incorporação de structs permite que cada tipo de personagem compartilhe os campos e métodos base de Character, enquanto as interfaces permitem o combate polimórfico onde qualquer Attacker pode causar dano e qualquer Defender pode recebê-lo. O padrão Strategy brilha com as habilidades trocáveis do Mago — o mesmo personagem pode usar feitiços diferentes sem alterar sua implementação principal!
Experimente você mesmo
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Ler o tipo de personagem
scanner.Scan()
charType := strings.TrimSpace(scanner.Text())
// Ler o nome do personagem
scanner.Scan()
name := strings.TrimSpace(scanner.Text())
// Ler a vida
scanner.Scan()
health, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// Ler o primeiro atributo específico do tipo
scanner.Scan()
stat1, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// Ler o segundo atributo específico do tipo
scanner.Scan()
stat2, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// TODO: Criar o personagem apropriado com base em charType
// Usar variáveis: charType, name, health, stat1, stat2
// Para warrior: stat1 = Strength, stat2 = Armor
// Para mage: stat1 = Intelligence, stat2 = MagicResist
// Para archer: stat1 = Agility, stat2 = Evasion
// Ler o número de ações
scanner.Scan()
numActions, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// Processar cada ação
for i := 0; i < numActions; i++ {
scanner.Scan()
line := strings.TrimSpace(scanner.Text())
parts := strings.Fields(line)
action := parts[0]
// TODO: Lidar com cada tipo de ação:
// - "status": imprimir o Status() do personagem
// - "attack": imprimir "[Name] attacks for [damage] damage"
// - "defend [amount]": aplicar dano e imprimir "[Name] takes [actual] damage"
// - "setability [type] [power]": para mages, definir habilidade (fire, ice, ou strike)
// - "useability": para mages, imprimir o resultado de UseAbility()
_ = action // Remova esta linha ao implementar
_ = fmt.Println // Remova esta linha ao implementar
}
}
Todas as lições de Programação Orientada a Objetos
1Fundamentos de OOP em Go
Arquivos ExternosGo Workspace e MódulosPacotes e ImportaçõesNomes Exportados vs Não ExportadosIntrodução a OOP em GoStructs como ClassesDefinindo Métodos em StructsRecebedores de Ponteiro vs ValorInicialização de StructsFunções ConstrutorasRecapitulação - Calculadora Simples4Interfaces
Introdução a InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Type AssertionType SwitchComposição de InterfacesInterfaces Stringer & ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs Não ExportadosEncapsulamento em Nível de PacoteMétodos Getter e SetterOcultação de Informação em GoRecapitulação - Registros de Estudantes10Generics (Go 1.18+)
Introdução a GenericsParâmetros de TipoRestrições de TipoStructs GenéricasSolução Alternativa para Métodos GenéricosRecapitulação - Coleção Genérica2Mergulho Profundo em Tipos e Structs
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosStruct TagsStructs AnônimasStructs AninhadasZero Values e PadrõesRecapitulação - Agenda de Contatos5Composição em vez de Herança
Por que Go não possui HerançaFundamentos de Incorporação de StructsPromoção de MétodosIncorporando Múltiplas StructsIncorporação vs AgregaçãoShadowing de Métodos IncorporadosRecapitulação - Hierarquia de Funcionários8Tratamento de Erros e OOP
A Interface errorTipos de Erro PersonalizadosError Wrapping (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - File Parser3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs ReferênciaA Função new()Garbage Collection em GoRecapitulação - Construtor de Lista Encadeada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfaceColeções PolimórficasInjeção de DependênciaRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e ComunicaçãoCanais com Buffer vs. Sem BufferInstrução Selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de Structs Thread-SafeRecapitulação - Worker Pool