Menu
Coddy logo textTech

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.

challenge icon

Desafio

Fácil

Vamos 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 base Character com campos comuns: Name (string), Health (int) e MaxHealth (int). Inclua um construtor NewCharacter e os métodos:
    • IsAlive() bool — retorna true se a saúde for maior que 0
    • Status() string — retorna o formato: [Name]: [Health]/[MaxHealth] HP
  • interfaces.go: Defina as interfaces de combate que permitem o polimorfismo:
    • Interface Attacker com Attack() int — retorna o dano causado
    • Interface Defender com TakeDamage(damage int) int — aplica a redução de dano e retorna o dano real recebido
    • Interface Ability com Use() string — retorna uma descrição do efeito da habilidade
  • characters.go: Crie três tipos de personagens que incorporam Character e implementam tanto Attacker quanto Defender:

    Warrior — possui os campos Strength (int) e Armor (int). Attack retorna Strength como dano. TakeDamage reduz o dano recebido pela Armor (mínimo de 1 de dano recebido).

    Mage — possui os campos Intelligence (int) e MagicResist (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 campos Agility (int) e Evasion (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, NewArcher que recebam o nome, saúde e seus valores de atributos específicos.

  • abilities.go: Implemente o padrão Strategy com habilidades trocáveis:

    Struct FireSpell com Power (int). Seu Use() retorna: Casts fireball for [Power] damage

    Struct IceSpell com Power (int). Seu Use() retorna: Casts ice shard for [Power] damage

    Struct PowerStrike com Multiplier (int). Seu Use() retorna: Performs power strike at [Multiplier]x damage

    Adicione um campo ability (do tipo Ability) à struct Mage, com os métodos SetAbility(a Ability) e UseAbility() string. UseAbility retorna No ability set se a habilidade for nil.

  • main.go: Crie personagens e demonstre as mecânicas de combate.

    Leia o tipo de personagem (warrior, mage ou archer), 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 personagem
    • attack — imprime [Name] attacks for [damage] damage
    • defend [amount] — aplica o dano e imprime [Name] takes [actual] damage
    • setability [type] [power] — apenas para magos, define a habilidade (o tipo é fire, ice ou strike)
    • 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 100

Sua saída deve ser:

Thorin: 100/100 HP
Thorin attacks for 15 damage
Thorin takes 12 damage
Thorin: 88/100 HP
Thorin takes 92 damage

E dado:

mage
Gandalf
80
25
10
6
attack
useability
setability fire 30
useability
setability ice 25
useability
defend 40

Sua 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 damage

E dado:

archer
Legolas
90
18
15
4
status
attack
defend 24
status

Sua saída deve ser:

Legolas: 90/90 HP
Legolas attacks for 23 damage
Legolas takes 18 damage
Legolas: 72/90 HP

Observe 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