Job terrain #12

Merged
erns6604 merged 16 commits from job_terrain into main 2025-10-28 11:21:35 +01:00
17 changed files with 262 additions and 36 deletions

View File

@ -29,7 +29,7 @@
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId> <artifactId>mockito-core</artifactId>
<version>5.12.0</version> <version>5.20.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -2,12 +2,17 @@ package Action;
import Job.HasJob; import Job.HasJob;
import Job.Miner; import Job.Miner;
import Terrain.Biome;
public class DigAction implements Action { public class DigAction implements Action {
Biome biome;
public DigAction(Biome biome) {
this.biome = biome;
}
@Override @Override
public void execute(Actor actor) { public void execute(Actor actor) {
Miner miner = requireMiner(actor); Miner miner = requireMiner(actor);
miner.dig(actor); miner.dig(biome);
} }
private Miner requireMiner(Actor actor) { private Miner requireMiner(Actor actor) {

View File

@ -1,3 +0,0 @@
public enum Biomes {
GRASSLAND, MOUNTAIN, COAST, FOREST //Är inte fäst vid dessa
}

View File

@ -1,20 +1,23 @@
package Inventory; package Inventory;
import Item.Item;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
public class Inventory { public class Inventory {
List<String> items; List<Item> items;
public Inventory() { public Inventory() {
items = new ArrayList<>(); items = new ArrayList<>();
} }
List<String> getItems() { public List<Item> getItems() {
return items; return Collections.unmodifiableList(items);
} }
public void addItem(String item) { public void addItem(Item item) {
items.add(item); items.add(item);
} }

View File

@ -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;
}
}

View File

@ -0,0 +1,6 @@
package Item;
public interface Item {
String getId();
String getName();
}

View File

@ -1,17 +1,23 @@
package Job; package Job;
import Action.Actor;
import Inventory.HasInventory; import Inventory.HasInventory;
import Terrain.Biome;
public class Miner extends Job { public class Miner extends Job {
public Miner() { HasInventory actor;
public Miner(HasInventory actor) {
super("Miner"); super("Miner");
this.actor = actor;
} }
public void dig(Actor actor) { public HasInventory getActor() {
if (actor instanceof HasInventory a) { return actor;
a.getInventory().addItem("Stone");
} }
public void dig(Biome biome) {
var item = biome.getLootTable().roll();
actor.getInventory().addItem(item);
} }
} }

View File

@ -1,32 +1,33 @@
import Entity.Position; import Entity.Position;
import Terrain.Biome;
import java.util.*; import java.util.*;
public abstract class Monster extends Character{ public abstract class Monster extends Character{
private final List<Biomes> habitat = new ArrayList<>(); private final List<Biome> habitat = new ArrayList<>();
public Monster() { 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) { public Monster(Position position) {
super(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) { public Monster(double health, double level, double energy, Position position) {
super(health, level, energy, 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<Biomes> habitat) { public Monster(double health, double level, double energy, Position position, List<Biome> habitat) {
super(health, level, energy, position); super(health, level, energy, position);
this.habitat.addAll(habitat); this.habitat.addAll(habitat);
} }
//Är detta bra??? Med unmodifiableList dvs //Är detta bra??? Med unmodifiableList dvs
public List<Biomes> getHabitat() { public List<Biome> getHabitat() {
return Collections.unmodifiableList(habitat); return Collections.unmodifiableList(habitat);
} }
} }

View File

@ -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);
}
}

View File

@ -0,0 +1,4 @@
package Shared;
public record LootEntry<T>(T item, int weight) {
}

View File

@ -0,0 +1,46 @@
package Shared;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class LootTable<T> {
private RandomProvider random;
private final List<LootEntry<T>> entries = new ArrayList<>();
public LootTable() {
this.random = new DefaultRandomProvider();
}
public LootTable(List<LootEntry<T>> 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<T> entry : entries) {
current += entry.weight();
if (roll < current) {
return entry.item();
}
}
return null;
}
}

View File

@ -0,0 +1,5 @@
package Shared;
public interface RandomProvider {
int nextInt(int bound);
}

View File

@ -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<? extends Item> lootTable;
Biome(String name, LootTable<? extends Item> lootTable) {
this.name = name;
this.lootTable = lootTable;
}
public String getName() {
return name;
}
public LootTable<? extends Item> getLootTable() {
return lootTable;
}
}

View File

@ -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"))
));
}
}

View File

@ -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<String> defaultLootTable() {
LootTable<String> lootTable = new LootTable<>();
lootTable.addEntry("Stone", 90);
lootTable.addEntry("Iron", 10);
return lootTable;
}
@Test
void returns_an_item_was_added() {
LootTable<String> 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<String> loot = new LootTable<>(mockRandomProvider);
loot.addEntry("Stone", 90);
loot.addEntry("Iron", 10);
String result = loot.roll();
assertThat(result, equalTo("Iron"));
}
}

View File

@ -1,13 +1,19 @@
import Entity.Player;
import Job.Miner; import Job.Miner;
import Shared.RandomProvider;
import Terrain.Biome;
import org.junit.jupiter.api.Test; 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.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
public class MinerTest { public class MinerTest {
private Player defaultPlayer() {return new Player("John"); }
@Test @Test
void can_level_up() { void can_level_up() {
var job = new Miner(); var job = new Miner(defaultPlayer());
assertEquals(1, job.getLevel()); assertEquals(1, job.getLevel());
job.levelUp(); job.levelUp();
assertEquals(2, job.getLevel()); assertEquals(2, job.getLevel());
@ -15,32 +21,42 @@ public class MinerTest {
@Test @Test
void can_gain_xp() { void can_gain_xp() {
var job = new Miner(); var job = new Miner(defaultPlayer());
job.gainExperience(25); job.gainExperience(25);
assertEquals(25, job.getExperience()); assertEquals(25, job.getExperience());
} }
@Test @Test
void level_up_when_experience_cap_is_reached() { void level_up_when_experience_cap_is_reached() {
var job = new Miner(); var job = new Miner(defaultPlayer());
job.gainExperience(job.remainingXpUntilLevelUp()); job.gainExperience(job.remainingXpUntilLevelUp());
assertEquals(2, job.getLevel()); assertEquals(2, job.getLevel());
} }
@Test @Test
void additional_xp_carries_over_on_level_up() { void additional_xp_carries_over_on_level_up() {
var job = new Miner(); var job = new Miner(defaultPlayer());
job.gainExperience(job.remainingXpUntilLevelUp() + 10); job.gainExperience(job.remainingXpUntilLevelUp() + 10);
assertEquals(10, job.getExperience()); assertEquals(10, job.getExperience());
} }
@Test @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"))));
}
} }

View File

@ -4,10 +4,15 @@ import Combat.OffensiveDamageSpell;
import Entity.Position; import Entity.Position;
import Job.Miner; import Job.Miner;
import Entity.Player; import Entity.Player;
import Terrain.Biome;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import Job.Wizard; 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.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class PlayerTest { class PlayerTest {
private Player defaultPlayer() { private Player defaultPlayer() {
@ -45,17 +50,18 @@ class PlayerTest {
var p = defaultPlayer(); var p = defaultPlayer();
assertNull(p.getJob()); assertNull(p.getJob());
var job = new Miner(); var job = new Miner(p);
p.learnJob(job); p.learnJob(job);
assertEquals(new Miner(), p.getJob()); assertThat(p.getJob(), instanceOf(Miner.class));
} }
@Test @Test
void miner_can_dig() { void miner_can_dig() {
var p = new Player("John"); var p = new Player("John");
p.learnJob(new Miner()); p.learnJob(new Miner(p));
p.performAction(new DigAction()); var mockAction = mock(DigAction.class);
assertTrue(p.getInventory().containsItem("Stone")); p.performAction(mockAction);
verify(mockAction, times(1)).execute(any());
} }
@Test @Test