done
This commit is contained in:
61
src/Arena.java
Normal file
61
src/Arena.java
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public final class Arena<C extends Character> {
|
||||
|
||||
private final Set<C> characters =
|
||||
new HashSet<>();
|
||||
|
||||
public void add(C character) {
|
||||
characters.add(
|
||||
Objects.requireNonNull(
|
||||
character,
|
||||
"Character must not be null"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public boolean remove(C character) {
|
||||
return characters.remove(character);
|
||||
}
|
||||
|
||||
public void attackAll(Attack attack) {
|
||||
Objects.requireNonNull(
|
||||
attack,
|
||||
"Attack must not be null"
|
||||
);
|
||||
|
||||
for (C character : characters) {
|
||||
attack.execute(character);
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return characters.size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return characters.isEmpty();
|
||||
}
|
||||
|
||||
public Set<C> getCharacters() {
|
||||
return Set.copyOf(characters);
|
||||
}
|
||||
|
||||
public List<C> getLivingCharacters() {
|
||||
return characters.stream()
|
||||
.filter(character ->
|
||||
!character.isDefeated()
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public List<C> getDefeatedCharacters() {
|
||||
return characters.stream()
|
||||
.filter(Character::isDefeated)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
445
src/ArenaTest.java
Normal file
445
src/ArenaTest.java
Normal file
@@ -0,0 +1,445 @@
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@DisplayName("Generische Arena")
|
||||
class ArenaTest {
|
||||
|
||||
@Nested
|
||||
@DisplayName("Spieler-Arena")
|
||||
class PlayerArenaTests {
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"Ein Angriff wird auf alle Spieler angewendet"
|
||||
)
|
||||
void attackAllAttacksEveryPlayer() {
|
||||
// Arrange
|
||||
Arena<Player> arena = new Arena<>();
|
||||
|
||||
Player warrior = new Player(
|
||||
"Krieger",
|
||||
100
|
||||
);
|
||||
|
||||
Player mage = new Player(
|
||||
"Magier",
|
||||
70
|
||||
);
|
||||
|
||||
arena.add(warrior);
|
||||
arena.add(mage);
|
||||
|
||||
// Act
|
||||
arena.attackAll(new SwordAttack());
|
||||
|
||||
// Assert
|
||||
assertAll(
|
||||
() -> assertEquals(
|
||||
70,
|
||||
warrior.getHitPoints()
|
||||
),
|
||||
() -> assertEquals(
|
||||
40,
|
||||
mage.getHitPoints()
|
||||
),
|
||||
() -> assertFalse(
|
||||
warrior.isDefeated()
|
||||
),
|
||||
() -> assertFalse(
|
||||
mage.isDefeated()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"Dasselbe Objekt wird im Set nicht doppelt gespeichert"
|
||||
)
|
||||
void samePlayerIsNotAddedTwice() {
|
||||
// Arrange
|
||||
Arena<Player> arena = new Arena<>();
|
||||
|
||||
Player player = new Player(
|
||||
"Held",
|
||||
100
|
||||
);
|
||||
|
||||
// Act
|
||||
arena.add(player);
|
||||
arena.add(player);
|
||||
|
||||
// Assert
|
||||
assertEquals(
|
||||
1,
|
||||
arena.size()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Gegner-Arena")
|
||||
class EnemyArenaTests {
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"Double Dispatch wirkt für jeden Gegnertyp unterschiedlich"
|
||||
)
|
||||
void fireSpellAffectsEnemyTypesDifferently() {
|
||||
// Arrange
|
||||
Arena<Enemy> arena = new Arena<>();
|
||||
|
||||
Skeleton skeleton = new Skeleton(
|
||||
"Knochensoldat",
|
||||
100
|
||||
);
|
||||
|
||||
IceGolem iceGolem = new IceGolem(
|
||||
"Frostwächter",
|
||||
100
|
||||
);
|
||||
|
||||
arena.add(skeleton);
|
||||
arena.add(iceGolem);
|
||||
|
||||
// Act
|
||||
arena.attackAll(new FireSpell());
|
||||
|
||||
// Assert
|
||||
assertAll(
|
||||
() -> assertEquals(
|
||||
75,
|
||||
skeleton.getHitPoints()
|
||||
),
|
||||
() -> assertEquals(
|
||||
40,
|
||||
iceGolem.getHitPoints()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"Ein Schwert wirkt ebenfalls abhängig vom Gegnertyp"
|
||||
)
|
||||
void swordAttackAffectsEnemyTypesDifferently() {
|
||||
// Arrange
|
||||
Arena<Enemy> arena = new Arena<>();
|
||||
|
||||
Skeleton skeleton = new Skeleton(
|
||||
"Knochensoldat",
|
||||
100
|
||||
);
|
||||
|
||||
IceGolem iceGolem = new IceGolem(
|
||||
"Frostwächter",
|
||||
100
|
||||
);
|
||||
|
||||
arena.add(skeleton);
|
||||
arena.add(iceGolem);
|
||||
|
||||
// Act
|
||||
arena.attackAll(new SwordAttack());
|
||||
|
||||
// Assert
|
||||
assertAll(
|
||||
() -> assertEquals(
|
||||
60,
|
||||
skeleton.getHitPoints()
|
||||
),
|
||||
() -> assertEquals(
|
||||
85,
|
||||
iceGolem.getHitPoints()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Gemischte Arena")
|
||||
class MixedArenaTests {
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"Spieler und Gegner können gemeinsam angegriffen werden"
|
||||
)
|
||||
void mixedArenaAttacksAllCharacterTypes() {
|
||||
// Arrange
|
||||
Arena<Character> arena = new Arena<>();
|
||||
|
||||
Player player = new Player(
|
||||
"Held",
|
||||
100
|
||||
);
|
||||
|
||||
Skeleton skeleton = new Skeleton(
|
||||
"Knochensoldat",
|
||||
100
|
||||
);
|
||||
|
||||
IceGolem iceGolem = new IceGolem(
|
||||
"Frostwächter",
|
||||
100
|
||||
);
|
||||
|
||||
arena.add(player);
|
||||
arena.add(skeleton);
|
||||
arena.add(iceGolem);
|
||||
|
||||
// Act
|
||||
arena.attackAll(new FireSpell());
|
||||
|
||||
// Assert
|
||||
assertAll(
|
||||
() -> assertEquals(
|
||||
70,
|
||||
player.getHitPoints()
|
||||
),
|
||||
() -> assertEquals(
|
||||
75,
|
||||
skeleton.getHitPoints()
|
||||
),
|
||||
() -> assertEquals(
|
||||
40,
|
||||
iceGolem.getHitPoints()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"Lebende und besiegte Figuren werden getrennt"
|
||||
)
|
||||
void livingAndDefeatedCharactersAreSeparated() {
|
||||
// Arrange
|
||||
Arena<Character> arena = new Arena<>();
|
||||
|
||||
Player player = new Player(
|
||||
"Held",
|
||||
20
|
||||
);
|
||||
|
||||
Skeleton skeleton = new Skeleton(
|
||||
"Schwaches Skelett",
|
||||
20
|
||||
);
|
||||
|
||||
IceGolem iceGolem = new IceGolem(
|
||||
"Frostwächter",
|
||||
100
|
||||
);
|
||||
|
||||
arena.add(player);
|
||||
arena.add(skeleton);
|
||||
arena.add(iceGolem);
|
||||
|
||||
// Act
|
||||
arena.attackAll(new FireSpell());
|
||||
|
||||
List<Character> living =
|
||||
arena.getLivingCharacters();
|
||||
|
||||
List<Character> defeated =
|
||||
arena.getDefeatedCharacters();
|
||||
|
||||
// Assert
|
||||
assertAll(
|
||||
() -> assertEquals(
|
||||
1,
|
||||
living.size()
|
||||
),
|
||||
() -> assertTrue(
|
||||
living.contains(iceGolem)
|
||||
),
|
||||
() -> assertEquals(
|
||||
2,
|
||||
defeated.size()
|
||||
),
|
||||
() -> assertTrue(
|
||||
defeated.contains(player)
|
||||
),
|
||||
() -> assertTrue(
|
||||
defeated.contains(skeleton)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"Besiegte Figuren können entfernt werden"
|
||||
)
|
||||
void defeatedCharactersCanBeRemoved() {
|
||||
// Arrange
|
||||
Arena<Character> arena = new Arena<>();
|
||||
|
||||
Player player = new Player(
|
||||
"Held",
|
||||
20
|
||||
);
|
||||
|
||||
IceGolem iceGolem = new IceGolem(
|
||||
"Frostwächter",
|
||||
100
|
||||
);
|
||||
|
||||
arena.add(player);
|
||||
arena.add(iceGolem);
|
||||
|
||||
arena.attackAll(new FireSpell());
|
||||
|
||||
// Act
|
||||
for (Character defeated
|
||||
: arena.getDefeatedCharacters()) {
|
||||
arena.remove(defeated);
|
||||
}
|
||||
|
||||
// Assert
|
||||
assertAll(
|
||||
() -> assertEquals(
|
||||
1,
|
||||
arena.size()
|
||||
),
|
||||
() -> assertTrue(
|
||||
arena.getCharacters()
|
||||
.contains(iceGolem)
|
||||
),
|
||||
() -> assertFalse(
|
||||
arena.getCharacters()
|
||||
.contains(player)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Verwaltung der Arena")
|
||||
class ArenaManagementTests {
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"remove entfernt eine vorhandene Figur"
|
||||
)
|
||||
void removeDeletesCharacter() {
|
||||
// Arrange
|
||||
Arena<Player> arena = new Arena<>();
|
||||
|
||||
Player player = new Player(
|
||||
"Held",
|
||||
100
|
||||
);
|
||||
|
||||
arena.add(player);
|
||||
|
||||
// Act
|
||||
boolean removed =
|
||||
arena.remove(player);
|
||||
|
||||
// Assert
|
||||
assertAll(
|
||||
() -> assertTrue(removed),
|
||||
() -> assertTrue(
|
||||
arena.isEmpty()
|
||||
),
|
||||
() -> assertEquals(
|
||||
0,
|
||||
arena.size()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"remove liefert false für eine unbekannte Figur"
|
||||
)
|
||||
void removingUnknownCharacterReturnsFalse() {
|
||||
// Arrange
|
||||
Arena<Player> arena = new Arena<>();
|
||||
|
||||
Player storedPlayer = new Player(
|
||||
"Held",
|
||||
100
|
||||
);
|
||||
|
||||
Player unknownPlayer = new Player(
|
||||
"Fremder",
|
||||
100
|
||||
);
|
||||
|
||||
arena.add(storedPlayer);
|
||||
|
||||
// Act
|
||||
boolean removed =
|
||||
arena.remove(unknownPlayer);
|
||||
|
||||
// Assert
|
||||
assertAll(
|
||||
() -> assertFalse(removed),
|
||||
() -> assertEquals(
|
||||
1,
|
||||
arena.size()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"getCharacters liefert ein unveränderliches Set"
|
||||
)
|
||||
void returnedCharacterSetIsUnmodifiable() {
|
||||
// Arrange
|
||||
Arena<Player> arena = new Arena<>();
|
||||
|
||||
arena.add(
|
||||
new Player(
|
||||
"Held",
|
||||
100
|
||||
)
|
||||
);
|
||||
|
||||
Set<Player> characters =
|
||||
arena.getCharacters();
|
||||
|
||||
// Act und Assert
|
||||
assertThrows(
|
||||
UnsupportedOperationException.class,
|
||||
characters::clear
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"null kann nicht hinzugefügt werden"
|
||||
)
|
||||
void nullCharacterCannotBeAdded() {
|
||||
Arena<Character> arena = new Arena<>();
|
||||
|
||||
assertThrows(
|
||||
NullPointerException.class,
|
||||
() -> arena.add(null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName(
|
||||
"attackAll akzeptiert keinen null-Angriff"
|
||||
)
|
||||
void nullAttackIsRejected() {
|
||||
Arena<Character> arena = new Arena<>();
|
||||
|
||||
assertThrows(
|
||||
NullPointerException.class,
|
||||
() -> arena.attackAll(null)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/Attack.java
Normal file
20
src/Attack.java
Normal file
@@ -0,0 +1,20 @@
|
||||
public abstract class Attack {
|
||||
|
||||
private final int baseDamage;
|
||||
|
||||
protected Attack(int baseDamage) {
|
||||
if (baseDamage <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Base damage must be positive"
|
||||
);
|
||||
}
|
||||
|
||||
this.baseDamage = baseDamage;
|
||||
}
|
||||
|
||||
public final int getBaseDamage() {
|
||||
return baseDamage;
|
||||
}
|
||||
|
||||
public abstract void execute(Character target);
|
||||
}
|
||||
11
src/Character.java
Normal file
11
src/Character.java
Normal file
@@ -0,0 +1,11 @@
|
||||
public interface Character {
|
||||
|
||||
String getName();
|
||||
|
||||
int getHitPoints();
|
||||
|
||||
boolean isDefeated();
|
||||
|
||||
void receive(FireSpell attack);
|
||||
void receive(SwordAttack attack);
|
||||
}
|
||||
61
src/Enemy.java
Normal file
61
src/Enemy.java
Normal file
@@ -0,0 +1,61 @@
|
||||
public abstract class Enemy implements Character {
|
||||
|
||||
private final String name;
|
||||
private int hitPoints;
|
||||
|
||||
protected Enemy(String name, int hitPoints) {
|
||||
if (name == null || name.isBlank()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Name must not be blank"
|
||||
);
|
||||
}
|
||||
|
||||
if (hitPoints <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Hit points must be positive"
|
||||
);
|
||||
}
|
||||
|
||||
this.name = name;
|
||||
this.hitPoints = hitPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getHitPoints() {
|
||||
return hitPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isDefeated() {
|
||||
return hitPoints == 0;
|
||||
}
|
||||
|
||||
protected final void takeDamage(int damage) {
|
||||
if (damage < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Damage must not be negative"
|
||||
);
|
||||
}
|
||||
|
||||
hitPoints = Math.max(
|
||||
0,
|
||||
hitPoints - damage
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return "%s{name='%s', hitPoints=%d, defeated=%s}"
|
||||
.formatted(
|
||||
getClass().getSimpleName(),
|
||||
name,
|
||||
hitPoints,
|
||||
isDefeated()
|
||||
);
|
||||
}
|
||||
}
|
||||
20
src/FireSpell.java
Normal file
20
src/FireSpell.java
Normal file
@@ -0,0 +1,20 @@
|
||||
import java.util.Objects;
|
||||
|
||||
public final class FireSpell extends Attack {
|
||||
|
||||
private static final int BASE_DAMAGE = 30;
|
||||
|
||||
public FireSpell() {
|
||||
super(BASE_DAMAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Character target) {
|
||||
Objects.requireNonNull(
|
||||
target,
|
||||
"Target must not be null"
|
||||
);
|
||||
|
||||
target.receive(this);
|
||||
}
|
||||
}
|
||||
34
src/IceGolem.java
Normal file
34
src/IceGolem.java
Normal file
@@ -0,0 +1,34 @@
|
||||
import java.util.Objects;
|
||||
|
||||
public final class IceGolem extends Enemy {
|
||||
|
||||
public IceGolem(String name, int hitPoints) {
|
||||
super(name, hitPoints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(SwordAttack attack) {
|
||||
Objects.requireNonNull(
|
||||
attack,
|
||||
"Attack must not be null"
|
||||
);
|
||||
|
||||
int damage =
|
||||
attack.getBaseDamage() / 2;
|
||||
|
||||
takeDamage(damage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(FireSpell attack) {
|
||||
Objects.requireNonNull(
|
||||
attack,
|
||||
"Attack must not be null"
|
||||
);
|
||||
|
||||
int damage =
|
||||
attack.getBaseDamage() * 2;
|
||||
|
||||
takeDamage(damage);
|
||||
}
|
||||
}
|
||||
166
src/Main.java
Normal file
166
src/Main.java
Normal file
@@ -0,0 +1,166 @@
|
||||
public final class Main {
|
||||
|
||||
private Main() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
demonstratePlayerArena();
|
||||
demonstrateEnemyArena();
|
||||
demonstrateMixedArena();
|
||||
}
|
||||
|
||||
/*
|
||||
* Homogene Arena:
|
||||
* Es dürfen ausschließlich Player-Objekte
|
||||
* hinzugefügt werden.
|
||||
*/
|
||||
private static void demonstratePlayerArena() {
|
||||
Arena<Player> arena = new Arena<>();
|
||||
|
||||
Player warrior = new Player(
|
||||
"Krieger",
|
||||
100
|
||||
);
|
||||
|
||||
Player mage = new Player(
|
||||
"Magier",
|
||||
70
|
||||
);
|
||||
|
||||
arena.add(warrior);
|
||||
arena.add(mage);
|
||||
|
||||
System.out.println("Spieler-Arena vor dem Angriff:");
|
||||
printArena(arena);
|
||||
|
||||
arena.attackAll(new SwordAttack());
|
||||
|
||||
System.out.println("Spieler-Arena nach dem Schwertangriff:");
|
||||
printArena(arena);
|
||||
}
|
||||
|
||||
/*
|
||||
* Arena mit unterschiedlichen Gegnerklassen.
|
||||
*
|
||||
* Skeleton und IceGolem sind beide Enemy,
|
||||
* reagieren aber unterschiedlich auf FireSpell.
|
||||
*/
|
||||
private static void demonstrateEnemyArena() {
|
||||
Arena<Enemy> arena = new Arena<>();
|
||||
|
||||
Skeleton skeleton = new Skeleton(
|
||||
"Knochensoldat",
|
||||
100
|
||||
);
|
||||
|
||||
IceGolem iceGolem = new IceGolem(
|
||||
"Frostwächter",
|
||||
100
|
||||
);
|
||||
|
||||
arena.add(skeleton);
|
||||
arena.add(iceGolem);
|
||||
|
||||
System.out.println("Gegner-Arena vor dem Angriff:");
|
||||
printArena(arena);
|
||||
|
||||
arena.attackAll(new FireSpell());
|
||||
|
||||
System.out.println("Gegner-Arena nach dem Feuerzauber:");
|
||||
printArena(arena);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gemischte Arena:
|
||||
* Spieler und Gegner sind gemeinsam möglich,
|
||||
* weil alle Character implementieren.
|
||||
*/
|
||||
private static void demonstrateMixedArena() {
|
||||
Arena<Character> arena = new Arena<>();
|
||||
|
||||
Player player = new Player(
|
||||
"Held",
|
||||
50
|
||||
);
|
||||
|
||||
Skeleton skeleton = new Skeleton(
|
||||
"Schwaches Skelett",
|
||||
20
|
||||
);
|
||||
|
||||
IceGolem iceGolem = new IceGolem(
|
||||
"Frostwächter",
|
||||
100
|
||||
);
|
||||
|
||||
arena.add(player);
|
||||
arena.add(skeleton);
|
||||
arena.add(iceGolem);
|
||||
|
||||
System.out.println("Gemischte Arena vor dem Angriff:");
|
||||
printArena(arena);
|
||||
|
||||
arena.attackAll(new FireSpell());
|
||||
|
||||
System.out.println("Gemischte Arena nach dem Feuerzauber:");
|
||||
printArena(arena);
|
||||
|
||||
System.out.println("Lebende Figuren:");
|
||||
printCharacters(arena.getLivingCharacters());
|
||||
|
||||
System.out.println("Besiegte Figuren:");
|
||||
printCharacters(arena.getDefeatedCharacters());
|
||||
|
||||
/*
|
||||
* Besiegte Figuren entfernen.
|
||||
*
|
||||
* getDefeatedCharacters() liefert eine neue Liste,
|
||||
* daher wird das Set nicht während seiner Iteration
|
||||
* verändert.
|
||||
*/
|
||||
for (Character character
|
||||
: arena.getDefeatedCharacters()) {
|
||||
arena.remove(character);
|
||||
}
|
||||
|
||||
System.out.println("Arena nach dem Entfernen besiegter Figuren:");
|
||||
printArena(arena);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generische Hilfsmethode:
|
||||
* Funktioniert für Arena<Player>,
|
||||
* Arena<Enemy> und Arena<Character>.
|
||||
*/
|
||||
private static <C extends Character> void printArena(
|
||||
Arena<C> arena
|
||||
) {
|
||||
System.out.printf(
|
||||
"Größe: %d, lebend: %d, besiegt: %d%n",
|
||||
arena.size(),
|
||||
arena.getLivingCharacters().size(),
|
||||
arena.getDefeatedCharacters().size()
|
||||
);
|
||||
|
||||
printCharacters(arena.getCharacters());
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterable<? extends Character> erlaubt sowohl
|
||||
* Set<C> als auch List<C>.
|
||||
*/
|
||||
private static void printCharacters(
|
||||
Iterable<? extends Character> characters
|
||||
) {
|
||||
for (Character character : characters) {
|
||||
System.out.printf(
|
||||
"%s: %d HP, besiegt: %s%n",
|
||||
character.getName(),
|
||||
character.getHitPoints(),
|
||||
character.isDefeated()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/Player.java
Normal file
54
src/Player.java
Normal file
@@ -0,0 +1,54 @@
|
||||
public class Player implements Character {
|
||||
|
||||
private final String name;
|
||||
private int hitPoints;
|
||||
|
||||
public Player(String name, int hitPoints) {
|
||||
if (name == null || name.isBlank()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Name must not be null or blank"
|
||||
);
|
||||
}
|
||||
|
||||
if (hitPoints <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Hit points must be positive"
|
||||
);
|
||||
}
|
||||
|
||||
this.name = name;
|
||||
this.hitPoints = hitPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHitPoints() {
|
||||
return hitPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefeated() {
|
||||
return hitPoints <= 0;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(FireSpell attack) {
|
||||
hitPoints = Math.max(
|
||||
0,
|
||||
hitPoints - attack.getBaseDamage()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(SwordAttack attack) {
|
||||
hitPoints = Math.max(
|
||||
0,
|
||||
hitPoints - attack.getBaseDamage()
|
||||
);
|
||||
}
|
||||
}
|
||||
41
src/Skeleton.java
Normal file
41
src/Skeleton.java
Normal file
@@ -0,0 +1,41 @@
|
||||
import java.util.Objects;
|
||||
|
||||
public final class Skeleton extends Enemy {
|
||||
|
||||
private static final int SWORD_BONUS_DAMAGE = 10;
|
||||
private static final int FIRE_RESISTANCE = 5;
|
||||
|
||||
public Skeleton(String name, int hitPoints) {
|
||||
super(name, hitPoints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(SwordAttack attack) {
|
||||
Objects.requireNonNull(
|
||||
attack,
|
||||
"Attack must not be null"
|
||||
);
|
||||
|
||||
int damage =
|
||||
attack.getBaseDamage()
|
||||
+ SWORD_BONUS_DAMAGE;
|
||||
|
||||
takeDamage(damage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(FireSpell attack) {
|
||||
Objects.requireNonNull(
|
||||
attack,
|
||||
"Attack must not be null"
|
||||
);
|
||||
|
||||
int damage = Math.max(
|
||||
0,
|
||||
attack.getBaseDamage()
|
||||
- FIRE_RESISTANCE
|
||||
);
|
||||
|
||||
takeDamage(damage);
|
||||
}
|
||||
}
|
||||
20
src/SwordAttack.java
Normal file
20
src/SwordAttack.java
Normal file
@@ -0,0 +1,20 @@
|
||||
import java.util.Objects;
|
||||
|
||||
public final class SwordAttack extends Attack {
|
||||
|
||||
private static final int BASE_DAMAGE = 30;
|
||||
|
||||
public SwordAttack() {
|
||||
super(BASE_DAMAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Character target) {
|
||||
Objects.requireNonNull(
|
||||
target,
|
||||
"Target must not be null"
|
||||
);
|
||||
|
||||
target.receive(this);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user