RPG 2: битката
Проблем
Срещу кого се бият героите?
Героите от първата част вече имат живот и умения. Сега им трябват врагове: чудовищата, които се надигат от дълбините на Арканум. Падналият шаман е слаб, Скелетният крал е покрит с костна броня, а Касапинът става по-силен с всеки удар.
Чудовището не е герой. То няма умение, а и героите се бият срещу чудовищата. Затова не може да наследи Character.
Но ударът трябва да работи и в двете посоки. А някои от участниците се защитават: кралят спира част от щетата, варваринът също. Ако атаката проверява кой е кой с if за всеки клас, всяко ново чудовище ще иска нов if.
Идеята
Чудовищата
Чудовищата имат свой абстрактен родител. Той прилича на Character: име, живот, сила, раса. Атаката му удря героя:
abstract class Monster implements IAttackable { protected String name; protected int health; protected int power; protected String race;
public Monster(String name, int health, int power, String race) { this.name = name; this.health = health; this.power = power; this.race = race; }
public String getName() { return name; }
public int getHealth() { return health; }
@Override public void takeDamage(int amount) { health = Math.max(health - amount, 0); }
public boolean isAlive() { return health > 0; }
public void attack(Character target) { target.defend(power); }}abstract class Monster : IAttackable{ public string Name { get; protected set; } public int Health { get; protected set; } public int Power { get; protected set; } public string Race { get; protected set; }
public Monster(string name, int health, int power, string race) { Name = name; Health = health; Power = power; Race = race; }
public void TakeDamage(int amount) { Health = Math.Max(Health - amount, 0); }
public bool IsAlive() { return Health > 0; }
public virtual void Attack(Character target) { target.Defend(Power); }}class Monster(Attackable): def __init__(self, name, health, power, race): self._name = name self._health = health self._power = power self._race = race
@property def name(self): return self._name
@property def health(self): return self._health
def take_damage(self, amount): self._health = max(self._health - amount, 0)
def is_alive(self): return self._health > 0
def attack(self, target): target.defend(self._power)class Monster { constructor(name, health, power, race) { this._name = name; this._health = health; this._power = power; this._race = race; }
get name() { return this._name; }
get health() { return this._health; }
takeDamage(amount) { this._health = Math.max(this._health - amount, 0); }
isAlive() { return this._health > 0; }
attack(target) { target.defend(this._power); }}Падналият шаман е най-простото чудовище: само имена и числа. Касапинът презаписва атаката: след всеки удар силата му расте с 1.
class FallenShaman extends Monster { public FallenShaman() { super("Падналият шаман", 60, 12, "демон"); }}class FallenShaman : Monster{ public FallenShaman() : base("Падналият шаман", 60, 12, "демон") { }}class FallenShaman(Monster): def __init__(self): super().__init__("Падналият шаман", 60, 12, "демон")class FallenShaman extends Monster { constructor() { super("Падналият шаман", 60, 12, "демон"); }}class Butcher extends Monster { public Butcher() { super("Касапинът", 130, 10, "демон"); }
@Override public void attack(Character target) { super.attack(target); power += 1; }}class Butcher : Monster{ public Butcher() : base("Касапинът", 130, 10, "демон") { }
public override void Attack(Character target) { base.Attack(target); Power += 1; }}class Butcher(Monster): def __init__(self): super().__init__("Касапинът", 130, 10, "демон")
def attack(self, target): super().attack(target) self._power += 1class Butcher extends Monster { constructor() { super("Касапинът", 130, 10, "демон"); }
attack(target) { super.attack(target); this._power += 1; }}Идеята
Три договора
Общото между героите и чудовищата не е родител, а способности. Всяка е интерфейс:
interface IAttackable { void takeDamage(int amount);}
interface IDefendable { void defend(int attackPower);}
interface IHealable { void heal(Character target, int amount);}interface IAttackable{ void TakeDamage(int amount);}
interface IDefendable{ void Defend(int attackPower);}
interface IHealable{ void Heal(Character target, int amount);}from abc import ABC, abstractmethod
class Attackable(ABC): @abstractmethod def take_damage(self, amount): pass
class Defendable(ABC): @abstractmethod def defend(self, attack_power): pass
class Healable(ABC): @abstractmethod def heal(self, target, amount): pass// JavaScript няма интерфейси. Договорът е името на метода:// може да бъде ударен: takeDamage(amount)// може да се защитава: defend(attackPower)// може да лекува: heal(target, amount)- IAttackable: може да бъде ударен. Реализират го и
Character, иMonster. - IDefendable: може да се защитава. Защитата решава колко от удара да мине. Всеки герой го реализира през
Character, а от чудовищата само Скелетният крал:
class SkeletonKing extends Monster implements IDefendable { public SkeletonKing() { super("Скелетният крал", 90, 16, "нежив"); }
@Override public void defend(int attackPower) { takeDamage(Math.max(attackPower - 5, 0)); }}class SkeletonKing : Monster, IDefendable{ public SkeletonKing() : base("Скелетният крал", 90, 16, "нежив") { }
public void Defend(int attackPower) { TakeDamage(Math.Max(attackPower - 5, 0)); }}class SkeletonKing(Monster, Defendable): def __init__(self): super().__init__("Скелетният крал", 90, 16, "нежив")
def defend(self, attack_power): self.take_damage(max(attack_power - 5, 0))class SkeletonKing extends Monster { constructor() { super("Скелетният крал", 90, 16, "нежив"); }
defend(attackPower) { this.takeDamage(Math.max(attackPower - 5, 0)); }}- IHealable: може да лекува, включително себе си. Само някои герои го реализират. Паладинът лекува с него, а варваринът го няма, но презаписва защитата: от всеки удар поема с една пета по-малко.
class Paladin extends Character implements IHealable { public Paladin(String name) { super(name, 120, 14, "човек"); }
@Override public void heal(Character target, int amount) { target.restore(amount); }
@Override public String useAbility() { heal(this, 20); return name + " се лекува: +20 живот"; }}class Paladin : Character, IHealable{ public Paladin(string name) : base(name, 120, 14, "човек") { }
public void Heal(Character target, int amount) { target.Restore(amount); }
public override string UseAbility() { Heal(this, 20); return Name + " се лекува: +20 живот"; }}class Paladin(Character, Healable): def __init__(self, name): super().__init__(name, 120, 14, "човек")
def heal(self, target, amount): target.restore(amount)
def use_ability(self): self.heal(self, 20) return f"{self._name} се лекува: +20 живот"class Paladin extends Character { constructor(name) { super(name, 120, 14, "човек"); }
heal(target, amount) { target.restore(amount); }
useAbility() { this.heal(this, 20); return `${this._name} се лекува: +20 живот`; }}class Barbarian extends Character { public Barbarian(String name) { super(name, 110, 20, "орк"); }
@Override public void defend(int attackPower) { takeDamage(attackPower - attackPower / 5); }
@Override public String useAbility() { power += power / 2; return name + " изпада в ярост: сила " + power; }}class Barbarian : Character{ public Barbarian(string name) : base(name, 110, 20, "орк") { }
public override void Defend(int attackPower) { TakeDamage(attackPower - attackPower / 5); }
public override string UseAbility() { Power += Power / 2; return Name + " изпада в ярост: сила " + Power; }}class Barbarian(Character): def __init__(self, name): super().__init__(name, 110, 20, "орк")
def defend(self, attack_power): self.take_damage(attack_power - attack_power // 5)
def use_ability(self): self._power += self._power // 2 return f"{self._name} изпада в ярост: сила {self._power}"class Barbarian extends Character { constructor(name) { super(name, 110, 20, "орк"); }
defend(attackPower) { this.takeDamage(attackPower - Math.trunc(attackPower / 5)); }
useAbility() { this._power += Math.trunc(this._power / 2); return `${this._name} изпада в ярост: сила ${this._power}`; }}По-точно, за любопитните
- В C# имената на интерфейсите започват с
I. В Java обичайното е безI:Attackable,Defendable,Healable. Тук имената са еднакви в двата езика, за да се чете един и същ код.
- В първата част варваринът презаписваше
takeDamage. Сега защитата има свое име.takeDamageсамо вади щетата, аdefendрешава колко щета да мине. Така ще може да има и щета, която минава покрай защитата: отрова, капан. restoreе вCharacter, за да може всеки герой да бъде лекуван. Да лекува може само онзи, който реализира IHealable.
Виж го
Един удар, две цели
Ето новият Character. Освен договорите, той има и атака:
abstract class Character implements IAttackable, IDefendable { protected String name; protected int health; protected int power; protected String race;
public Character(String name, int health, int power, String race) { this.name = name; this.health = health; this.power = power; this.race = race; }
public String getName() { return name; }
public int getHealth() { return health; }
@Override public void takeDamage(int amount) { health = Math.max(health - amount, 0); }
@Override public void defend(int attackPower) { takeDamage(attackPower); }
public void restore(int amount) { health += amount; }
public boolean isAlive() { return health > 0; }
public void attack(Monster target) { if (target instanceof IDefendable defender) { defender.defend(power); } else { target.takeDamage(power); } }
public abstract String useAbility();}abstract class Character : IAttackable, IDefendable{ public string Name { get; protected set; } public int Health { get; protected set; } public int Power { get; protected set; } public string Race { get; protected set; }
public Character(string name, int health, int power, string race) { Name = name; Health = health; Power = power; Race = race; }
public void TakeDamage(int amount) { Health = Math.Max(Health - amount, 0); }
public virtual void Defend(int attackPower) { TakeDamage(attackPower); }
public void Restore(int amount) { Health += amount; }
public bool IsAlive() { return Health > 0; }
public void Attack(Monster target) { if (target is IDefendable defender) { defender.Defend(Power); } else { target.TakeDamage(Power); } }
public abstract string UseAbility();}class Character(Attackable, Defendable): def __init__(self, name, health, power, race): self._name = name self._health = health self._power = power self._race = race
@property def name(self): return self._name
@property def health(self): return self._health
def take_damage(self, amount): self._health = max(self._health - amount, 0)
def defend(self, attack_power): self.take_damage(attack_power)
def restore(self, amount): self._health += amount
def is_alive(self): return self._health > 0
def attack(self, target): if isinstance(target, Defendable): target.defend(self._power) else: target.take_damage(self._power)
@abstractmethod def use_ability(self): passclass Character { constructor(name, health, power, race) { this._name = name; this._health = health; this._power = power; this._race = race; }
get name() { return this._name; }
get health() { return this._health; }
takeDamage(amount) { this._health = Math.max(this._health - amount, 0); }
defend(attackPower) { this.takeDamage(attackPower); }
restore(amount) { this._health += amount; }
isAlive() { return this._health > 0; }
attack(target) { if (typeof target.defend === "function") { target.defend(this._power); } else { target.takeDamage(this._power); } }}Атаката пита целта: можеш ли да се защитаваш? target instanceof IDefendable defender е true, ако обектът реализира интерфейса. Тогава defender е същият обект, но като IDefendable, и на него може да се извика defend. Иначе щетата се вади направо.
Варваринът Грок удря шамана и краля. После шаманът удря него:
Character grok = new Barbarian("Грок");Monster shaman = new FallenShaman();Monster king = new SkeletonKing();grok.attack(shaman);grok.attack(king);System.out.println(shaman.getName() + ": " + shaman.getHealth());System.out.println(king.getName() + ": " + king.getHealth());shaman.attack(grok);System.out.println(grok.getName() + ": " + grok.getHealth());Падналият шаман: 40Скелетният крал: 75Грок: 100Character grok = new Barbarian("Грок");Monster shaman = new FallenShaman();Monster king = new SkeletonKing();grok.Attack(shaman);grok.Attack(king);Console.WriteLine(shaman.Name + ": " + shaman.Health);Console.WriteLine(king.Name + ": " + king.Health);shaman.Attack(grok);Console.WriteLine(grok.Name + ": " + grok.Health);Падналият шаман: 40Скелетният крал: 75Грок: 100grok = Barbarian("Грок")shaman = FallenShaman()king = SkeletonKing()grok.attack(shaman)grok.attack(king)print(f"{shaman.name}: {shaman.health}")print(f"{king.name}: {king.health}")shaman.attack(grok)print(f"{grok.name}: {grok.health}")Падналият шаман: 40Скелетният крал: 75Грок: 100const grok = new Barbarian("Грок");const shaman = new FallenShaman();const king = new SkeletonKing();grok.attack(shaman);grok.attack(king);console.log(`${shaman.name}: ${shaman.health}`);console.log(`${king.name}: ${king.health}`);shaman.attack(grok);console.log(`${grok.name}: ${grok.health}`);Падналият шаман: 40Скелетният крал: 75Грок: 100Шаманът губи цялата сила на Грок, 20. Кралят спира 5 и губи 15. Шаманът удря с 12, а Грок поема 12 − 2 = 10. Атаката не знае нищо за краля или варварина: тя знае само договора.
Пробвай
Кой може да лекува?
Какво ще отпечата програмата? Класовете са от горните карти.
Character[] party = { new Paladin("Ардан"), new Barbarian("Грок"), new Witch("Морена") };for (Character hero : party) { if (hero instanceof IHealable) { System.out.println(hero.getName()); }}Паладинът и вещицата реализират IHealable, а варваринът не. Проверката гледа истинския обект, не типа на масива.
Виж го
Бой до смърт
Паладинът Ардан срещу Падналия шаман. Всеки рунд е удар на героя и, ако чудовището е още живо, удар на чудовището. Боят свършва, когато единият падне:
Character hero = new Paladin("Ардан");Monster monster = new FallenShaman();int round = 1;while (hero.isAlive() && monster.isAlive()) { hero.attack(monster); System.out.println(round + ". " + hero.getName() + " удря: " + monster.getName() + " има " + monster.getHealth()); if (monster.isAlive()) { monster.attack(hero); System.out.println(" " + monster.getName() + " удря: " + hero.getName() + " има " + hero.getHealth()); } round++;}System.out.println(hero.isAlive() ? "Печели " + hero.getName() : "Печели " + monster.getName());1. Ардан удря: Падналият шаман има 46 Падналият шаман удря: Ардан има 1082. Ардан удря: Падналият шаман има 32 Падналият шаман удря: Ардан има 963. Ардан удря: Падналият шаман има 18 Падналият шаман удря: Ардан има 844. Ардан удря: Падналият шаман има 4 Падналият шаман удря: Ардан има 725. Ардан удря: Падналият шаман има 0Печели АрданCharacter hero = new Paladin("Ардан");Monster monster = new FallenShaman();int round = 1;while (hero.IsAlive() && monster.IsAlive()){ hero.Attack(monster); Console.WriteLine(round + ". " + hero.Name + " удря: " + monster.Name + " има " + monster.Health); if (monster.IsAlive()) { monster.Attack(hero); Console.WriteLine(" " + monster.Name + " удря: " + hero.Name + " има " + hero.Health); } round++;}Console.WriteLine(hero.IsAlive() ? "Печели " + hero.Name : "Печели " + monster.Name);1. Ардан удря: Падналият шаман има 46 Падналият шаман удря: Ардан има 1082. Ардан удря: Падналият шаман има 32 Падналият шаман удря: Ардан има 963. Ардан удря: Падналият шаман има 18 Падналият шаман удря: Ардан има 844. Ардан удря: Падналият шаман има 4 Падналият шаман удря: Ардан има 725. Ардан удря: Падналият шаман има 0Печели Арданhero = Paladin("Ардан")monster = FallenShaman()round_number = 1while hero.is_alive() and monster.is_alive(): hero.attack(monster) print(f"{round_number}. {hero.name} удря: {monster.name} има {monster.health}") if monster.is_alive(): monster.attack(hero) print(f" {monster.name} удря: {hero.name} има {hero.health}") round_number += 1print(f"Печели {hero.name}" if hero.is_alive() else f"Печели {monster.name}")1. Ардан удря: Падналият шаман има 46 Падналият шаман удря: Ардан има 1082. Ардан удря: Падналият шаман има 32 Падналият шаман удря: Ардан има 963. Ардан удря: Падналият шаман има 18 Падналият шаман удря: Ардан има 844. Ардан удря: Падналият шаман има 4 Падналият шаман удря: Ардан има 725. Ардан удря: Падналият шаман има 0Печели Арданconst hero = new Paladin("Ардан");const monster = new FallenShaman();let round = 1;while (hero.isAlive() && monster.isAlive()) { hero.attack(monster); console.log(`${round}. ${hero.name} удря: ${monster.name} има ${monster.health}`); if (monster.isAlive()) { monster.attack(hero); console.log(` ${monster.name} удря: ${hero.name} има ${hero.health}`); } round++;}console.log(hero.isAlive() ? `Печели ${hero.name}` : `Печели ${monster.name}`);1. Ардан удря: Падналият шаман има 46 Падналият шаман удря: Ардан има 1082. Ардан удря: Падналият шаман има 32 Падналият шаман удря: Ардан има 963. Ардан удря: Падналият шаман има 18 Падналият шаман удря: Ардан има 844. Ардан удря: Падналият шаман има 4 Падналият шаман удря: Ардан има 725. Ардан удря: Падналият шаман има 0Печели АрданЦикълът е цикълът на играта от урока „Как се мисли игра“: състоянието е животът на двамата, правилата са атаките, а краят е условието на while.
Character hero = new Paladin("Ардан");Monster monster = new FallenShaman();int round = 1;while (hero.isAlive() && monster.isAlive()) { hero.attack(monster); System.out.println(round + ". " + hero.getName() + " удря: " + monster.getName() + " има " + monster.getHealth()); if (monster.isAlive()) { monster.attack(hero); System.out.println(" " + monster.getName() + " удря: " + hero.getName() + " има " + hero.getHealth()); } round++;}System.out.println(hero.isAlive() ? "Печели " + hero.getName() : "Печели " + monster.getName());Стъпка по стъпка, с думи
- Ардан започва с 120 живот, Падналият шаман с 60. Рундовете започват от 1.
- Рунд 1: Ардан удря и отнема 14. Падналият шаман пада от 60 на 46.
- Падналият шаман е жив и отвръща. Отнема 12: Ардан пада от 120 на 108.
- Рунд 2: Ардан удря и отнема 14. Падналият шаман пада от 46 на 32.
- Падналият шаман е жив и отвръща. Отнема 12: Ардан пада от 108 на 96.
- Рунд 3: Ардан удря и отнема 14. Падналият шаман пада от 32 на 18.
- Падналият шаман е жив и отвръща. Отнема 12: Ардан пада от 96 на 84.
- Рунд 4: Ардан удря и отнема 14. Падналият шаман пада от 18 на 4.
- Падналият шаман е жив и отвръща. Отнема 12: Ардан пада от 84 на 72.
- Рунд 5: Ардан удря и Падналият шаман пада от 4 на 0. monster.isAlive() е false, затова не отвръща.
- Условието на while вече е false: единият не е жив. Цикълът спира и печели Ардан.
Виж го
10 хода и избор на играча
Срещу Касапина само атаките не стигат. Затова играчът избира всеки ход: 1 е атака, 2 е умението на героя. Боят е най-много 10 хода, а ако и двамата са живи накрая, печели по-здравият:
Scanner in = new Scanner(System.in);Character hero = new Paladin("Ардан");Monster monster = new Butcher();for (int turn = 1; turn <= 10 && hero.isAlive() && monster.isAlive(); turn++) { String choice = in.nextLine(); if (choice.equals("2")) { System.out.println(turn + ". " + hero.useAbility()); } else { hero.attack(monster); System.out.println(turn + ". " + hero.getName() + " удря: " + monster.getName() + " има " + monster.getHealth()); } if (monster.isAlive()) { monster.attack(hero); System.out.println(" " + monster.getName() + " удря: " + hero.getName() + " има " + hero.getHealth()); }}String winner = hero.getHealth() > monster.getHealth() ? hero.getName() : monster.getName();System.out.println("Печели " + winner);1. Ардан удря: Касапинът има 116 Касапинът удря: Ардан има 1102. Ардан удря: Касапинът има 102 Касапинът удря: Ардан има 993. Ардан удря: Касапинът има 88 Касапинът удря: Ардан има 874. Ардан се лекува: +20 живот Касапинът удря: Ардан има 945. Ардан удря: Касапинът има 74 Касапинът удря: Ардан има 806. Ардан удря: Касапинът има 60 Касапинът удря: Ардан има 657. Ардан се лекува: +20 живот Касапинът удря: Ардан има 698. Ардан удря: Касапинът има 46 Касапинът удря: Ардан има 529. Ардан се лекува: +20 живот Касапинът удря: Ардан има 5410. Ардан удря: Касапинът има 32 Касапинът удря: Ардан има 35Печели АрданCharacter hero = new Paladin("Ардан");Monster monster = new Butcher();for (int turn = 1; turn <= 10 && hero.IsAlive() && monster.IsAlive(); turn++){ string choice = Console.ReadLine(); if (choice == "2") { Console.WriteLine(turn + ". " + hero.UseAbility()); } else { hero.Attack(monster); Console.WriteLine(turn + ". " + hero.Name + " удря: " + monster.Name + " има " + monster.Health); } if (monster.IsAlive()) { monster.Attack(hero); Console.WriteLine(" " + monster.Name + " удря: " + hero.Name + " има " + hero.Health); }}string winner = hero.Health > monster.Health ? hero.Name : monster.Name;Console.WriteLine("Печели " + winner);1. Ардан удря: Касапинът има 116 Касапинът удря: Ардан има 1102. Ардан удря: Касапинът има 102 Касапинът удря: Ардан има 993. Ардан удря: Касапинът има 88 Касапинът удря: Ардан има 874. Ардан се лекува: +20 живот Касапинът удря: Ардан има 945. Ардан удря: Касапинът има 74 Касапинът удря: Ардан има 806. Ардан удря: Касапинът има 60 Касапинът удря: Ардан има 657. Ардан се лекува: +20 живот Касапинът удря: Ардан има 698. Ардан удря: Касапинът има 46 Касапинът удря: Ардан има 529. Ардан се лекува: +20 живот Касапинът удря: Ардан има 5410. Ардан удря: Касапинът има 32 Касапинът удря: Ардан има 35Печели Арданhero = Paladin("Ардан")monster = Butcher()turn = 1while turn <= 10 and hero.is_alive() and monster.is_alive(): choice = input() if choice == "2": print(f"{turn}. {hero.use_ability()}") else: hero.attack(monster) print(f"{turn}. {hero.name} удря: {monster.name} има {monster.health}") if monster.is_alive(): monster.attack(hero) print(f" {monster.name} удря: {hero.name} има {hero.health}") turn += 1winner = hero.name if hero.health > monster.health else monster.nameprint(f"Печели {winner}")1. Ардан удря: Касапинът има 116 Касапинът удря: Ардан има 1102. Ардан удря: Касапинът има 102 Касапинът удря: Ардан има 993. Ардан удря: Касапинът има 88 Касапинът удря: Ардан има 874. Ардан се лекува: +20 живот Касапинът удря: Ардан има 945. Ардан удря: Касапинът има 74 Касапинът удря: Ардан има 806. Ардан удря: Касапинът има 60 Касапинът удря: Ардан има 657. Ардан се лекува: +20 живот Касапинът удря: Ардан има 698. Ардан удря: Касапинът има 46 Касапинът удря: Ардан има 529. Ардан се лекува: +20 живот Касапинът удря: Ардан има 5410. Ардан удря: Касапинът има 32 Касапинът удря: Ардан има 35Печели Арданconst hero = new Paladin("Ардан");const monster = new Butcher();for (let turn = 1; turn <= 10 && hero.isAlive() && monster.isAlive(); turn++) { const choice = await ask(""); if (choice === "2") { console.log(`${turn}. ${hero.useAbility()}`); } else { hero.attack(monster); console.log(`${turn}. ${hero.name} удря: ${monster.name} има ${monster.health}`); } if (monster.isAlive()) { monster.attack(hero); console.log(` ${monster.name} удря: ${hero.name} има ${hero.health}`); }}const winner = hero.health > monster.health ? hero.name : monster.name;console.log(`Печели ${winner}`);rl.close();1. Ардан удря: Касапинът има 116 Касапинът удря: Ардан има 1102. Ардан удря: Касапинът има 102 Касапинът удря: Ардан има 993. Ардан удря: Касапинът има 88 Касапинът удря: Ардан има 874. Ардан се лекува: +20 живот Касапинът удря: Ардан има 945. Ардан удря: Касапинът има 74 Касапинът удря: Ардан има 806. Ардан удря: Касапинът има 60 Касапинът удря: Ардан има 657. Ардан се лекува: +20 живот Касапинът удря: Ардан има 698. Ардан удря: Касапинът има 46 Касапинът удря: Ардан има 529. Ардан се лекува: +20 живот Касапинът удря: Ардан има 5410. Ардан удря: Касапинът има 32 Касапинът удря: Ардан има 35Печели АрданВходът на този пример е:
1 1 1 2 1 1 2 1 2 1Ако играчът избере само атаки, десет пъти 1, паладинът пада на 9-ия ход, а на Касапина остават 4 живот. С три лечения ходовете стигат до края и паладинът печели с 35 живот срещу 32.
По-точно, за любопитните
- Изборът се чете на всеки ход, но ако героят падне, цикълът спира и останалите редове на входа не се четат.
choice.equals("2")сравнява текста. Всичко друго, и празен ред, е атака: играта не може да спре на непознат избор. Как да откажеш неправилния избор с изключение, ще видиш в третата част.
Пробвай
Намери грешката
Варваринът удря Скелетния крал със сила 20, а кралят губи 20 вместо 15. На кой ред в атаката е грешката?
Избери реда с грешката.
Проверката открива, че кралят може да се защитава, но после щетата се вади направо, покрай defend. И двата клона правят едно и също. Трябва defender.defend(power);.
Пробвай
Кое липсва?
public void attack(Monster target) {
if (target IDefendable defender) {
defender.defend(power);
} else {
target.takeDamage(power);
}
}instanceof проверява истинския обект. С име след типа, defender, проверката и превръщането са на един ред.Упражнение
Още битки
Реши задачите в редактора си. Започни от класовете в урока.
1. Ново чудовище. Андариел, демон, 100 живот и 12 сила. Ударът ѝ е отровен: след обикновената атака целта поема още 4 щета направо през takeDamage, покрай защитата. Пусни боя до смърт с варварина Грок.
1. Грок удря: Андариел има 80 Андариел удря: Грок има 962. Грок удря: Андариел има 60 Андариел удря: Грок има 823. Грок удря: Андариел има 40 Андариел удря: Грок има 684. Грок удря: Андариел има 20 Андариел удря: Грок има 545. Грок удря: Андариел има 0Печели ГрокГрок поема 10 от удара (12 минус една пета) и 4 от отровата: по 14 на рунд.
2. Лечебна отвара. Клас Potion с количество живот. use(Character target) лекува целта и връща текст. Отварата се пие веднъж: след това казва „Отварата е празна“. В боя на паладина с Касапина героят пие отвара за 40 вместо да удари, когато животът му е под 30.
1. Ардан удря: Касапинът има 116 Касапинът удря: Ардан има 1102. Ардан удря: Касапинът има 102 Касапинът удря: Ардан има 993. Ардан удря: Касапинът има 88 Касапинът удря: Ардан има 874. Ардан удря: Касапинът има 74 Касапинът удря: Ардан има 745. Ардан удря: Касапинът има 60 Касапинът удря: Ардан има 606. Ардан удря: Касапинът има 46 Касапинът удря: Ардан има 457. Ардан удря: Касапинът има 32 Касапинът удря: Ардан има 298. Ардан пие отвара: +40 живот Касапинът удря: Ардан има 529. Ардан удря: Касапинът има 18 Касапинът удря: Ардан има 3410. Ардан удря: Касапинът има 4 Касапинът удря: Ардан има 1511. Ардан удря: Касапинът има 0Печели Ардан3. Отчет за битката. Вместо ред за всеки удар, отпечатай накрая колко рунда е траял боят, колко щета е нанесъл героят, колко е поел и колко живот му остава. Варваринът Грок срещу Скелетния крал:
Рундове: 6Нанесена щета: 90Поета щета: 65Остава живот: 45Подсказка: запомни живота на целта преди удара и го извади от живота след удара.
Още задачи
- Вещица срещу крал. Пусни боя на вещицата Морена със Скелетния крал с избор от входа. Може ли да спечели в 10 хода?
- Лечител на отряда. Паладинът лекува с 10 героя с най-малко живот в масив от трима, вместо себе си.
- Кой удря пръв? С
new Random(5)реши в началото на всеки рунд кой удря пръв.
Тест