diff --git a/pom.xml b/pom.xml index a427f23..4954a6f 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ org.mockito mockito-core - 5.12.0 + 5.20.0 test diff --git a/src/main/java/Action/DigAction.java b/src/main/java/Action/DigAction.java index 4d652e2..d657191 100644 --- a/src/main/java/Action/DigAction.java +++ b/src/main/java/Action/DigAction.java @@ -2,12 +2,17 @@ package Action; import Job.HasJob; import Job.Miner; +import Terrain.Biome; public class DigAction implements Action { + Biome biome; + public DigAction(Biome biome) { + this.biome = biome; + } @Override public void execute(Actor actor) { Miner miner = requireMiner(actor); - miner.dig(actor); + miner.dig(biome); } private Miner requireMiner(Actor actor) { diff --git a/src/main/java/Biomes.java b/src/main/java/Biomes.java deleted file mode 100644 index 3c61251..0000000 --- a/src/main/java/Biomes.java +++ /dev/null @@ -1,3 +0,0 @@ -public enum Biomes { - GRASSLAND, MOUNTAIN, COAST, FOREST //Är inte fäst vid dessa -} diff --git a/src/main/java/Inventory/Inventory.java b/src/main/java/Inventory/Inventory.java index 786cfda..6d65070 100644 --- a/src/main/java/Inventory/Inventory.java +++ b/src/main/java/Inventory/Inventory.java @@ -1,20 +1,23 @@ package Inventory; +import Item.Item; + import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class Inventory { - List items; + List items; public Inventory() { items = new ArrayList<>(); } - List getItems() { - return items; + public List getItems() { + return Collections.unmodifiableList(items); } - public void addItem(String item) { + public void addItem(Item item) { items.add(item); } diff --git a/src/main/java/Item/BasicItem.java b/src/main/java/Item/BasicItem.java new file mode 100644 index 0000000..863776a --- /dev/null +++ b/src/main/java/Item/BasicItem.java @@ -0,0 +1,13 @@ +package Item; + +public record BasicItem(String id, String name) implements Item { + @Override + public String getId() { + return id; + } + + @Override + public String getName() { + return name; + } +} \ No newline at end of file diff --git a/src/main/java/Item/Item.java b/src/main/java/Item/Item.java new file mode 100644 index 0000000..6d88ff4 --- /dev/null +++ b/src/main/java/Item/Item.java @@ -0,0 +1,6 @@ +package Item; + +public interface Item { + String getId(); + String getName(); +} \ No newline at end of file diff --git a/src/main/java/Job/Miner.java b/src/main/java/Job/Miner.java index 79c56ec..f731381 100644 --- a/src/main/java/Job/Miner.java +++ b/src/main/java/Job/Miner.java @@ -1,17 +1,23 @@ package Job; -import Action.Actor; import Inventory.HasInventory; +import Terrain.Biome; public class Miner extends Job { - public Miner() { + HasInventory actor; + + public Miner(HasInventory actor) { super("Miner"); + this.actor = actor; } - public void dig(Actor actor) { - if (actor instanceof HasInventory a) { - a.getInventory().addItem("Stone"); - } + public HasInventory getActor() { + return actor; + } + + public void dig(Biome biome) { + var item = biome.getLootTable().roll(); + actor.getInventory().addItem(item); } } diff --git a/src/main/java/Monster.java b/src/main/java/Monster.java index a9b3efc..9e1d6d7 100644 --- a/src/main/java/Monster.java +++ b/src/main/java/Monster.java @@ -1,32 +1,33 @@ import Entity.Position; +import Terrain.Biome; import java.util.*; public abstract class Monster extends Character{ - private final List habitat = new ArrayList<>(); + private final List habitat = new ArrayList<>(); public Monster() { - habitat.addAll(Arrays.asList(Biomes.GRASSLAND, Biomes.MOUNTAIN, Biomes.COAST, Biomes.FOREST)); + habitat.addAll(Arrays.asList(Biome.GRASSLAND, Biome.MOUNTAIN, Biome.COAST, Biome.FOREST)); } public Monster(Position position) { super(position); - habitat.addAll(Arrays.asList(Biomes.GRASSLAND, Biomes.MOUNTAIN, Biomes.COAST, Biomes.FOREST)); + habitat.addAll(Arrays.asList(Biome.GRASSLAND, Biome.MOUNTAIN, Biome.COAST, Biome.FOREST)); } 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)); + habitat.addAll(Arrays.asList(Biome.GRASSLAND, Biome.MOUNTAIN, Biome.COAST, Biome.FOREST)); } - public Monster(double health, double level, double energy, Position position, List habitat) { + 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() { + public List getHabitat() { return Collections.unmodifiableList(habitat); } } diff --git a/src/main/java/Shared/DefaultRandomProvider.java b/src/main/java/Shared/DefaultRandomProvider.java new file mode 100644 index 0000000..2cc40fe --- /dev/null +++ b/src/main/java/Shared/DefaultRandomProvider.java @@ -0,0 +1,11 @@ +package Shared; + +import java.util.Random; + +public class DefaultRandomProvider implements RandomProvider { + private final Random random = new Random(); + @Override + public int nextInt(int bound) { + return random.nextInt(bound); + } +} diff --git a/src/main/java/Shared/LootEntry.java b/src/main/java/Shared/LootEntry.java new file mode 100644 index 0000000..1586bb4 --- /dev/null +++ b/src/main/java/Shared/LootEntry.java @@ -0,0 +1,4 @@ +package Shared; + +public record LootEntry(T item, int weight) { +} diff --git a/src/main/java/Shared/LootTable.java b/src/main/java/Shared/LootTable.java new file mode 100644 index 0000000..3b02763 --- /dev/null +++ b/src/main/java/Shared/LootTable.java @@ -0,0 +1,46 @@ +package Shared; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class LootTable { + private RandomProvider random; + private final List> entries = new ArrayList<>(); + + + public LootTable() { + this.random = new DefaultRandomProvider(); + } + + public LootTable(List> entries) { + this.random = new DefaultRandomProvider(); + this.entries.addAll(entries); + } + + public void setRandomProvider(RandomProvider random) { + this.random = random; + } + + public LootTable(RandomProvider random) { + this.random = random; + } + + public void addEntry(T entry, int weight) { + entries.add(new LootEntry<>(entry,weight)); + } + + public T roll() { + int totalWeight = entries.stream().mapToInt(LootEntry::weight).sum(); + int roll = random.nextInt(totalWeight); + int current = 0; + for(LootEntry entry : entries) { + current += entry.weight(); + if (roll < current) { + return entry.item(); + } + } + return null; + } + +} diff --git a/src/main/java/Shared/RandomProvider.java b/src/main/java/Shared/RandomProvider.java new file mode 100644 index 0000000..bbaf10d --- /dev/null +++ b/src/main/java/Shared/RandomProvider.java @@ -0,0 +1,5 @@ +package Shared; + +public interface RandomProvider { + int nextInt(int bound); +} diff --git a/src/main/java/Terrain/Biome.java b/src/main/java/Terrain/Biome.java new file mode 100644 index 0000000..185871d --- /dev/null +++ b/src/main/java/Terrain/Biome.java @@ -0,0 +1,51 @@ +package Terrain; + +import Item.BasicItem; +import Item.Item; +import Shared.LootEntry; +import Shared.LootTable; + +import java.util.ArrayList; +import java.util.List; + +public enum Biome { + GRASSLAND("Grassland", new LootTable<>(new ArrayList<>(List.of( + new LootEntry<>(new BasicItem("mud", "Mud"), 10), + new LootEntry<>(new BasicItem("earth_root", "Earth Root"), 50), + new LootEntry<>(new BasicItem("crystal_core", "Crystal core"), 180) + )) { + })), + MOUNTAIN("Mountain", new LootTable<>(new ArrayList<>(List.of( + new LootEntry<>(new BasicItem("rock", "Rock"), 10), + new LootEntry<>(new BasicItem("iron", "Iron"), 50), + new LootEntry<>(new BasicItem("diamond", "Diamond"), 180) + )) { + })), + COAST("Coast", new LootTable<>(new ArrayList<>(List.of( + new LootEntry<>(new BasicItem("sand", "Sand"), 10), + new LootEntry<>(new BasicItem("shell", "Shell"), 50), + new LootEntry<>(new BasicItem("sandfish", "Sandfish"), 180) + )) { + })), + FOREST("Forest", new LootTable<>(new ArrayList<>(List.of( + new LootEntry<>(new BasicItem("moss", "Moss"), 10), + new LootEntry<>(new BasicItem("fairy_rock", "Fairy rock"), 50), + new LootEntry<>(new BasicItem("unicorn_horn", "Unicorn horn"), 230) + )) { + })); + + private final String name; + private final LootTable lootTable; + Biome(String name, LootTable lootTable) { + this.name = name; + this.lootTable = lootTable; + } + + public String getName() { + return name; + } + + public LootTable getLootTable() { + return lootTable; + } +} diff --git a/src/test/java/InventoryTest.java b/src/test/java/InventoryTest.java new file mode 100644 index 0000000..1d143db --- /dev/null +++ b/src/test/java/InventoryTest.java @@ -0,0 +1,17 @@ +import Inventory.Inventory; +import Item.BasicItem; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +public class InventoryTest { + @Test + void can_add_item_to_inventory() { + var inventory = new Inventory(); + inventory.addItem(new BasicItem("iron_sword", "Iron Sword")); + assertThat(inventory.getItems(), hasItem( + hasProperty("id", equalTo("iron_sword")) + )); + } +} diff --git a/src/test/java/LootTableTest.java b/src/test/java/LootTableTest.java new file mode 100644 index 0000000..f91bcab --- /dev/null +++ b/src/test/java/LootTableTest.java @@ -0,0 +1,39 @@ +import Shared.LootTable; +import Shared.RandomProvider; +import org.junit.jupiter.api.Test; + +import java.util.Random; + +import static org.hamcrest.CoreMatchers.anyOf; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class LootTableTest { + LootTable defaultLootTable() { + LootTable lootTable = new LootTable<>(); + lootTable.addEntry("Stone", 90); + lootTable.addEntry("Iron", 10); + return lootTable; + } + + @Test + void returns_an_item_was_added() { + LootTable loot = defaultLootTable(); + String result = loot.roll(); + assertThat(result, anyOf(equalTo("Stone"), equalTo("Iron"))); + } + + @Test + void respects_weight_when_rolling() { + var mockRandomProvider = mock(RandomProvider.class); + when(mockRandomProvider.nextInt(anyInt())).thenReturn(94); + LootTable loot = new LootTable<>(mockRandomProvider); + loot.addEntry("Stone", 90); + loot.addEntry("Iron", 10); + String result = loot.roll(); + assertThat(result, equalTo("Iron")); + } +} diff --git a/src/test/java/MinerTest.java b/src/test/java/MinerTest.java index cb187fb..177d65a 100644 --- a/src/test/java/MinerTest.java +++ b/src/test/java/MinerTest.java @@ -1,13 +1,19 @@ +import Entity.Player; import Job.Miner; +import Shared.RandomProvider; +import Terrain.Biome; import org.junit.jupiter.api.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; public class MinerTest { - + private Player defaultPlayer() {return new Player("John"); } @Test void can_level_up() { - var job = new Miner(); + var job = new Miner(defaultPlayer()); assertEquals(1, job.getLevel()); job.levelUp(); assertEquals(2, job.getLevel()); @@ -15,32 +21,42 @@ public class MinerTest { @Test void can_gain_xp() { - var job = new Miner(); + var job = new Miner(defaultPlayer()); job.gainExperience(25); assertEquals(25, job.getExperience()); } @Test void level_up_when_experience_cap_is_reached() { - var job = new Miner(); + var job = new Miner(defaultPlayer()); job.gainExperience(job.remainingXpUntilLevelUp()); assertEquals(2, job.getLevel()); } @Test void additional_xp_carries_over_on_level_up() { - var job = new Miner(); + var job = new Miner(defaultPlayer()); job.gainExperience(job.remainingXpUntilLevelUp() + 10); assertEquals(10, job.getExperience()); } - @Test - void dig_on_ice_require_ice_pick() { - + void dig_in_coast_use_coast_loot() { + RandomProvider random = bound -> 9; + var job = new Miner(defaultPlayer()); + Biome.COAST.getLootTable().setRandomProvider(random); + job.dig(Biome.COAST); + assertThat(job.getActor().getInventory().getItems(), hasItem( + hasProperty("id", equalTo("sand")))); } - - + @Test + void dig_in_mountain_use_mountain_loot() { + RandomProvider random = bound -> 15; + var job = new Miner(defaultPlayer()); + Biome.MOUNTAIN.getLootTable().setRandomProvider(random); + job.dig(Biome.MOUNTAIN); + assertThat(job.getActor().getInventory().getItems(), hasItem( hasProperty("id", equalTo("iron")))); + } } diff --git a/src/test/java/PlayerTest.java b/src/test/java/PlayerTest.java index 970f0f9..593629f 100644 --- a/src/test/java/PlayerTest.java +++ b/src/test/java/PlayerTest.java @@ -4,10 +4,15 @@ import Combat.OffensiveDamageSpell; import Entity.Position; import Job.Miner; import Entity.Player; +import Terrain.Biome; import org.junit.jupiter.api.Test; import Job.Wizard; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.instanceOf; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; class PlayerTest { private Player defaultPlayer() { @@ -45,17 +50,18 @@ class PlayerTest { var p = defaultPlayer(); assertNull(p.getJob()); - var job = new Miner(); + var job = new Miner(p); p.learnJob(job); - assertEquals(new Miner(), p.getJob()); + assertThat(p.getJob(), instanceOf(Miner.class)); } @Test void miner_can_dig() { var p = new Player("John"); - p.learnJob(new Miner()); - p.performAction(new DigAction()); - assertTrue(p.getInventory().containsItem("Stone")); + p.learnJob(new Miner(p)); + var mockAction = mock(DigAction.class); + p.performAction(mockAction); + verify(mockAction, times(1)).execute(any()); } @Test