From 1622fc12c8c9fcce5955e5de57fdfb55a7c2b144 Mon Sep 17 00:00:00 2001 From: Aster000000 Date: Wed, 15 Oct 2025 23:53:21 +0200 Subject: [PATCH 01/19] =?UTF-8?q?Ett=20f=C3=B6rsta=20utkast=20att=20jobba?= =?UTF-8?q?=20fr=C3=A5n,=20har=20mest=20gjort=20=C3=A4ndringar=20i=20'Char?= =?UTF-8?q?acter'=20s=C3=A5=20att=20den=20ska=20p=C3=A5verka=20implementat?= =?UTF-8?q?ionerna=20av=20'Player'=20och=20'Monster'=20s=C3=A5=20lite=20so?= =?UTF-8?q?m=20m=C3=B6jligt.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Biomes.java | 3 ++ src/main/java/Character.java | 69 +++++++++++++++++++++++++++------- src/main/java/Monster.java | 14 +++++++ src/test/java/MonsterTest.java | 10 +++++ 4 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 src/main/java/Biomes.java create mode 100644 src/main/java/Monster.java create mode 100644 src/test/java/MonsterTest.java diff --git a/src/main/java/Biomes.java b/src/main/java/Biomes.java new file mode 100644 index 0000000..3c61251 --- /dev/null +++ b/src/main/java/Biomes.java @@ -0,0 +1,3 @@ +public enum Biomes { + GRASSLAND, MOUNTAIN, COAST, FOREST //Är inte fäst vid dessa +} diff --git a/src/main/java/Character.java b/src/main/java/Character.java index 17ca63e..2a50619 100644 --- a/src/main/java/Character.java +++ b/src/main/java/Character.java @@ -1,28 +1,69 @@ +//Vill inte göra så mycket med den här klassen, då jag vill påverka implementeringen av Player så lite som möjligt public abstract class Character { - protected String name; - protected int health; - protected int level; - protected Position position; + private static final int DEFAULT_HEALTH = 10; + private static final int DEFAULT_LEVEL = 1; + private static final Position DEFAULT_POSITION = new Position(0,0); //Är detta en bra idé?? Mest för att kunna ha defaultkonstruktor + // Borde jag bara sätta allt till default direkt här???? + private int health; + private int level; + private Position position; + private boolean isAlive; //osäker på om den här variabeln är en bra idé - public Character(String name) { - this.name = name; - this.health = 10; - this.level = 1; + // Hur många varianter på konstruktorn behövs? + public Character() { + this.health = DEFAULT_HEALTH; + this.level = DEFAULT_LEVEL; + this.position = DEFAULT_POSITION; } - public Character(String name, int level) { - this.name = name; + // Jag antar att den som instansierar massa monster i världen ansvarar + // för att kolla att "position" har ett tillåtet värde. + public Character(Position position) { + this.health = DEFAULT_HEALTH; + this.level = DEFAULT_LEVEL; + this.position = position; + this.isAlive = true; + } + + public Character(int health, int level, Position position) { + this.health = health; this.level = level; + this.position = position; + this.isAlive = true; } - public void setHealth(int i) { - this.health = i; + public int getHealth() { + return health; + } + + public void setHealth(int newHealth) { + health = newHealth; + } + + public int getLevel() { + return level; + } + + public void setLevel(int newLevel) { + level = newLevel; + } + + public Position getPosition() { + return position; + } + + public void setPosition(Position newPosition) { + position = newPosition; } public boolean isAlive() { - return health > 0; + return isAlive; + } + + // "status" är kanske inte ett jättebra namn... + public void setAlive(boolean status) { + isAlive = status; } - public abstract void spawn(int x, int y); } diff --git a/src/main/java/Monster.java b/src/main/java/Monster.java new file mode 100644 index 0000000..b61c1b8 --- /dev/null +++ b/src/main/java/Monster.java @@ -0,0 +1,14 @@ +public abstract class Monster extends Character{ + + public Monster() { + + } + + public Monster(Position position) { + super(position); + } + + public Monster(int health, int level, Position position) { + super(health, level, position); + } +} diff --git a/src/test/java/MonsterTest.java b/src/test/java/MonsterTest.java new file mode 100644 index 0000000..e3238f1 --- /dev/null +++ b/src/test/java/MonsterTest.java @@ -0,0 +1,10 @@ +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + +public class MonsterTest { + + @Test + void nothing_in_particular() { + + } +} -- 2.39.5 From 0d43c72ebcb89310e026dfa44ff7173dad500684 Mon Sep 17 00:00:00 2001 From: Aster000000 Date: Wed, 15 Oct 2025 23:53:21 +0200 Subject: [PATCH 02/19] =?UTF-8?q?Ett=20f=C3=B6rsta=20utkast=20att=20jobba?= =?UTF-8?q?=20fr=C3=A5n,=20har=20mest=20gjort=20=C3=A4ndringar=20i=20'Char?= =?UTF-8?q?acter'=20s=C3=A5=20att=20den=20ska=20p=C3=A5verka=20implementat?= =?UTF-8?q?ionerna=20av=20'Player'=20och=20'Monster'=20s=C3=A5=20lite=20so?= =?UTF-8?q?m=20m=C3=B6jligt.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Biomes.java | 3 ++ src/main/java/Character.java | 70 +++++++++++++++++++++++++++------- src/main/java/Monster.java | 14 +++++++ src/test/java/MonsterTest.java | 10 +++++ 4 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 src/main/java/Biomes.java create mode 100644 src/main/java/Monster.java create mode 100644 src/test/java/MonsterTest.java diff --git a/src/main/java/Biomes.java b/src/main/java/Biomes.java new file mode 100644 index 0000000..3c61251 --- /dev/null +++ b/src/main/java/Biomes.java @@ -0,0 +1,3 @@ +public enum Biomes { + GRASSLAND, MOUNTAIN, COAST, FOREST //Är inte fäst vid dessa +} diff --git a/src/main/java/Character.java b/src/main/java/Character.java index 17ca63e..972193e 100644 --- a/src/main/java/Character.java +++ b/src/main/java/Character.java @@ -1,28 +1,70 @@ +//Vill inte göra så mycket med den här klassen, då jag vill påverka implementeringen av Player så lite som möjligt public abstract class Character { - protected String name; - protected int health; - protected int level; - protected Position position; + private static final int DEFAULT_HEALTH = 10; + private static final int DEFAULT_LEVEL = 1; + private static final Position DEFAULT_POSITION = new Position(0,0); //Är detta en bra idé?? Mest för att kunna ha defaultkonstruktor + // Borde jag bara sätta allt till default direkt här???? + private int health; + private int level; + private Position position; + private boolean isAlive; //osäker på om den här variabeln är en bra idé - public Character(String name) { - this.name = name; - this.health = 10; - this.level = 1; + // Hur många varianter på konstruktorn behövs? + public Character() { + this.health = DEFAULT_HEALTH; + this.level = DEFAULT_LEVEL; + this.position = DEFAULT_POSITION; + isAlive = true; } - public Character(String name, int level) { - this.name = name; + // Jag antar att den som instansierar massa monster i världen ansvarar + // för att kolla att "position" har ett tillåtet värde. + public Character(Position position) { + this.health = DEFAULT_HEALTH; + this.level = DEFAULT_LEVEL; + this.position = position; + this.isAlive = true; + } + + public Character(int health, int level, Position position) { + this.health = health; this.level = level; + this.position = position; + this.isAlive = true; } - public void setHealth(int i) { - this.health = i; + public int getHealth() { + return health; + } + + public void setHealth(int newHealth) { + health = newHealth; + } + + public int getLevel() { + return level; + } + + public void setLevel(int newLevel) { + level = newLevel; + } + + public Position getPosition() { + return position; + } + + public void setPosition(Position newPosition) { + position = newPosition; } public boolean isAlive() { - return health > 0; + return isAlive; + } + + // "status" är kanske inte ett jättebra namn... + public void setAlive(boolean status) { + isAlive = status; } - public abstract void spawn(int x, int y); } diff --git a/src/main/java/Monster.java b/src/main/java/Monster.java new file mode 100644 index 0000000..b61c1b8 --- /dev/null +++ b/src/main/java/Monster.java @@ -0,0 +1,14 @@ +public abstract class Monster extends Character{ + + public Monster() { + + } + + public Monster(Position position) { + super(position); + } + + public Monster(int health, int level, Position position) { + super(health, level, position); + } +} diff --git a/src/test/java/MonsterTest.java b/src/test/java/MonsterTest.java new file mode 100644 index 0000000..e3238f1 --- /dev/null +++ b/src/test/java/MonsterTest.java @@ -0,0 +1,10 @@ +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + +public class MonsterTest { + + @Test + void nothing_in_particular() { + + } +} -- 2.39.5 From ab19423f26fe138dc88a3a85f1182e70d35047f0 Mon Sep 17 00:00:00 2001 From: emja4309 Date: Wed, 15 Oct 2025 23:59:44 +0200 Subject: [PATCH 03/19] Update src/main/java/Character.java --- src/main/java/Character.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/Character.java b/src/main/java/Character.java index 2a50619..972193e 100644 --- a/src/main/java/Character.java +++ b/src/main/java/Character.java @@ -15,6 +15,7 @@ public abstract class Character { this.health = DEFAULT_HEALTH; this.level = DEFAULT_LEVEL; this.position = DEFAULT_POSITION; + isAlive = true; } // Jag antar att den som instansierar massa monster i världen ansvarar -- 2.39.5 From fdf039348e5c5d5c16b7c0e8035919ec89d9b1fb Mon Sep 17 00:00:00 2001 From: Aster000000 Date: Thu, 16 Oct 2025 11:36:26 +0200 Subject: [PATCH 04/19] =?UTF-8?q?Allt=20jag=20jobbat=20p=C3=A5=20under=20m?= =?UTF-8?q?orgonen.=20Mycket=20kommer=20nog=20tas=20bort/integreras=20med?= =?UTF-8?q?=20andras=20delar=20fram=C3=A5t,=20s=C3=A5=20m=C3=A5nga=20av=20?= =?UTF-8?q?dom=20nya=20klasserna=20=C3=A4r=20fr=C3=A4mst=20f=C3=B6r=20att?= =?UTF-8?q?=20illustrera=20logiken=20jag=20t=C3=A4nker=20mig/funderar=20?= =?UTF-8?q?=C3=B6ver.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Attack.java | 22 ++++++++++++++++ src/main/java/Character.java | 47 +++++++++++++++++++--------------- src/main/java/Monster.java | 22 +++++++++++++--- src/main/java/Shade.java | 3 +++ src/test/java/MonsterTest.java | 42 +++++++++++++++++++++++++++++- 5 files changed, 111 insertions(+), 25 deletions(-) create mode 100644 src/main/java/Attack.java create mode 100644 src/main/java/Shade.java diff --git a/src/main/java/Attack.java b/src/main/java/Attack.java new file mode 100644 index 0000000..a292f3e --- /dev/null +++ b/src/main/java/Attack.java @@ -0,0 +1,22 @@ +public abstract class Attack { + //Namnet och bägge dessa siffror är helt lösryckta + private static final String DEFAULT_NAME = "DEFAULT_NAME"; + private static final double DEFAULT_ENERGY_COST = 5d; + private static final double DEFAULT_DAMAGE = 5d; + + private String name; + private double energyCost; + private double damage; + + public Attack() { + name = DEFAULT_NAME; + energyCost = DEFAULT_ENERGY_COST; + damage = DEFAULT_DAMAGE; + } + + public Attack(String name, double energyCost, double damage) { + this.name = name; + this.energyCost = energyCost; + this.damage = damage; + } +} diff --git a/src/main/java/Character.java b/src/main/java/Character.java index 972193e..80387ef 100644 --- a/src/main/java/Character.java +++ b/src/main/java/Character.java @@ -1,40 +1,47 @@ //Vill inte göra så mycket med den här klassen, då jag vill påverka implementeringen av Player så lite som möjligt public abstract class Character { - private static final int DEFAULT_HEALTH = 10; - private static final int DEFAULT_LEVEL = 1; + private static final double DEFAULT_HEALTH = 50d; + private static final double DEFAULT_LEVEL = 1.0; + private static final double DEFAULT_ENERGY = 50d; //Detta borde kanske egentligen beräknas och sättas automatiskt genom en algoritm som tar health och level i beaktskap?? Eller något sådant private static final Position DEFAULT_POSITION = new Position(0,0); //Är detta en bra idé?? Mest för att kunna ha defaultkonstruktor // Borde jag bara sätta allt till default direkt här???? - private int health; - private int level; + private double health; + private double level; + private double energy; //Borde kanske beräknas genom en algoritm istället för att kunna sättas i konstruktorn... Så det alltid blir balanserat private Position position; - private boolean isAlive; //osäker på om den här variabeln är en bra idé // Hur många varianter på konstruktorn behövs? public Character() { this.health = DEFAULT_HEALTH; this.level = DEFAULT_LEVEL; + this.energy = DEFAULT_ENERGY; this.position = DEFAULT_POSITION; - isAlive = true; } // Jag antar att den som instansierar massa monster i världen ansvarar // för att kolla att "position" har ett tillåtet värde. + // Just denna variant är bara för testning i min implementation av Monster public Character(Position position) { this.health = DEFAULT_HEALTH; this.level = DEFAULT_LEVEL; + this.energy = DEFAULT_ENERGY; this.position = position; - this.isAlive = true; } - public Character(int health, int level, Position position) { + public Character(double health, double level, double energy, Position position) { this.health = health; this.level = level; + this.energy = energy; this.position = position; - this.isAlive = true; } - public int getHealth() { + //Returnerar true om attacken gick igenom, annars false + //Tänker att positionen som skickas in är karaktärens egna + //Kommer antagligen behöva diverse hjälpmetoder i implementeringen då det är många krav som måste uppfyllas för att attacken ska gå igenom + abstract boolean attack(Position position, Character character); + + public double getHealth() { return health; } @@ -42,7 +49,7 @@ public abstract class Character { health = newHealth; } - public int getLevel() { + public double getLevel() { return level; } @@ -50,6 +57,14 @@ public abstract class Character { level = newLevel; } + public double getEnergy() { + return energy; + } + + public void setEnergy(double newEnergy) { + energy = newEnergy; + } + public Position getPosition() { return position; } @@ -57,14 +72,4 @@ public abstract class Character { public void setPosition(Position newPosition) { position = newPosition; } - - public boolean isAlive() { - return isAlive; - } - - // "status" är kanske inte ett jättebra namn... - public void setAlive(boolean status) { - isAlive = status; - } - } diff --git a/src/main/java/Monster.java b/src/main/java/Monster.java index b61c1b8..bc69f90 100644 --- a/src/main/java/Monster.java +++ b/src/main/java/Monster.java @@ -1,14 +1,30 @@ +import java.util.*; + public abstract class Monster extends Character{ - public Monster() { + private final List habitat = new ArrayList<>(); + public Monster() { + habitat.addAll(Arrays.asList(Biomes.GRASSLAND, Biomes.MOUNTAIN, Biomes.COAST, Biomes.FOREST)); } public Monster(Position position) { super(position); + habitat.addAll(Arrays.asList(Biomes.GRASSLAND, Biomes.MOUNTAIN, Biomes.COAST, Biomes.FOREST)); } - public Monster(int health, int level, Position position) { - super(health, level, position); + public Monster(double health, double level, double energy, Position position) { + super(health, level, energy, position); + habitat.addAll(Arrays.asList(Biomes.GRASSLAND, Biomes.MOUNTAIN, Biomes.COAST, Biomes.FOREST)); + } + + public Monster(double health, double level, double energy, Position position, List habitat) { + super(health, level, energy, position); + this.habitat.addAll(habitat); + } + + //Är detta bra??? Med unmodifiableList dvs + public List getHabitat() { + return Collections.unmodifiableList(habitat); } } diff --git a/src/main/java/Shade.java b/src/main/java/Shade.java new file mode 100644 index 0000000..047fc68 --- /dev/null +++ b/src/main/java/Shade.java @@ -0,0 +1,3 @@ +public class Shade extends Monster { + +} diff --git a/src/test/java/MonsterTest.java b/src/test/java/MonsterTest.java index e3238f1..cc75cd4 100644 --- a/src/test/java/MonsterTest.java +++ b/src/test/java/MonsterTest.java @@ -4,7 +4,47 @@ import org.junit.jupiter.api.Test; public class MonsterTest { @Test - void nothing_in_particular() { + void monster_cannot_attack_when_dead() { + /* + Monster someMonster = new Monster(); + someMonster.setHealth(-5d); + Assert(false, someMonster.attack(someMonster.getPosition(), character)); + */ + } + + @Test + void monster_cannot_be_attacked_when_dead() { + /* + Monster attackingMonster = new Monster(); + Monster deadMonster = new Monster(); + deadMonster.setHealth(-4d); + Assert(false, attackingMonster.attack(attackingMonster.getPosition(), deadMonster)); + */ + } + + @Test + void monster_cannot_attack_character_at_different_position() { + /* + Monster monsterAtOnePosition = new Monster(new Position(0,0)); + Monster monsterAtDifferentPosition = new Monster(new Position(1,1)); + Assert(false, monsterAtOnePosition.attack(monsterAtOnePosition.getPosition(), monsterAtDifferentPosition); + */ + } + + @Test + void monster_is_instantiated_correctly_from_default_constructor() { + /* + AssertAll???? + */ + } + + @Test + void monster_is_instantiated_correctly_from_position_only_constructor() { + + } + + @Test + void monster_is_instantiated_correctly_from_full_constructor() { } } -- 2.39.5 From c0e93c9a427a0ef016d659d5714be09cebb475eb Mon Sep 17 00:00:00 2001 From: TricyOne Date: Thu, 23 Oct 2025 10:20:38 +0200 Subject: [PATCH 05/19] starting to work on wizard actions --- src/main/java/Action/learnSpellAction.java | 19 +++++++++++++++++ src/main/java/Character/HasSpellBook.java | 7 +++++++ src/main/java/Entity/Player.java | 4 ++++ src/main/java/Inventory/HasSpellBook.java | 7 +++++++ src/main/java/Job/Wizard.java | 24 ++++++++++++++++++++++ src/test/java/PlayerTest.java | 2 ++ src/test/java/WizardTest.java | 7 +++++++ 7 files changed, 70 insertions(+) create mode 100644 src/main/java/Action/learnSpellAction.java create mode 100644 src/main/java/Character/HasSpellBook.java create mode 100644 src/main/java/Inventory/HasSpellBook.java create mode 100644 src/main/java/Job/Wizard.java create mode 100644 src/test/java/WizardTest.java diff --git a/src/main/java/Action/learnSpellAction.java b/src/main/java/Action/learnSpellAction.java new file mode 100644 index 0000000..019367e --- /dev/null +++ b/src/main/java/Action/learnSpellAction.java @@ -0,0 +1,19 @@ +package Action; + +import Job.HasJob; +import Job.Wizard; + +public class learnSpellAction implements Action { + @Override + public void exectue(Actor actor) { + Wizard wizard = requireWizard(actor); + wizard.learnSpell(actor); + } + + private Wizard requireWizard(Actor actor) { + if (actor instanceof HasJob hasJob && hasJob.getJob() instanceof Wizard wizard) { + return wizard; + } + throw new IllegalStateException(actor + " cannot perform this action without being a Wizard!"); + } +} diff --git a/src/main/java/Character/HasSpellBook.java b/src/main/java/Character/HasSpellBook.java new file mode 100644 index 0000000..6b3c6a4 --- /dev/null +++ b/src/main/java/Character/HasSpellBook.java @@ -0,0 +1,7 @@ +package Character; + +import java.util.List; + +public interface HasSpellBook { + List getSpellBook(); +} diff --git a/src/main/java/Entity/Player.java b/src/main/java/Entity/Player.java index 8b49942..c23bc4f 100644 --- a/src/main/java/Entity/Player.java +++ b/src/main/java/Entity/Player.java @@ -6,6 +6,7 @@ import Combat.HasHealth; import Job.Job; import Job.HasJob; import Inventory.HasInventory; +import Inventory.HasSpellBook; import java.util.LinkedList; import java.util.List; @@ -15,6 +16,7 @@ public class Player extends Entity implements Movable, Actor, HasInventory, HasJ protected Job job; protected Position position; protected List items = new LinkedList<>(); + protected List spells = new LinkedList<>(); public Player(String name, Job job) { super(name); this.job = job; @@ -49,6 +51,8 @@ public class Player extends Entity implements Movable, Actor, HasInventory, HasJ return items; } + public List getSpellBook() {return spells;} + @Override public void performAction(Action action) { action.exectue(this); diff --git a/src/main/java/Inventory/HasSpellBook.java b/src/main/java/Inventory/HasSpellBook.java new file mode 100644 index 0000000..9078576 --- /dev/null +++ b/src/main/java/Inventory/HasSpellBook.java @@ -0,0 +1,7 @@ +package Inventory; + +import java.util.List; + +public interface HasSpellBook { + List getSpellBook(); +} diff --git a/src/main/java/Job/Wizard.java b/src/main/java/Job/Wizard.java new file mode 100644 index 0000000..5456e68 --- /dev/null +++ b/src/main/java/Job/Wizard.java @@ -0,0 +1,24 @@ +package Job; + +import Action.Actor; +import Character.HasSpellBook; + +public class Wizard extends Job { + public Wizard() {super("Wizard");} + + public void learnSpell(Actor actor) { + if(actor instanceof HasSpellBook a ) { + a.getSpellBook().add("Fireball"); + } + } + + @Override + public int getExperience() { + return experience; + } + + @Override + public void gainExperience(int exp) { + experience += exp; + } +} diff --git a/src/test/java/PlayerTest.java b/src/test/java/PlayerTest.java index 37157aa..176a7a1 100644 --- a/src/test/java/PlayerTest.java +++ b/src/test/java/PlayerTest.java @@ -1,8 +1,10 @@ import Action.DigAction; +import Action.learnSpellAction; import Entity.Position; import Job.Miner; import Entity.Player; import org.junit.jupiter.api.Test; +import Job.Wizard; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/WizardTest.java b/src/test/java/WizardTest.java new file mode 100644 index 0000000..8150b6f --- /dev/null +++ b/src/test/java/WizardTest.java @@ -0,0 +1,7 @@ +import Job.Wizard; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class WizardTest { + +} -- 2.39.5 From b2dcc7345315974fb622ba3a58af41417d68ed75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20N=C3=A4slund?= Date: Thu, 23 Oct 2025 12:05:12 +0200 Subject: [PATCH 06/19] Install mockito --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index 0680bce..dfd3061 100644 --- a/pom.xml +++ b/pom.xml @@ -20,6 +20,12 @@ 5.8.1 test + + org.mockito + mockito-core + 5.12.0 + test + \ No newline at end of file -- 2.39.5 From 98e7589d04971861117cd4251882b5b3dc61cbc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20N=C3=A4slund?= Date: Thu, 23 Oct 2025 12:31:16 +0200 Subject: [PATCH 07/19] Base implementation of test --- src/main/java/Job/Job.java | 1 + src/main/java/Job/Miner.java | 3 +++ src/test/java/MinerTest.java | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/src/main/java/Job/Job.java b/src/main/java/Job/Job.java index e399ed3..2fb1232 100644 --- a/src/main/java/Job/Job.java +++ b/src/main/java/Job/Job.java @@ -20,6 +20,7 @@ public abstract class Job implements HasExperience { public void levelUp() { level++; + experience = 0; } @Override diff --git a/src/main/java/Job/Miner.java b/src/main/java/Job/Miner.java index ae10edd..f6058b0 100644 --- a/src/main/java/Job/Miner.java +++ b/src/main/java/Job/Miner.java @@ -23,5 +23,8 @@ public class Miner extends Job { @Override public void gainExperience(int exp) { experience += exp; + if (experience >= 100) { + levelUp(); + } } } diff --git a/src/test/java/MinerTest.java b/src/test/java/MinerTest.java index 0aad49b..a437945 100644 --- a/src/test/java/MinerTest.java +++ b/src/test/java/MinerTest.java @@ -20,6 +20,14 @@ public class MinerTest { assertEquals(25, job.getExperience()); } + @Test + void miner_level_up_when_experience_is_100() { + var job = new Miner(); + job.gainExperience(100); + assertEquals(0, job.getExperience()); + assertEquals(2, job.getLevel()); + } + } -- 2.39.5 From acc365c78aaf1c211a5f7e975dfba26d64de9630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20N=C3=A4slund?= Date: Fri, 24 Oct 2025 00:57:24 +0200 Subject: [PATCH 08/19] Correct spelling --- src/main/java/Action/Action.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/Action/Action.java b/src/main/java/Action/Action.java index e1bf012..b65a6c4 100644 --- a/src/main/java/Action/Action.java +++ b/src/main/java/Action/Action.java @@ -1,5 +1,5 @@ package Action; public interface Action { - void exectue(Actor player); + void execute(Actor player); } -- 2.39.5 From 2587acc00bec09cc78b78af404f181ee790e4c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20N=C3=A4slund?= Date: Fri, 24 Oct 2025 00:58:00 +0200 Subject: [PATCH 09/19] Change spelling --- src/main/java/Action/DigAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/Action/DigAction.java b/src/main/java/Action/DigAction.java index 7fcc7fa..4d652e2 100644 --- a/src/main/java/Action/DigAction.java +++ b/src/main/java/Action/DigAction.java @@ -5,7 +5,7 @@ import Job.Miner; public class DigAction implements Action { @Override - public void exectue(Actor actor) { + public void execute(Actor actor) { Miner miner = requireMiner(actor); miner.dig(actor); } -- 2.39.5 From 33a2132e07f4ff93d17cc92a5c8da309d6126e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20N=C3=A4slund?= Date: Fri, 24 Oct 2025 00:58:28 +0200 Subject: [PATCH 10/19] Create Inventory --- src/main/java/Entity/Player.java | 9 +++++---- src/main/java/Inventory/HasInventory.java | 2 +- src/main/java/Inventory/Inventory.java | 24 +++++++++++++++++++++++ src/main/java/Job/Miner.java | 2 +- src/test/java/PlayerTest.java | 5 ++--- 5 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 src/main/java/Inventory/Inventory.java diff --git a/src/main/java/Entity/Player.java b/src/main/java/Entity/Player.java index 8b49942..779778a 100644 --- a/src/main/java/Entity/Player.java +++ b/src/main/java/Entity/Player.java @@ -3,6 +3,7 @@ package Entity; import Action.Action; import Action.Actor; import Combat.HasHealth; +import Inventory.Inventory; import Job.Job; import Job.HasJob; import Inventory.HasInventory; @@ -14,7 +15,7 @@ public class Player extends Entity implements Movable, Actor, HasInventory, HasJ protected int health; protected Job job; protected Position position; - protected List items = new LinkedList<>(); + protected Inventory inventory= new Inventory(); public Player(String name, Job job) { super(name); this.job = job; @@ -45,13 +46,13 @@ public class Player extends Entity implements Movable, Actor, HasInventory, HasJ this.job = job; } - public List getInventory() { - return items; + public Inventory getInventory() { + return inventory; } @Override public void performAction(Action action) { - action.exectue(this); + action.execute(this); } @Override diff --git a/src/main/java/Inventory/HasInventory.java b/src/main/java/Inventory/HasInventory.java index 856c2da..2b894b7 100644 --- a/src/main/java/Inventory/HasInventory.java +++ b/src/main/java/Inventory/HasInventory.java @@ -3,5 +3,5 @@ package Inventory; import java.util.List; public interface HasInventory { - List getInventory(); + Inventory getInventory(); } diff --git a/src/main/java/Inventory/Inventory.java b/src/main/java/Inventory/Inventory.java new file mode 100644 index 0000000..786cfda --- /dev/null +++ b/src/main/java/Inventory/Inventory.java @@ -0,0 +1,24 @@ +package Inventory; + +import java.util.ArrayList; +import java.util.List; + +public class Inventory { + List items; + + public Inventory() { + items = new ArrayList<>(); + } + + List getItems() { + return items; + } + + public void addItem(String item) { + items.add(item); + } + + public boolean containsItem(String item) { + return items.contains(item); + } +} diff --git a/src/main/java/Job/Miner.java b/src/main/java/Job/Miner.java index f6058b0..9e8c690 100644 --- a/src/main/java/Job/Miner.java +++ b/src/main/java/Job/Miner.java @@ -11,7 +11,7 @@ public class Miner extends Job { public void dig(Actor actor) { if (actor instanceof HasInventory a) { - a.getInventory().add("Stone"); + a.getInventory().addItem("Stone"); } } diff --git a/src/test/java/PlayerTest.java b/src/test/java/PlayerTest.java index 37157aa..9c7555e 100644 --- a/src/test/java/PlayerTest.java +++ b/src/test/java/PlayerTest.java @@ -38,9 +38,8 @@ class PlayerTest { @Test void miner_can_dig() { var p = new Player("John"); - var job = new Miner(); - p.learnJob(job); + p.learnJob(new Miner()); p.performAction(new DigAction()); - assertTrue(p.getInventory().contains("Stone")); + assertTrue(p.getInventory().containsItem("Stone")); } } \ No newline at end of file -- 2.39.5 From a5620a7611ef1de2f9be8d6576533dd35117458f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20N=C3=A4slund?= Date: Fri, 24 Oct 2025 01:00:01 +0200 Subject: [PATCH 11/19] Set default position --- src/main/java/Entity/Player.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/Entity/Player.java b/src/main/java/Entity/Player.java index 779778a..3a3ab9b 100644 --- a/src/main/java/Entity/Player.java +++ b/src/main/java/Entity/Player.java @@ -14,7 +14,7 @@ import java.util.List; public class Player extends Entity implements Movable, Actor, HasInventory, HasJob, HasHealth { protected int health; protected Job job; - protected Position position; + protected Position position = new Position(0,0); protected Inventory inventory= new Inventory(); public Player(String name, Job job) { super(name); @@ -35,7 +35,7 @@ public class Player extends Entity implements Movable, Actor, HasInventory, HasJ @Override public boolean canMoveTo(Position position) { - return false; + return true; } public Job getJob() { return job; -- 2.39.5 From 9892a229dc9b5a5c67084633a7079bf809c2c9cb Mon Sep 17 00:00:00 2001 From: TricyOne Date: Fri, 24 Oct 2025 12:02:40 +0200 Subject: [PATCH 12/19] A lot of magic work --- src/main/java/Action/CastAction.java | 19 +++++++ src/main/java/Combat/HasHealth.java | 1 + src/main/java/Combat/HasMana.java | 6 +++ .../java/Combat/OffensiveDamageSpell.java | 18 +++++++ src/main/java/Combat/Spell.java | 54 +++++++++++++++++++ src/main/java/Entity/Player.java | 18 +++++-- src/main/java/Inventory/HasSpellBook.java | 3 +- src/main/java/Job/Wizard.java | 43 ++++++++++++++- src/main/java/Spell.java | 18 ------- src/test/java/PlayerTest.java | 12 +++++ src/test/java/SpellTest.java | 8 +-- src/test/java/WizardTest.java | 19 +++++++ 12 files changed, 192 insertions(+), 27 deletions(-) create mode 100644 src/main/java/Action/CastAction.java create mode 100644 src/main/java/Combat/HasMana.java create mode 100644 src/main/java/Combat/OffensiveDamageSpell.java create mode 100644 src/main/java/Combat/Spell.java delete mode 100644 src/main/java/Spell.java diff --git a/src/main/java/Action/CastAction.java b/src/main/java/Action/CastAction.java new file mode 100644 index 0000000..1817090 --- /dev/null +++ b/src/main/java/Action/CastAction.java @@ -0,0 +1,19 @@ +package Action; + +import Job.HasJob; +import Job.Wizard; + +public class CastAction implements Action { + @Override + public void exectue(Actor actor) { + Wizard wizard = requireWizard(actor); + wizard.castSpell(actor); + } + + private Wizard requireWizard(Actor actor) { + if (actor instanceof HasJob hasJob && hasJob.getJob() instanceof Wizard wizard) { + return wizard; + } + throw new IllegalStateException(actor + " cannot perform this action without being a Wizard!"); + } +} diff --git a/src/main/java/Combat/HasHealth.java b/src/main/java/Combat/HasHealth.java index 47e44c8..b5c7184 100644 --- a/src/main/java/Combat/HasHealth.java +++ b/src/main/java/Combat/HasHealth.java @@ -2,5 +2,6 @@ package Combat; public interface HasHealth { void setHealth(int health); + int getHealth(); boolean isAlive(); } diff --git a/src/main/java/Combat/HasMana.java b/src/main/java/Combat/HasMana.java new file mode 100644 index 0000000..8964f48 --- /dev/null +++ b/src/main/java/Combat/HasMana.java @@ -0,0 +1,6 @@ +package Combat; + +public interface HasMana { + void setMana(int mana); + int getMana(); +} diff --git a/src/main/java/Combat/OffensiveDamageSpell.java b/src/main/java/Combat/OffensiveDamageSpell.java new file mode 100644 index 0000000..05bea11 --- /dev/null +++ b/src/main/java/Combat/OffensiveDamageSpell.java @@ -0,0 +1,18 @@ +package Combat; + +import Entity.Entity; + +public class OffensiveDamageSpell extends Spell { + + public OffensiveDamageSpell(String spellName, int cost, int potency) { + super(spellName, cost, potency); + } + + @Override + public void cast(Entity source, Entity target) { + if(target instanceof HasHealth health && source instanceof HasMana mana) { + mana.setMana(mana.getMana() - this.getCost()); + health.setHealth(health.getHealth() - this.getPotency()); + } + } +} diff --git a/src/main/java/Combat/Spell.java b/src/main/java/Combat/Spell.java new file mode 100644 index 0000000..927442b --- /dev/null +++ b/src/main/java/Combat/Spell.java @@ -0,0 +1,54 @@ +package Combat; + +import Entity.Entity; + +public abstract class Spell { + private final String spellName; + private int cost; + private int potency; + + public Spell(String spellName, int cost, int potency) { + this.spellName = spellName; + this.cost = cost; + this.potency = potency; + } + + public String getSpellName() { + return spellName; + } + + public int getCost() { + return cost; + } + + public int getPotency() { + return potency; + } + + public abstract void cast(Entity source, Entity target); + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Spell spell = (Spell) o; + + if (cost != spell.cost) return false; + if (potency != spell.potency) return false; + return spellName.equals(spell.spellName); + } + + @Override + public int hashCode() { + int result = spellName.hashCode(); + result = 31 * result + cost; + result = 31 * result + potency; + return result; + } + + @Override + public String toString() { + return spellName + " (Cost: " + cost + ", Potency: " + potency + ")"; + } +} \ No newline at end of file diff --git a/src/main/java/Entity/Player.java b/src/main/java/Entity/Player.java index c23bc4f..92d76c6 100644 --- a/src/main/java/Entity/Player.java +++ b/src/main/java/Entity/Player.java @@ -3,6 +3,8 @@ package Entity; import Action.Action; import Action.Actor; import Combat.HasHealth; +import Combat.HasMana; +import Combat.Spell; import Job.Job; import Job.HasJob; import Inventory.HasInventory; @@ -11,12 +13,13 @@ import Inventory.HasSpellBook; import java.util.LinkedList; import java.util.List; -public class Player extends Entity implements Movable, Actor, HasInventory, HasJob, HasHealth { +public class Player extends Entity implements Movable, Actor, HasInventory, HasSpellBook, HasJob, HasHealth, HasMana { protected int health; + protected int mana; protected Job job; protected Position position; protected List items = new LinkedList<>(); - protected List spells = new LinkedList<>(); + protected List spells = new LinkedList<>(); public Player(String name, Job job) { super(name); this.job = job; @@ -51,7 +54,7 @@ public class Player extends Entity implements Movable, Actor, HasInventory, HasJ return items; } - public List getSpellBook() {return spells;} + public List getSpellBook() {return spells;} @Override public void performAction(Action action) { @@ -73,6 +76,15 @@ public class Player extends Entity implements Movable, Actor, HasInventory, HasJ this.health = health; } + @Override + public int getHealth() {return health;} + + @Override + public void setMana(int mana) {this.mana = mana;} + + @Override + public int getMana() {return mana;} + @Override public boolean isAlive() { return health > 0; diff --git a/src/main/java/Inventory/HasSpellBook.java b/src/main/java/Inventory/HasSpellBook.java index 9078576..5188389 100644 --- a/src/main/java/Inventory/HasSpellBook.java +++ b/src/main/java/Inventory/HasSpellBook.java @@ -1,7 +1,8 @@ package Inventory; import java.util.List; +import Combat.Spell; public interface HasSpellBook { - List getSpellBook(); + List getSpellBook(); } diff --git a/src/main/java/Job/Wizard.java b/src/main/java/Job/Wizard.java index 5456e68..9f62513 100644 --- a/src/main/java/Job/Wizard.java +++ b/src/main/java/Job/Wizard.java @@ -1,14 +1,53 @@ package Job; import Action.Actor; -import Character.HasSpellBook; +import Combat.HasMana; +import Combat.OffensiveDamageSpell; +import Entity.Entity; +import Inventory.HasSpellBook; +import Combat.Spell; + +import java.util.Scanner; public class Wizard extends Job { + private static final Scanner scanner = new Scanner(System.in); public Wizard() {super("Wizard");} public void learnSpell(Actor actor) { if(actor instanceof HasSpellBook a ) { - a.getSpellBook().add("Fireball"); + OffensiveDamageSpell defaultSpell = new OffensiveDamageSpell("fireball", 20, 20); + a.getSpellBook().add(defaultSpell); + } + } + + public void castSpell(Actor actor) { + if(actor instanceof HasSpellBook spellbook && actor instanceof HasMana mana && actor instanceof Entity entity) { + if(spellbook.getSpellBook().isEmpty()){ + System.out.println("You haven't learned any spells"); + return; + } + + System.out.println("Which spell do you want to cast?"); + for(int i = 0; i < spellbook.getSpellBook().size(); i++){ + System.out.println((i + 1) + ". " + spellbook.getSpellBook().get(i).toString()); + } + + int spellIndex = scanner.nextInt(); + scanner.nextLine(); + + if(spellIndex < 1 || spellIndex > spellbook.getSpellBook().size()) { + System.out.println("Invalid choice!"); + return; + } + + Spell spell = spellbook.getSpellBook().get(spellIndex - 1); + + if (mana.getMana() >= spell.getCost()){ + spell.cast(entity, entity); + } + else{ + System.out.println("Not enough mana!"); + } } } diff --git a/src/main/java/Spell.java b/src/main/java/Spell.java deleted file mode 100644 index 62ae11d..0000000 --- a/src/main/java/Spell.java +++ /dev/null @@ -1,18 +0,0 @@ -public class Spell { - private final String spellName; - private int cost; - private int potency; - - public Spell(String spellName, int cost, int potency) { - this.spellName = spellName; - this.cost = cost; - this.potency = potency; - } - - public String getSpellName() {return spellName;} - - public int getCost() {return cost;} - - public int getPotency() {return potency;} -} - diff --git a/src/test/java/PlayerTest.java b/src/test/java/PlayerTest.java index 176a7a1..ded18bc 100644 --- a/src/test/java/PlayerTest.java +++ b/src/test/java/PlayerTest.java @@ -1,5 +1,6 @@ import Action.DigAction; import Action.learnSpellAction; +import Combat.OffensiveDamageSpell; import Entity.Position; import Job.Miner; import Entity.Player; @@ -45,4 +46,15 @@ class PlayerTest { p.performAction(new DigAction()); assertTrue(p.getInventory().contains("Stone")); } + + @Test + void wizard_can_learn_spell() { + var p = new Player("Bob"); + var job = new Wizard(); + var defaultSpell = new OffensiveDamageSpell("fireball", 20, 20); + p.learnJob(job); + p.performAction(new learnSpellAction()); + System.out.println(p.getSpellBook()); + assertTrue(p.getSpellBook().contains(defaultSpell)); + } } \ No newline at end of file diff --git a/src/test/java/SpellTest.java b/src/test/java/SpellTest.java index d039d0d..9ed92f4 100644 --- a/src/test/java/SpellTest.java +++ b/src/test/java/SpellTest.java @@ -1,3 +1,5 @@ +import Combat.OffensiveDamageSpell; +import Combat.Spell; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -5,19 +7,19 @@ import static org.junit.jupiter.api.Assertions.*; public class SpellTest { private Spell defaultSpell() { - return new Spell("fireball", 20, 40); + return new OffensiveDamageSpell("fireball", 20, 40); } @Test void setSpellNameOnCreation(){ var spell = defaultSpell(); - assertEquals("fireball", spell.getSpellName(), "Spell name should be set"); + assertEquals("fireball", spell.getSpellName(), "Combat.Spell name should be set"); } @Test void setCostOnCreation() { var spell = defaultSpell(); - assertEquals(20, spell.getCost(), "Spell cost should have been set"); + assertEquals(20, spell.getCost(), "Combat.Spell cost should have been set"); } @Test diff --git a/src/test/java/WizardTest.java b/src/test/java/WizardTest.java index 8150b6f..2fe1093 100644 --- a/src/test/java/WizardTest.java +++ b/src/test/java/WizardTest.java @@ -4,4 +4,23 @@ import static org.junit.jupiter.api.Assertions.*; public class WizardTest { + @Test + void wizard_has_level() { + var job = new Wizard(); + assertEquals(1, job.getLevel()); + } + + @Test + void wizard_can_level_up() { + var job = new Wizard(); + job.levelUp(); + assertEquals(2, job.getLevel()); + } + + @Test + void wizard_can_gain_xp(){ + var job = new Wizard(); + job.gainExperience(40); + assertEquals(40, job.getExperience()); + } } -- 2.39.5 From 82bd72fcb4baeb6e774b8f1f639dcd720d91f76a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20N=C3=A4slund?= Date: Sun, 26 Oct 2025 23:24:56 +0100 Subject: [PATCH 13/19] Import position --- src/main/java/Character.java | 2 ++ src/main/java/Monster.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/main/java/Character.java b/src/main/java/Character.java index 80387ef..3125774 100644 --- a/src/main/java/Character.java +++ b/src/main/java/Character.java @@ -1,3 +1,5 @@ +import Entity.Position; + //Vill inte göra så mycket med den här klassen, då jag vill påverka implementeringen av Player så lite som möjligt public abstract class Character { private static final double DEFAULT_HEALTH = 50d; diff --git a/src/main/java/Monster.java b/src/main/java/Monster.java index bc69f90..a9b3efc 100644 --- a/src/main/java/Monster.java +++ b/src/main/java/Monster.java @@ -1,3 +1,5 @@ +import Entity.Position; + import java.util.*; public abstract class Monster extends Character{ -- 2.39.5 From 3d36d69ed113c3b6437f68f215c6ff13ac0ebd5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20N=C3=A4slund?= Date: Sun, 26 Oct 2025 23:25:08 +0100 Subject: [PATCH 14/19] Remove unused file --- src/main/java/Shade.java | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/main/java/Shade.java diff --git a/src/main/java/Shade.java b/src/main/java/Shade.java deleted file mode 100644 index 047fc68..0000000 --- a/src/main/java/Shade.java +++ /dev/null @@ -1,3 +0,0 @@ -public class Shade extends Monster { - -} -- 2.39.5 From e476a7e0a69321c7f9be86fe72539eeaa1e59b9a Mon Sep 17 00:00:00 2001 From: TricyOne Date: Mon, 27 Oct 2025 09:31:07 +0100 Subject: [PATCH 15/19] tests --- pom.xml | 14 ++++ ...SpellAction.java => LearnSpellAction.java} | 2 +- src/main/java/Job/Wizard.java | 11 ++- src/test/java/InterestingTests.java | 73 +++++++++++++++++++ src/test/java/MagicSystemTest.java | 61 ++++++++++++++++ src/test/java/PlayerTest.java | 16 +++- 6 files changed, 172 insertions(+), 5 deletions(-) rename src/main/java/Action/{learnSpellAction.java => LearnSpellAction.java} (90%) create mode 100644 src/test/java/InterestingTests.java create mode 100644 src/test/java/MagicSystemTest.java diff --git a/pom.xml b/pom.xml index 0680bce..b21c15d 100644 --- a/pom.xml +++ b/pom.xml @@ -20,6 +20,20 @@ 5.8.1 test + + + org.hamcrest + hamcrest + 2.2 + test + + + + org.mockito + mockito-core + 5.11.0 + test + \ No newline at end of file diff --git a/src/main/java/Action/learnSpellAction.java b/src/main/java/Action/LearnSpellAction.java similarity index 90% rename from src/main/java/Action/learnSpellAction.java rename to src/main/java/Action/LearnSpellAction.java index 019367e..74f1422 100644 --- a/src/main/java/Action/learnSpellAction.java +++ b/src/main/java/Action/LearnSpellAction.java @@ -3,7 +3,7 @@ package Action; import Job.HasJob; import Job.Wizard; -public class learnSpellAction implements Action { +public class LearnSpellAction implements Action { @Override public void exectue(Actor actor) { Wizard wizard = requireWizard(actor); diff --git a/src/main/java/Job/Wizard.java b/src/main/java/Job/Wizard.java index 9f62513..db06bf4 100644 --- a/src/main/java/Job/Wizard.java +++ b/src/main/java/Job/Wizard.java @@ -10,8 +10,15 @@ import Combat.Spell; import java.util.Scanner; public class Wizard extends Job { - private static final Scanner scanner = new Scanner(System.in); - public Wizard() {super("Wizard");} + private final Scanner scanner; + public Wizard(Scanner scanner) { + super("Wizard"); + this.scanner = scanner; + } + + public Wizard() { + this(new Scanner(System.in)); + } public void learnSpell(Actor actor) { if(actor instanceof HasSpellBook a ) { diff --git a/src/test/java/InterestingTests.java b/src/test/java/InterestingTests.java new file mode 100644 index 0000000..bb5f449 --- /dev/null +++ b/src/test/java/InterestingTests.java @@ -0,0 +1,73 @@ +import Action.Actor; +import Combat.HasMana; +import Combat.OffensiveDamageSpell; +import Combat.Spell; +import Entity.Entity; +import Entity.Player; +import Inventory.HasSpellBook; +import Job.Wizard; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.List; +import java.util.Scanner; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.mockito.Mockito.*; + +public class InterestingTests { + + private Wizard wizard; + + @BeforeEach + public void setup() { + wizard = new Wizard(); + } + + @Test + public void testCastSpell_successfulCast() { + Scanner testScanner = new Scanner(new ByteArrayInputStream("1\n".getBytes())); + wizard = new Wizard(testScanner); + + Player player = new Player("Gandalf", wizard); + + player.setMana(50); + player.setHealth(100); + + OffensiveDamageSpell fireball = new OffensiveDamageSpell("Fireball", 20, 15); + player.getSpellBook().add(fireball); + + int initialMana = player.getMana(); + int initialHealth = player.getHealth(); + + wizard.castSpell(player); + + assertThat(player.getMana(), is(initialMana - fireball.getCost())); + assertThat(player.getHealth(), is(initialHealth - fireball.getPotency())); + } + + @Test + public void testCastSpell_notEnoughMana() { + Scanner testScanner = new Scanner(new ByteArrayInputStream("1\n".getBytes())); + wizard = new Wizard(testScanner); + + Player player = new Player("Merlin", wizard); + player.setMana(10); + player.setHealth(100); + + OffensiveDamageSpell fireball = new OffensiveDamageSpell("Fireball", 20, 15); + player.getSpellBook().add(fireball); + + int initialMana = player.getMana(); + int initialHealth = player.getHealth(); + + wizard.castSpell(player); + + assertThat(player.getMana(),is(initialMana)); + assertThat(player.getHealth(),is(initialHealth)); + } +} \ No newline at end of file diff --git a/src/test/java/MagicSystemTest.java b/src/test/java/MagicSystemTest.java new file mode 100644 index 0000000..c667fb2 --- /dev/null +++ b/src/test/java/MagicSystemTest.java @@ -0,0 +1,61 @@ +import Action.CastAction; +import Action.LearnSpellAction; +import Combat.OffensiveDamageSpell; +import Entity.Player; +import Job.Wizard; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class MagicSystemTest { + + private Player defaultPlayer() { + return new Player("Alex"); + } + + @Test void cant_cast_spell_without_being_wizard(){ + var p = defaultPlayer(); + CastAction action = new CastAction(); + IllegalStateException exception = assertThrows( + IllegalStateException.class, + () -> action.exectue(p) + ); + + assertTrue(exception.getMessage().contains("cannot perform this action without being a Wizard")); + } + + @Test void cant_learn_spell_without_being_wizard(){ + var p = defaultPlayer(); + LearnSpellAction action = new LearnSpellAction(); + IllegalStateException exception = assertThrows( + IllegalStateException.class, + () -> action.exectue(p) + ); + assertTrue(exception.getMessage().contains("cannot perform this action without being a Wizard")); + } + + @Test void damage_spell_damages_target(){ + var defaultSpell = new OffensiveDamageSpell("fireball", 20, 20); + var p = defaultPlayer(); + p.setMana(30); + p.setHealth(30); + defaultSpell.cast(p, p); + assertEquals(10, p.getHealth()); + } + + @Test void damage_spell_drains_mana_from_caster(){ + var defaultSpell = new OffensiveDamageSpell("fireball", 20, 20); + var p = defaultPlayer(); + p.setMana(30); + p.setHealth(30); + defaultSpell.cast(p, p); + assertEquals(10, p.getMana()); + } + + @Test void cant_cast_spell_with_empty_spellbook(){ + var p = defaultPlayer(); + var job = new Wizard(); + p.learnJob(job); + p.performAction(new CastAction()); + + } +} diff --git a/src/test/java/PlayerTest.java b/src/test/java/PlayerTest.java index ded18bc..94ab26b 100644 --- a/src/test/java/PlayerTest.java +++ b/src/test/java/PlayerTest.java @@ -1,5 +1,5 @@ import Action.DigAction; -import Action.learnSpellAction; +import Action.LearnSpellAction; import Combat.OffensiveDamageSpell; import Entity.Position; import Job.Miner; @@ -21,6 +21,18 @@ class PlayerTest { assertFalse(p.isAlive()); } + @Test void get_health_returns_health() { + var p = defaultPlayer(); + p.setHealth(10); + assertEquals(10, p.getHealth()); + } + + @Test void get_mana_returns_mana() { + var p = defaultPlayer(); + p.setMana(10); + assertEquals(10, p.getMana()); + } + @Test public void can_change_position() { var p = defaultPlayer(); @@ -53,7 +65,7 @@ class PlayerTest { var job = new Wizard(); var defaultSpell = new OffensiveDamageSpell("fireball", 20, 20); p.learnJob(job); - p.performAction(new learnSpellAction()); + p.performAction(new LearnSpellAction()); System.out.println(p.getSpellBook()); assertTrue(p.getSpellBook().contains(defaultSpell)); } -- 2.39.5 From 8390d152ce1ab9b902b48f1bb5c89cbc5070bf86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20N=C3=A4slund?= Date: Mon, 27 Oct 2025 09:59:32 +0100 Subject: [PATCH 16/19] xp carries over on level up --- src/main/java/Job/Job.java | 2 +- src/main/java/Job/Miner.java | 4 ++++ src/test/java/MinerTest.java | 16 +++++++++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/java/Job/Job.java b/src/main/java/Job/Job.java index 2fb1232..d621f36 100644 --- a/src/main/java/Job/Job.java +++ b/src/main/java/Job/Job.java @@ -20,7 +20,7 @@ public abstract class Job implements HasExperience { public void levelUp() { level++; - experience = 0; + experience = experience - 100; } @Override diff --git a/src/main/java/Job/Miner.java b/src/main/java/Job/Miner.java index 9e8c690..6377079 100644 --- a/src/main/java/Job/Miner.java +++ b/src/main/java/Job/Miner.java @@ -27,4 +27,8 @@ public class Miner extends Job { levelUp(); } } + + public int remainingXpUntilLevelUp() { + return 100; + } } diff --git a/src/test/java/MinerTest.java b/src/test/java/MinerTest.java index a437945..448e5c2 100644 --- a/src/test/java/MinerTest.java +++ b/src/test/java/MinerTest.java @@ -6,7 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class MinerTest { @Test - void miner_can_level_up() { + void can_level_up() { var job = new Miner(); assertEquals(1, job.getLevel()); job.levelUp(); @@ -14,20 +14,26 @@ public class MinerTest { } @Test - void miner_can_gain_xp() { + void can_gain_xp() { var job = new Miner(); job.gainExperience(25); assertEquals(25, job.getExperience()); } @Test - void miner_level_up_when_experience_is_100() { + void level_up_when_experience_cap_is_reached() { var job = new Miner(); - job.gainExperience(100); - assertEquals(0, job.getExperience()); + job.gainExperience(job.remainingXpUntilLevelUp()); assertEquals(2, job.getLevel()); } + @Test + void additional_xp_carries_over_on_level_up() { + var job = new Miner(); + job.gainExperience(job.remainingXpUntilLevelUp() + 10); + assertEquals(10, job.getExperience()); + } + } -- 2.39.5 From d1a1047682fcd281c2cb220a7c88de21238ce005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20N=C3=A4slund?= Date: Mon, 27 Oct 2025 10:18:13 +0100 Subject: [PATCH 17/19] Create XP lookup table --- src/main/java/Job/Job.java | 36 +++++++++++++++---- src/main/java/Job/Miner.java | 17 --------- src/main/java/Shared/ExperienceTable.java | 16 +++++++++ .../{HasExperience.java => Levelable.java} | 4 ++- 4 files changed, 48 insertions(+), 25 deletions(-) create mode 100644 src/main/java/Shared/ExperienceTable.java rename src/main/java/Shared/{HasExperience.java => Levelable.java} (65%) diff --git a/src/main/java/Job/Job.java b/src/main/java/Job/Job.java index d621f36..28c7c57 100644 --- a/src/main/java/Job/Job.java +++ b/src/main/java/Job/Job.java @@ -1,10 +1,11 @@ package Job; -import Shared.HasExperience; +import Shared.ExperienceTable; +import Shared.Levelable; import java.util.Objects; -public abstract class Job implements HasExperience { +public abstract class Job implements Levelable { protected int level; protected int experience; protected String name; @@ -18,11 +19,6 @@ public abstract class Job implements HasExperience { return level; } - public void levelUp() { - level++; - experience = experience - 100; - } - @Override public String toString() { return String.format("Job: %s. Level: %d", name, level); @@ -39,4 +35,30 @@ public abstract class Job implements HasExperience { return Objects.hash(name); } + @Override + public int remainingXpUntilLevelUp() { + return ExperienceTable.getXpForLevel(level) - experience; + } + + @Override + public void gainExperience(int exp) { + experience += exp; + if (experience >= ExperienceTable.getXpForLevel(level)) { + levelUp(); + } + } + + @Override + public int getExperience() { + return experience; + } + + @Override + public void levelUp() { + experience = experience - ExperienceTable.getXpForLevel(level); + level++; + } + + + } diff --git a/src/main/java/Job/Miner.java b/src/main/java/Job/Miner.java index 6377079..79c56ec 100644 --- a/src/main/java/Job/Miner.java +++ b/src/main/java/Job/Miner.java @@ -14,21 +14,4 @@ public class Miner extends Job { a.getInventory().addItem("Stone"); } } - - @Override - public int getExperience() { - return experience; - } - - @Override - public void gainExperience(int exp) { - experience += exp; - if (experience >= 100) { - levelUp(); - } - } - - public int remainingXpUntilLevelUp() { - return 100; - } } diff --git a/src/main/java/Shared/ExperienceTable.java b/src/main/java/Shared/ExperienceTable.java new file mode 100644 index 0000000..1bc0de6 --- /dev/null +++ b/src/main/java/Shared/ExperienceTable.java @@ -0,0 +1,16 @@ +package Shared; + +import java.util.Map; + +public final class ExperienceTable { + private static final Map XP_TABLE = Map.of( + 1, 100, + 2, 250 + ); + + private ExperienceTable() {} + + public static int getXpForLevel(int level) { + return XP_TABLE.getOrDefault(level, Integer.MAX_VALUE); + } +} diff --git a/src/main/java/Shared/HasExperience.java b/src/main/java/Shared/Levelable.java similarity index 65% rename from src/main/java/Shared/HasExperience.java rename to src/main/java/Shared/Levelable.java index fcfb9ad..039c8ba 100644 --- a/src/main/java/Shared/HasExperience.java +++ b/src/main/java/Shared/Levelable.java @@ -1,6 +1,6 @@ package Shared; -public interface HasExperience { +public interface Levelable { int getLevel(); int getExperience(); @@ -8,4 +8,6 @@ public interface HasExperience { void gainExperience(int exp); void levelUp(); + + int remainingXpUntilLevelUp(); } -- 2.39.5 From af344e1c36a0bf012490f0e1dfae1da3181a24ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20N=C3=A4slund?= Date: Mon, 27 Oct 2025 10:26:17 +0100 Subject: [PATCH 18/19] Formatting --- src/main/java/Job/Job.java | 33 ++++++++++++++++----------------- src/test/java/MinerTest.java | 2 ++ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/main/java/Job/Job.java b/src/main/java/Job/Job.java index 28c7c57..a25fe60 100644 --- a/src/main/java/Job/Job.java +++ b/src/main/java/Job/Job.java @@ -19,25 +19,9 @@ public abstract class Job implements Levelable { return level; } - @Override - public String toString() { - return String.format("Job: %s. Level: %d", name, level); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - return o != null && getClass() == o.getClass(); - } - - @Override - public int hashCode() { - return Objects.hash(name); - } - @Override public int remainingXpUntilLevelUp() { - return ExperienceTable.getXpForLevel(level) - experience; + return ExperienceTable.getXpForLevel(level) - experience; } @Override @@ -59,6 +43,21 @@ public abstract class Job implements Levelable { level++; } + @Override + public String toString() { + return String.format("Job: %s. Level: %d", name, level); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o != null && getClass() == o.getClass(); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } } diff --git a/src/test/java/MinerTest.java b/src/test/java/MinerTest.java index 448e5c2..d50cb61 100644 --- a/src/test/java/MinerTest.java +++ b/src/test/java/MinerTest.java @@ -34,6 +34,8 @@ public class MinerTest { assertEquals(10, job.getExperience()); } + + } -- 2.39.5 From 4e0d180903aae38758b2af3f189cbbc9490cbfca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20N=C3=A4slund?= Date: Mon, 27 Oct 2025 12:16:23 +0100 Subject: [PATCH 19/19] Merge magic --- pom.xml | 3 +-- src/main/java/Action/CastAction.java | 12 ++++++------ src/main/java/Action/LearnSpellAction.java | 2 +- src/main/java/Entity/Player.java | 4 +--- src/test/java/InterestingTests.java | 16 ++++------------ src/test/java/MagicSystemTest.java | 4 ++-- src/test/java/MinerTest.java | 5 +++++ 7 files changed, 20 insertions(+), 26 deletions(-) diff --git a/pom.xml b/pom.xml index 193b2a4..a427f23 100644 --- a/pom.xml +++ b/pom.xml @@ -20,10 +20,9 @@ 5.8.1 test - org.hamcrest - hamcrest + hamcrest-core 2.2 test diff --git a/src/main/java/Action/CastAction.java b/src/main/java/Action/CastAction.java index 1817090..e54d024 100644 --- a/src/main/java/Action/CastAction.java +++ b/src/main/java/Action/CastAction.java @@ -4,16 +4,16 @@ import Job.HasJob; import Job.Wizard; public class CastAction implements Action { - @Override - public void exectue(Actor actor) { - Wizard wizard = requireWizard(actor); - wizard.castSpell(actor); - } - private Wizard requireWizard(Actor actor) { if (actor instanceof HasJob hasJob && hasJob.getJob() instanceof Wizard wizard) { return wizard; } throw new IllegalStateException(actor + " cannot perform this action without being a Wizard!"); } + + @Override + public void execute(Actor player) { + var wizard = requireWizard(player); + wizard.castSpell(player); + } } diff --git a/src/main/java/Action/LearnSpellAction.java b/src/main/java/Action/LearnSpellAction.java index 74f1422..b3524a0 100644 --- a/src/main/java/Action/LearnSpellAction.java +++ b/src/main/java/Action/LearnSpellAction.java @@ -5,7 +5,7 @@ import Job.Wizard; public class LearnSpellAction implements Action { @Override - public void exectue(Actor actor) { + public void execute(Actor actor) { Wizard wizard = requireWizard(actor); wizard.learnSpell(actor); } diff --git a/src/main/java/Entity/Player.java b/src/main/java/Entity/Player.java index 5c6e034..a7cb7f3 100644 --- a/src/main/java/Entity/Player.java +++ b/src/main/java/Entity/Player.java @@ -20,8 +20,6 @@ public class Player extends Entity implements Movable, Actor, HasInventory, HasS protected Job job; protected Position position = new Position(0,0); protected Inventory inventory= new Inventory(); - protected Position position; - protected List items = new LinkedList<>(); protected List spells = new LinkedList<>(); public Player(String name, Job job) { super(name); @@ -42,7 +40,7 @@ public class Player extends Entity implements Movable, Actor, HasInventory, HasS @Override public boolean canMoveTo(Position position) { - return false; + return true; } public Job getJob() { return job; diff --git a/src/test/java/InterestingTests.java b/src/test/java/InterestingTests.java index bb5f449..365a456 100644 --- a/src/test/java/InterestingTests.java +++ b/src/test/java/InterestingTests.java @@ -1,23 +1,15 @@ -import Action.Actor; -import Combat.HasMana; import Combat.OffensiveDamageSpell; -import Combat.Spell; -import Entity.Entity; import Entity.Player; -import Inventory.HasSpellBook; import Job.Wizard; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; + import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.util.List; import java.util.Scanner; +import static org.hamcrest.Matchers.*; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.mockito.Mockito.*; + public class InterestingTests { @@ -67,7 +59,7 @@ public class InterestingTests { wizard.castSpell(player); - assertThat(player.getMana(),is(initialMana)); + assertThat(player.getMana(), is(initialMana)); assertThat(player.getHealth(),is(initialHealth)); } } \ No newline at end of file diff --git a/src/test/java/MagicSystemTest.java b/src/test/java/MagicSystemTest.java index c667fb2..852daa2 100644 --- a/src/test/java/MagicSystemTest.java +++ b/src/test/java/MagicSystemTest.java @@ -17,7 +17,7 @@ public class MagicSystemTest { CastAction action = new CastAction(); IllegalStateException exception = assertThrows( IllegalStateException.class, - () -> action.exectue(p) + () -> action.execute(p) ); assertTrue(exception.getMessage().contains("cannot perform this action without being a Wizard")); @@ -28,7 +28,7 @@ public class MagicSystemTest { LearnSpellAction action = new LearnSpellAction(); IllegalStateException exception = assertThrows( IllegalStateException.class, - () -> action.exectue(p) + () -> action.execute(p) ); assertTrue(exception.getMessage().contains("cannot perform this action without being a Wizard")); } diff --git a/src/test/java/MinerTest.java b/src/test/java/MinerTest.java index d50cb61..cb187fb 100644 --- a/src/test/java/MinerTest.java +++ b/src/test/java/MinerTest.java @@ -34,6 +34,11 @@ public class MinerTest { assertEquals(10, job.getExperience()); } + @Test + void dig_on_ice_require_ice_pick() { + + } + } -- 2.39.5