RPG 3: das ganze Spiel
Kurz gesagt: Das ganze Rollenspiel: Eine Klasse Game führt den Helden durch Kämpfe, Rast, Stufen und Erfahrung, und ein falscher Zug wirft GameException, die das Spiel abfängt, um weiterzumachen.
Problem
Von einem Kampf zu einem Spiel
Die Helden und die Monster kämpfen schon. Aber ein Spiel ist mehr als ein Kampf: Du erschaffst einen Helden, stellst die Monster auf, gegen die er antritt, und er geht sie nacheinander durch. Zwischen den Kämpfen ruht er sich aus oder heilt sich mit Magie.
Wenn das alles in Main steht, liegen dort der Held, ein Array mit Monstern, die Nummer des nächsten, Zähler und Prüfungen. Und wer hält den Spieler davon ab, einen Kampf ohne Helden zu starten oder von einem Barbaren Magie zu verlangen?
Der Zustand des Spiels und seine Regeln müssen an einem Ort stehen: in einer Klasse Game.
Die Idee
Die Klasse Game
Der Held und die Monster sind der Zustand des Spiels. Die Methoden von Game sind die Regeln: was der Spieler tun kann und was danach passiert. Die erste ist, einen Helden zu erstellen:
private Character hero;private Monster[] Monster = new Monster[5];private int count;private int next;private Random random;
public Game(Random random) { this.random = random;}
public void createHero(String name, String race) { if (race.equals("paladin")) { hero = new Paladin(name); } else if (race.equals("barbar")) { hero = new Barbarian(name); } else if (race.equals("hexe")) { hero = new Witch(name); } else { throw new GameException("Dieses Volk gibt es nicht: " + race); }}private Character hero;private Monster[] Monster = new Monster[5];private int count;private int next;private Random random;
public Game(Random random){ this.random = random;}
public void CreateHero(string name, string race){ if (race == "paladin") { hero = new Paladin(name); } else if (race == "barbar") { hero = new Barbarian(name); } else if (race == "hexe") { hero = new Witch(name); } else { throw new GameException("Dieses Volk gibt es nicht: " + race); }}def __init__(self, random): self._hero = None self._monsters = [None] * 5 self._count = 0 self._next = 0 self._random = random
def create_hero(self, name, race): if race == "paladin": self._hero = Paladin(name) elif race == "barbar": self._hero = Barbarian(name) elif race == "hexe": self._hero = Witch(name) else: raise GameError(f"Dieses Volk gibt es nicht: {race}")#hero = null;#monsters = new Array(5);#count = 0;#next = 0;#random;
constructor(random) { this.#random = random;}
createHero(name, race) { if (race === "paladin") { this.#hero = new Paladin(name); } else if (race === "barbar") { this.#hero = new Barbarian(name); } else if (race === "hexe") { this.#hero = new Witch(name); } else { throw new GameError(`Dieses Volk gibt es nicht: ${race}`); }}new Random(), und in den Beispielen hat er einen Startwert, damit die Ausgabe immer gleich ist.Ein unbekanntes Volk ist kein Grund, irgendeinen Helden zu erschaffen. Die Methode wirft GameException: unsere eigene Exception für jeden Zug, den die Regeln nicht erlauben. Sie bekommt weiter unten eine eigene Karte.
Die Idee
Eine Reihe von Monstern
Die Monster warten in einer Schlange: Das zuerst hinzugefügte ist der erste Kampf. Hier ist die Schlange ein Array und zwei Indizes:
public void addMonster(Monster monster) { if (count == Monster.length) { throw new GameException("Die Reihe ist voll: höchstens " + Monster.length + " Monster"); } Monster[count] = monster; count++;}
public Monster createRandomMonster() { int kind = random.nextInt(3); if (kind == 0) { return new FallenShaman(); } if (kind == 1) { return new SkeletonKing(); } return new Butcher();}
public boolean hasMonsters() { return next < count;}public void AddMonster(Monster monster){ if (count == Monster.Length) { throw new GameException("Die Reihe ist voll: höchstens " + Monster.Length + " Monster"); } Monster[count] = monster; count++;}
public Monster CreateRandomMonster(){ int kind = random.Next(3); if (kind == 0) { return new FallenShaman(); } if (kind == 1) { return new SkeletonKing(); } return new Butcher();}
public bool HasMonsters(){ return next < count;}def add_monster(self, monster): if self._count == len(self._monsters): raise GameError(f"Die Reihe ist voll: höchstens {len(self._monsters)} Monster") self._monsters[self._count] = monster self._count += 1
def create_random_monster(self): kind = self._random.randrange(3) if kind == 0: return FallenShaman() if kind == 1: return SkeletonKing() return Butcher()
def has_monsters(self): return self._next < self._countaddMonster(monster) { if (this.#count === this.#monsters.length) { throw new GameError(`Die Reihe ist voll: höchstens ${this.#monsters.length} Monster`); } this.#monsters[this.#count] = monster; this.#count++;}
createRandomMonster() { const kind = Math.floor(this.#random() * 3); if (kind === 0) { return new FallenShaman(); } if (kind === 1) { return new SkeletonKing(); } return new Butcher();}
hasMonsters() { return this.#next < this.#count;}countist die Anzahl der bisher hinzugefügten Monster. Das neue kommt nachmonsters[count].nextist das Monster, das als Nächstes kämpft. Nach jedem Kampf steigt es um 1, und wenn escounterreicht, ist die Reihe zu Ende.
AddMonster wirft GameException, bevor es außerhalb des Arrays schreibt.CreateRandomMonster ist der Bonus aus der alten Übung: ein zufällig gewähltes Monster.
Genauer, für Neugierige
-
Die alte Übung hielt die Monster in einer echten Warteschlange,
Queue<Monster>. Eine Warteschlange merkt sich selbst, welches das erste ist, und sie wird nie voll. Die ListeList<T>kommt im Modul Web und Desktop, undQueue<T>stammt aus derselben Bibliothek und wird genauso benutzt:Enqueuefügt hinten an,Dequeueentnimmt vorne. - Die Monster vor
nextbleiben in dem Array. Sie schaden nicht: Niemand liest sie mehr.
Die Idee
Stufen und Erfahrung
Nach jedem Sieg bekommt der Held Erfahrung in Höhe des Lebens des Monsters. Auf Stufe 1 braucht er 100 Erfahrung bis Stufe 2, auf Stufe 2 braucht er 200 bis Stufe 3 und so weiter. Eine neue Stufe gibt 10 Leben mehr, 3 Kraft mehr und volles Leben:
public boolean gainExperience(int exp) { experience += exp; boolean up = false; while (experience >= level * 100) { experience -= level * 100; levelUp(); up = true; } return up;}
public void levelUp() { level++; maxHealth += 10; power += 3; health = maxHealth;}public bool GainExperience(int exp){ Experience += exp; bool up = false; while (Experience >= Level * 100) { Experience -= Level * 100; LevelUp(); up = true; } return up;}
public void LevelUp(){ Level++; MaxHealth += 10; Power += 3; Health = MaxHealth;}def gain_experience(self, exp): self._experience += exp up = False while self._experience >= self._level * 100: self._experience -= self._level * 100 self.level_up() up = True return up
def level_up(self): self._level += 1 self._max_health += 10 self._power += 3 self._health = self._max_healthgainExperience(exp) { this._experience += exp; let up = false; while (this._experience >= this._level * 100) { this._experience -= this._level * 100; this.levelUp(); up = true; } return up;}
levelUp() { this._level++; this._maxHealth += 10; this._power += 3; this._health = this._maxHealth;}Die Methoden stehen in Character. Erfahrung über der Schwelle bleibt für die nächste Stufe. Die while-Schleife ist für den Fall, dass ein Sieg für zwei Stufen reicht.
Die Felder MaxHealth, Level und Experience sind neu in Character. Mit MaxHealth hebt Heilen das Leben nicht mehr über das Maximum.
Probier es aus
Auf welcher Stufe steht der Barbar?
Grok beginnt auf Stufe 1 mit Kraft 20. Er gewinnt gegen den Schamanen (60 Erfahrung) und gegen den König (90 Erfahrung).
Was gibt das Programm aus? Stufe, Erfahrung, Kraft.
public bool GainExperience(int exp){ Experience += exp; bool up = false; while (Experience >= Level * 100) { Experience -= Level * 100; LevelUp(); up = true; } return up;}
public void LevelUp(){ Level++; MaxHealth += 10; Power += 3; Health = MaxHealth;}
Character grok = new Barbarian("Grok");grok.GainExperience(60);grok.GainExperience(90);Console.WriteLine(grok.Level + " " + grok.Experience + " " + grok.Power);60 + 90 = 150 Erfahrung. Auf Stufe 1 liegt die Schwelle bei 100: Grok steigt auf Stufe 2 auf, und 50 bleiben übrig. Stufe 3 kostet 200. Die Kraft ist 20 + 3 = 23.
Sieh es dir an
Die Schlacht und die Rast
StartBattle prüft zuerst, ob der Kampf möglich ist. Dann nimmt es das nächste Monster und lässt den Kampf auf Leben und Tod aus dem zweiten Teil laufen. Es gibt zurück, ob der Held überlebt hat:
public boolean startBattle() { if (hero == null) { throw new GameException("Erstelle zuerst einen Helden"); } if (!hasMonsters()) { throw new GameException("Keine Monster in der Reihe"); } Monster monster = Monster[next]; next++; int rounds = 0; while (hero.isAlive() && monster.isAlive()) { rounds++; hero.attack(monster); if (monster.isAlive()) { monster.attack(hero); } } if (!hero.isAlive()) { System.out.println(hero.getName() + " fällt in Runde " + rounds + ". Sieger: " + monster.getName()); return false; } System.out.println(monster.getName() + " wird in " + rounds + " Runden besiegt. " + hero.getName() + ": Leben " + hero.getHealth() + "/" + hero.getMaxHealth()); if (hero.gainExperience(monster.getReward())) { System.out.println(hero.getName() + " erreicht Stufe " + hero.getLevel() + ": Leben " + hero.getMaxHealth() + ", Kraft " + hero.getPower()); } return true;}public bool StartBattle(){ if (hero == null) { throw new GameException("Erstelle zuerst einen Helden"); } if (!HasMonsters()) { throw new GameException("Keine Monster in der Reihe"); } Monster monster = Monster[next]; next++; int rounds = 0; while (hero.IsAlive() && monster.IsAlive()) { rounds++; hero.Attack(monster); if (monster.IsAlive()) { monster.Attack(hero); } } if (!hero.IsAlive()) { Console.WriteLine(hero.Name + " fällt in Runde " + rounds + ". Sieger: " + monster.Name); return false; } Console.WriteLine(monster.Name + " wird in " + rounds + " Runden besiegt. " + hero.Name + ": Leben " + hero.Health + "/" + hero.MaxHealth); if (hero.GainExperience(monster.Reward)) { Console.WriteLine(hero.Name + " erreicht Stufe " + hero.Level + ": Leben " + hero.MaxHealth + ", Kraft " + hero.Power); } return true;}def start_battle(self): if self._hero is None: raise GameError("Erstelle zuerst einen Helden") if not self.has_monsters(): raise GameError("Keine Monster in der Reihe") hero = self._hero monster = self._monsters[self._next] self._next += 1 rounds = 0 while hero.is_alive() and monster.is_alive(): rounds += 1 hero.attack(monster) if monster.is_alive(): monster.attack(hero) if not hero.is_alive(): print(f"{hero.name} fällt in Runde {rounds}. Sieger: {monster.name}") return False print(f"{monster.name} wird in {rounds} Runden besiegt. {hero.name}: Leben {hero.health}/{hero.max_health}") if hero.gain_experience(monster.reward): print(f"{hero.name} erreicht Stufe {hero.level}: Leben {hero.max_health}, Kraft {hero.power}") return TruestartBattle() { if (this.#hero === null) { throw new GameError("Erstelle zuerst einen Helden"); } if (!this.hasMonsters()) { throw new GameError("Keine Monster in der Reihe"); } const hero = this.#hero; const monster = this.#monsters[this.#next]; this.#next++; let rounds = 0; while (hero.isAlive() && monster.isAlive()) { rounds++; hero.attack(monster); if (monster.isAlive()) { monster.attack(hero); } } if (!hero.isAlive()) { console.log(`${hero.name} fällt in Runde ${rounds}. Sieger: ${monster.name}`); return false; } console.log(`${monster.name} wird in ${rounds} Runden besiegt. ${hero.name}: Leben ${hero.health}/${hero.maxHealth}`); if (hero.gainExperience(monster.reward)) { console.log(`${hero.name} erreicht Stufe ${hero.level}: Leben ${hero.maxHealth}, Kraft ${hero.power}`); } return true;}Recover ist die Wahl nach dem Kampf. Nur ein Held, der IHealable implementiert, kann Magie nutzen. Die Rast dauert 5 Züge zu je 10 Leben:
public void recover(String way) { if (way.equals("ruhe")) { for (int turn = 0; turn < 5; turn++) { hero.restore(10); } System.out.println(hero.getName() + " ruht 5 Züge lang: Leben " + hero.getHealth() + "/" + hero.getMaxHealth()); } else if (way.equals("magie")) { if (!(hero instanceof IHealable)) { throw new GameException(hero.getName() + " beherrscht keine Heilmagie"); } System.out.println(hero.useAbility()); } else { throw new GameException("Unbekannte Wahl „" + way + "“: ruhe oder magie?"); }}public void Recover(string way){ if (way == "ruhe") { for (int turn = 0; turn < 5; turn++) { hero.Restore(10); } Console.WriteLine(hero.Name + " ruht 5 Züge lang: Leben " + hero.Health + "/" + hero.MaxHealth); } else if (way == "magie") { if (!(hero is IHealable)) { throw new GameException(hero.Name + " beherrscht keine Heilmagie"); } Console.WriteLine(hero.UseAbility()); } else { throw new GameException("Unbekannte Wahl „" + way + "“: ruhe oder magie?"); }}def recover(self, way): hero = self._hero if way == "ruhe": for _ in range(5): hero.restore(10) print(f"{hero.name} ruht 5 Züge lang: Leben {hero.health}/{hero.max_health}") elif way == "magie": if not isinstance(hero, Healable): raise GameError(f"{hero.name} beherrscht keine Heilmagie") print(hero.use_ability()) else: raise GameError(f"Unbekannte Wahl „{way}“: ruhe oder magie?")recover(way) { const hero = this.#hero; if (way === "ruhe") { for (let turn = 0; turn < 5; turn++) { hero.restore(10); } console.log(`${hero.name} ruht 5 Züge lang: Leben ${hero.health}/${hero.maxHealth}`); } else if (way === "magie") { if (typeof hero.heal !== "function") { throw new GameError(`${hero.name} beherrscht keine Heilmagie`); } console.log(hero.useAbility()); } else { throw new GameError(`Unbekannte Wahl „${way}“: ruhe oder magie?`); }}In der alten Übung wartete der Held 5 Sekunden. Hier zählt die Rast in Zügen, weil in einem rundenbasierten Spiel die Zeit in Zügen gemessen wird und die Prüfung des Spiels nicht warten soll.
Sieh es dir an
Das ganze Spiel
Grok der Barbar gegen den Schamanen und zwei zufällige Monster. Nach jedem Kampf ruht er sich aus. Fällt er, ist das Spiel aus:
Game game = new Game(new Random(12));game.createHero("Grok", "barbar");game.addMonster(new FallenShaman());game.addMonster(game.createRandomMonster());game.addMonster(game.createRandomMonster());while (game.hasMonsters()) { if (!game.startBattle()) { System.out.println("Spiel vorbei"); break; } game.recover("ruhe");}Gefallener Schamane wird in 3 Runden besiegt. Grok: Leben 90/110Grok ruht 5 Züge lang: Leben 110/110Gefallener Schamane wird in 3 Runden besiegt. Grok: Leben 90/110Grok erreicht Stufe 2: Leben 120, Kraft 23Grok ruht 5 Züge lang: Leben 120/120Schlächter wird in 6 Runden besiegt. Grok: Leben 70/120Grok ruht 5 Züge lang: Leben 120/120Game game = new Game(new Random(12));game.CreateHero("Grok", "barbar");game.AddMonster(new FallenShaman());game.AddMonster(game.CreateRandomMonster());game.AddMonster(game.CreateRandomMonster());while (game.HasMonsters()){ if (!game.StartBattle()) { Console.WriteLine("Spiel vorbei"); break; } game.Recover("ruhe");}Gefallener Schamane wird in 3 Runden besiegt. Grok: Leben 90/110Grok ruht 5 Züge lang: Leben 110/110Schlächter wird in 7 Runden besiegt. Grok: Leben 48/110Grok erreicht Stufe 2: Leben 120, Kraft 23Grok ruht 5 Züge lang: Leben 120/120Skelettkönig wird in 5 Runden besiegt. Grok: Leben 68/120Grok ruht 5 Züge lang: Leben 118/120game = Game(Random(12))game.create_hero("Grok", "barbar")game.add_monster(FallenShaman())game.add_monster(game.create_random_monster())game.add_monster(game.create_random_monster())while game.has_monsters(): if not game.start_battle(): print("Spiel vorbei") break game.recover("ruhe")Gefallener Schamane wird in 3 Runden besiegt. Grok: Leben 90/110Grok ruht 5 Züge lang: Leben 110/110Skelettkönig wird in 6 Runden besiegt. Grok: Leben 45/110Grok erreicht Stufe 2: Leben 120, Kraft 23Grok ruht 5 Züge lang: Leben 120/120Skelettkönig wird in 5 Runden besiegt. Grok: Leben 68/120Grok ruht 5 Züge lang: Leben 118/120const game = new Game(makeRandom(12));game.createHero("Grok", "barbar");game.addMonster(new FallenShaman());game.addMonster(game.createRandomMonster());game.addMonster(game.createRandomMonster());while (game.hasMonsters()) { if (!game.startBattle()) { console.log("Spiel vorbei"); break; } game.recover("ruhe");}Gefallener Schamane wird in 3 Runden besiegt. Grok: Leben 90/110Grok ruht 5 Züge lang: Leben 110/110Skelettkönig wird in 6 Runden besiegt. Grok: Leben 45/110Grok erreicht Stufe 2: Leben 120, Kraft 23Grok ruht 5 Züge lang: Leben 120/120Skelettkönig wird in 5 Runden besiegt. Grok: Leben 68/120Grok ruht 5 Züge lang: Leben 118/120Game game = new Game(new Random(12));game.CreateHero("Grok", "barbar");game.AddMonster(new FallenShaman());game.AddMonster(game.CreateRandomMonster());game.AddMonster(game.CreateRandomMonster());while (game.HasMonsters()){ if (!game.StartBattle()) { Console.WriteLine("Spiel vorbei"); break; } game.Recover("ruhe");}Schritt für Schritt, in Worten
- Das Spiel bekommt einen Generator mit Startwert 12. Der Held ist Grok, und das Array hat 5 Plätze, noch leer.
- Jedes AddMonster() schreibt nach monsters[count] und erhöht count. Das zweite und das dritte sind zufällig: Schlächter und Skelettkönig.
- next ist 0: Es kommt Gefallener Schamane. Es wird in 3 Runden besiegt, und Grok hat noch 90/110.
- next wird 1. Die Rast sind 5 Züge zu je 10 Leben: 110/110.
- next ist 1: Es kommt Schlächter. Es wird in 7 Runden besiegt, und Grok hat noch 48/110. Die Erfahrung reicht für Stufe 2: Leben 120, Kraft 23.
- next wird 2. Die Rast sind 5 Züge zu je 10 Leben: 120/120.
- next ist 2: Es kommt Skelettkönig. Es wird in 5 Runden besiegt, und Grok hat noch 68/120.
- next wird 3. Die Rast sind 5 Züge zu je 10 Leben: 118/120.
- next ist 3, genauso wie count: HasMonsters() gibt false zurück, und die Schleife endet.
Die Idee
Ein falscher Zug: GameException
Der Spieler kann einen Kampf ohne Helden verlangen, ein Volk, das es nicht gibt, oder Magie von einem Barbaren. Das ist kein Fehler im Programm. Es ist ein Zug, den die Regeln nicht erlauben. Die Methoden von Game werfen dafür ihre eigene Exception:
class GameException extends RuntimeException { public GameException(String message) { super(message); }}class GameException : Exception{ public GameException(string message) : base(message) { }}class GameError(Exception): passclass GameError extends Error { constructor(message) { super(message); this.name = "GameError"; }}Das Spiel fängt nur diese eine ab, sagt dem Spieler, warum der Zug nicht erlaubt ist, und macht mit dem nächsten Zug weiter:
Game game = new Game(new Random(12));String[] commands = { "kampf", "held Grok drache", "held Grok barbar", "magie", "schlaf" };for (String command : commands) { String[] words = command.split(" "); try { if (words[0].equals("kampf")) { game.startBattle(); } else if (words[0].equals("held")) { game.createHero(words[1], words[2]); System.out.println("Der Held ist " + words[1]); } else { game.recover(words[0]); } } catch (GameException e) { System.out.println("Nicht erlaubt: " + e.getMessage()); }}Nicht erlaubt: Erstelle zuerst einen HeldenNicht erlaubt: Dieses Volk gibt es nicht: dracheDer Held ist GrokNicht erlaubt: Grok beherrscht keine HeilmagieNicht erlaubt: Unbekannte Wahl „schlaf“: ruhe oder magie?Game game = new Game(new Random(12));string[] commands = { "kampf", "held Grok drache", "held Grok barbar", "magie", "schlaf" };foreach (string command in commands){ string[] words = command.Split(' '); try { if (words[0] == "kampf") { game.StartBattle(); } else if (words[0] == "held") { game.CreateHero(words[1], words[2]); Console.WriteLine("Der Held ist " + words[1]); } else { game.Recover(words[0]); } } catch (GameException e) { Console.WriteLine("Nicht erlaubt: " + e.Message); }}Nicht erlaubt: Erstelle zuerst einen HeldenNicht erlaubt: Dieses Volk gibt es nicht: dracheDer Held ist GrokNicht erlaubt: Grok beherrscht keine HeilmagieNicht erlaubt: Unbekannte Wahl „schlaf“: ruhe oder magie?game = Game(Random(12))commands = ["kampf", "held Grok drache", "held Grok barbar", "magie", "schlaf"]for command in commands: words = command.split() try: if words[0] == "kampf": game.start_battle() elif words[0] == "held": game.create_hero(words[1], words[2]) print(f"Der Held ist {words[1]}") else: game.recover(words[0]) except GameError as e: print(f"Nicht erlaubt: {e}")Nicht erlaubt: Erstelle zuerst einen HeldenNicht erlaubt: Dieses Volk gibt es nicht: dracheDer Held ist GrokNicht erlaubt: Grok beherrscht keine HeilmagieNicht erlaubt: Unbekannte Wahl „schlaf“: ruhe oder magie?const game = new Game(makeRandom(12));const commands = ["kampf", "held Grok drache", "held Grok barbar", "magie", "schlaf"];for (const command of commands) { const words = command.split(" "); try { if (words[0] === "kampf") { game.startBattle(); } else if (words[0] === "held") { game.createHero(words[1], words[2]); console.log(`Der Held ist ${words[1]}`); } else { game.recover(words[0]); } } catch (e) { if (!(e instanceof GameError)) { throw e; } console.log(`Nicht erlaubt: ${e.message}`); }}Nicht erlaubt: Erstelle zuerst einen HeldenNicht erlaubt: Dieses Volk gibt es nicht: dracheDer Held ist GrokNicht erlaubt: Grok beherrscht keine HeilmagieNicht erlaubt: Unbekannte Wahl „schlaf“: ruhe oder magie?Warum eine eigene Exception und nicht die eingebaute ArgumentException? Weil catch (GameException e) nur die Ablehnungen nach den Regeln abfängt. Hat das Spiel einen Fehler und wirft NullReferenceException, versteckt sich dieser nicht als „Nicht erlaubt“. Er hält das Programm an, und du siehst ihn.
Probier es aus
Finde den Fehler
Das sechste Monster sollte „Die Reihe ist voll“ bekommen. Stattdessen bricht das Programm mit IndexOutOfRangeException ab. In welcher Zeile steckt der Fehler?
Wähle die Zeile mit dem Fehler.
count gleich 5, und 5 > monsters.Length ist false. Die Prüfung lässt es durch, und das sechste wird nach monsters[5] geschrieben, obwohl der letzte Platz monsters[4] ist. Das Array ist voll, wenn count gleich seiner Länge wird: count == monsters.Length.Probier es aus
Was fehlt?
Von welcher Klasse erbt GameException?
class GameException :
{
public GameException(string message) : base(message)
{
}
}Exception, die Elternklasse aller eigenen Exceptions in C#. Die Meldung geht mit : base(message) an ihren Konstruktor.Übung
Spielen und erweitern
Löse diese Aufgaben in deinem Editor. Beginne mit den Klassen aus der Lektion.
1. Ein Menü. Die Befehle kommen aus der Eingabe, einer pro Zeile, bis „ende“:
- „held Name Volk“ erstellt den Helden;
- „monster schamane“ (oder „könig“, „schlächter“) fügt ein fertiges Monster hinzu, und „monster Name Leben Kraft“ fügt dein eigenes hinzu: eine Klasse
Beastmit dem Volk „Bestie“; - „zufall“ fügt ein zufälliges Monster hinzu;
- „kampf“ startet den nächsten Kampf, und „ruhe“ und „magie“ sind die Wahl danach.
Jede Ablehnung ist eine GameException, die das Menü abfängt, mit der Meldung „Nicht erlaubt: …“. Der Startwert ist 12.
held Morena Elfheld Morena hexemonster Wolf 50 9monster drachemonster schamanezufallkampfmagiekampfruhekampfendeNicht erlaubt: Dieses Volk gibt es nicht: ElfDer Held ist MorenaIn der Reihe: WolfNicht erlaubt: Dieses Monster gibt es nicht: dracheIn der Reihe: Gefallener SchamaneIn der Reihe: SchlächterWolf wird in 4 Runden besiegt. Morena: Leben 68/95Morena ruft die Elemente an: Leben 83/95Gefallener Schamane wird in 4 Runden besiegt. Morena: Leben 47/95Morena erreicht Stufe 2: Leben 105, Kraft 19Morena ruht 5 Züge lang: Leben 105/105Schlächter wird in 7 Runden besiegt. Morena: Leben 30/1052. Fortschritt. Füge Game eine Methode ShowProgress() hinzu, die die Stufe des Helden zeigt und wie viel Erfahrung er von der Menge hat, die er für die nächste Stufe braucht. Ardan der Paladin geht durch zwei Schamanen und den Skelettkönig. Nach jedem Kampf zeigt er seinen Fortschritt und heilt sich mit Magie.
Gefallener Schamane wird in 5 Runden besiegt. Ardan: Leben 72/120Stufe 1, Erfahrung 60/100Ardan heilt sich: Leben 92/120Gefallener Schamane wird in 5 Runden besiegt. Ardan: Leben 44/120Ardan erreicht Stufe 2: Leben 130, Kraft 17Stufe 2, Erfahrung 20/200Ardan heilt sich: Leben 130/130Skelettkönig wird in 8 Runden besiegt. Ardan: Leben 18/130Stufe 2, Erfahrung 110/200Ardan heilt sich: Leben 38/1303. Ein Boss. Eine Klasse Diablo, eine Unterklasse von Monster: 160 Leben, 14 Kraft. Jeder dritte Treffer von ihm zählt doppelt. Eine Methode CanChallenge(Character hero) sagt, ob der Held mindestens auf Stufe 2 ist. Wenn nicht, wirft StartBattle GameException, und Diablo bleibt in der Reihe. Lass ihn einmal gegen Grok auf Stufe 1 laufen, dann in einem neuen Spiel nach dem Schamanen und dem König.
Nicht erlaubt: Diablo kämpft nur gegen Helden ab Stufe 2Gefallener Schamane wird in 3 Runden besiegt. Grok: Leben 90/110Grok ruht 5 Züge lang: Leben 110/110Skelettkönig wird in 6 Runden besiegt. Grok: Leben 45/110Grok erreicht Stufe 2: Leben 120, Kraft 23Grok ruht 5 Züge lang: Leben 120/120Diablo wird in 7 Runden besiegt. Grok: Leben 24/120Grok erreicht Stufe 3: Leben 130, Kraft 26Grok ruht 5 Züge lang: Leben 130/130Mach allein weiter
Die alte Übung hatte weitere Ideen. Manche brauchen ein Werkzeug, das erst später kommt:
- Fertigkeiten mit Stufen. Eine Klasse
Skillmit einem Namen und einer Stufe,UseSkill(Character target)undUpgradeSkill(). Das kannst du schon jetzt, mit einem Array von Fertigkeiten im Helden. - Inventar. Eine Klasse
Itemmit einem Namen und einer Wirkung,ApplyEffect(Character target), und einInventory, in dem Gegenstände hinzugefügt und entfernt werden. Mit einem Array ist das umständlich. Die ListeList<T>kommt im Modul Web und Desktop. - Bosse mit Voraussetzungen. Ein Boss, der bestimmte Gegenstände und Fertigkeiten verlangt, bevor er den Kampf annimmt, und nach einem Sieg Beute fallen lässt. Dafür brauchst du das Inventar von oben.
- Bildschirme. Menü, Karte, Inventar: Jeder Bildschirm ist ein Objekt mit
Show(),Hide()undUpdate(), und die offenen Bildschirme liegen auf einem Stapel, damit „zurück“ funktioniert. In C# ist der StapelStack<T>, aus derselben Bibliothek wieList<T>aus dem Modul Web und Desktop. - Speichern und Laden. Der Held, die Stufe und das beste Ergebnis werden in eine Datei geschrieben und beim nächsten Start gelesen. Dieser Kurs hat keine Lektion über Dateien. Der kürzeste Weg sind
File.WriteAllTextundFile.ReadAllTextausSystem.IO.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Interfaces · Typprüfungen · Exceptions · Werfen und eigene Exceptions
- Aufgaben zum Thema: Wir bauen ein Spiel