Menu
Coddy logo textTech

Spielcharaktersystem

Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 107 von 107.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Spielcharakter-System, das Struct-Einbettung, Interfaces und das Strategy-Muster kombiniert, um flexible Kampfmechaniken zu entwickeln! Du entwirfst verschiedene Charaktertypen, die auf einzigartige Weise angreifen und verteidigen können, mit Fähigkeiten, die zur Laufzeit ausgetauscht werden können.

Du organisierst deinen Code auf fünf Dateien verteilt:

  • character.go: Definiere eine Character-Struct mit gemeinsamen Feldern: Name (string), Health (int) und MaxHealth (int). Füge einen NewCharacter-Konstruktor und folgende Methoden hinzu:
    • IsAlive() bool: gibt true zurück, wenn die Gesundheit größer als 0 ist
    • Status() string: gibt das Format [Name]: [Health]/[MaxHealth] HP zurück
  • interfaces.go: Definiere die Kampf-Interfaces, die Polymorphie ermöglichen:
    • Attacker-Interface mit Attack() int: gibt den verursachten Schaden zurück
    • Defender-Interface mit TakeDamage(damage int) int: wendet Schadensreduzierung an und gibt den tatsächlich erlittenen Schaden zurück
    • Ability-Interface mit Use() string: gibt eine Beschreibung des Fähigkeitseffekts zurück
  • characters.go: Erstelle drei Charaktertypen, die Character einbetten und sowohl Attacker als auch Defender implementieren:

    Warrior: besitzt die Felder Strength (int) und Armor (int). Der Angriff gibt Strength als Schaden zurück. TakeDamage reduziert den eingehenden Schaden um Armor (mindestens 1 erlittener Schaden).

    Mage: besitzt die Felder Intelligence (int) und MagicResist (int). Der Angriff gibt Intelligence * 2 als Schaden zurück. TakeDamage reduziert den eingehenden Schaden um MagicResist / 2 (mindestens 1 erlittener Schaden).

    Archer: besitzt die Felder Agility (int) und Evasion (int). Der Angriff gibt Agility + 5 als Schaden zurück. TakeDamage: Wenn Evasion > 10 ist, wird der Schaden um 25 % reduziert (Ganzzahldivision), andernfalls wird der volle Schaden erlitten (mindestens 1 erlittener Schaden).

    Füge für jeden Typ einen Konstruktor hinzu: NewWarrior, NewMage und NewArcher, die Name, Gesundheit und die jeweiligen spezifischen Statuswerte entgegennehmen.

  • abilities.go: Implementiere das Strategy-Muster mit austauschbaren Fähigkeiten:

    FireSpell-Struct mit Power (int). Ihr Use() gibt Folgendes zurück: Casts fireball for [Power] damage

    IceSpell-Struct mit Power (int). Ihr Use() gibt Folgendes zurück: Casts ice shard for [Power] damage

    PowerStrike-Struct mit Multiplier (int). Ihr Use() gibt Folgendes zurück: Performs power strike at [Multiplier]x damage

    Füge der Mage-Struct ein ability-Feld (vom Typ Ability) sowie die Methoden SetAbility(a Ability) und UseAbility() string hinzu. UseAbility gibt No ability set zurück, wenn ability nil ist.

  • main.go: Erstelle Charaktere und führe die Kampfmechaniken vor.

    Lies den Charaktertyp (warrior, mage oder archer) ein. Lies anschließend den Namen, die Gesundheit und die typspezifischen Statuswerte des Charakters ein. Erstelle den entsprechenden Charakter.

    Lies dann die Anzahl der Aktionen ein. Jede Aktion ist eine der folgenden:

    • status: gib den Status des Charakters aus
    • attack: gib [Name] attacks for [damage] damage aus
    • defend [amount]: wende Schaden an und gib [Name] takes [actual] damage aus
    • setability [type] [power]: setze nur für Magier eine Fähigkeit (type ist fire, ice oder strike)
    • useability: gib nur für Magier das Use()-Ergebnis der Fähigkeit aus

Die folgenden Eingaben werden bereitgestellt:

  • Charaktertyp, Name, Gesundheit und typspezifische Statuswerte
  • Anzahl der Aktionen, anschließend jede Aktion in einer eigenen Zeile

Beispielsweise bei folgender Eingabe:

warrior
Thorin
100
15
8
5
status
attack
defend 20
status
defend 100

Deine Ausgabe sollte folgendermaßen aussehen:

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

Und bei folgender Eingabe:

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

Deine Ausgabe sollte folgendermaßen aussehen:

Gandalf attacks for 50 damage
No ability set
Casts fireball for 30 damage
Casts ice shard for 25 damage
Gandalf takes 35 damage

Und bei folgender Eingabe:

archer
Legolas
90
18
15
4
status
attack
defend 24
status

Deine Ausgabe sollte folgendermaßen aussehen:

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

Beachte, wie die Struct-Einbettung es jedem Charaktertyp ermöglicht, die Felder und Methoden der grundlegenden Character-Struct gemeinsam zu verwenden, während Interfaces einen polymorphen Kampf ermöglichen, bei dem jeder Attacker Schaden verursachen und jeder Defender Schaden erleiden kann. Das Strategy-Muster zeigt seine Stärke bei den austauschbaren Fähigkeiten des Mage: Derselbe Charakter kann verschiedene Zauber verwenden, ohne dass seine grundlegende Implementierung geändert werden muss!

Probier es selbst

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)

	// Charaktertyp lesen
	scanner.Scan()
	charType := strings.TrimSpace(scanner.Text())

	// Charaktername lesen
	scanner.Scan()
	name := strings.TrimSpace(scanner.Text())

	// Gesundheit lesen
	scanner.Scan()
	health, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// Ersten typspezifischen Stat lesen
	scanner.Scan()
	stat1, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// Zweiten typspezifischen Stat lesen
	scanner.Scan()
	stat2, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// TODO: Erstelle den entsprechenden Charakter basierend auf charType
	// Verwende die Variablen: charType, name, health, stat1, stat2
	// Für warrior: stat1 = Strength, stat2 = Armor
	// Für mage: stat1 = Intelligence, stat2 = MagicResist
	// Für archer: stat1 = Agility, stat2 = Evasion

	// Anzahl der Aktionen lesen
	scanner.Scan()
	numActions, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))

	// Jede Aktion verarbeiten
	for i := 0; i < numActions; i++ {
		scanner.Scan()
		line := strings.TrimSpace(scanner.Text())
		parts := strings.Fields(line)
		action := parts[0]

		// TODO: Behandle jeden Aktionstyp:
		// - "status": Status() des Charakters ausgeben
		// - "attack": "[Name] attacks for [damage] damage" ausgeben
		// - "defend [amount]": Schaden anwenden und "[Name] takes [actual] damage" ausgeben
		// - "setability [type] [power]": für Magier Fähigkeit setzen (fire, ice oder strike)
		// - "useability": für Magier UseAbility()-Ergebnis ausgeben

		_ = action // Entferne diese Zeile bei der Implementierung
		_ = fmt.Println // Entferne diese Zeile bei der Implementierung
	}
}

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Go-Compiler