diff --git a/src/main/java/net/glowstone/entity/GlowAgeable.java b/src/main/java/net/glowstone/entity/GlowAgeable.java index 8ced13f548..52ba07875d 100644 --- a/src/main/java/net/glowstone/entity/GlowAgeable.java +++ b/src/main/java/net/glowstone/entity/GlowAgeable.java @@ -134,10 +134,11 @@ protected final void setScale(float scale) { @Override public boolean entityInteract(GlowPlayer player, InteractEntityMessage message) { - super.entityInteract(player, message); - if (message.getAction() == InteractEntityMessage.Action.INTERACT.ordinal()) { + if (!super.entityInteract(player, message) + && message.getAction() == InteractEntityMessage.Action.INTERACT.ordinal()) { ItemStack item = InventoryUtil .itemOrEmpty(player.getInventory().getItem(message.getHandSlot())); + int growthAmount = computeGrowthAmount(item.getType()); // Spawn eggs are used to spawn babies if (item.getType() == Material.MONSTER_EGG && item.hasItemMeta()) { @@ -147,16 +148,14 @@ public boolean entityInteract(GlowPlayer player, InteractEntityMessage message) if (player.getGameMode() == GameMode.SURVIVAL || player.getGameMode() == GameMode.ADVENTURE) { - // Consume the egg - if (item.getAmount() > 1) { - item.setAmount(item.getAmount() - 1); - } else { - player.getInventory() - .setItem(message.getHandSlot(), InventoryUtil.createEmptyStack()); - } + player.getInventory().consumeItem(message.getHand()); } return true; } + } else if (growthAmount > 0) { + grow(growthAmount); + player.getInventory().consumeItem(message.getHand()); + return true; } } return false; @@ -184,4 +183,24 @@ protected float getSoundPitch() { } return super.getSoundPitch(); } + + /** + * Grows an ageable creature. + * + * @param age The age to add to the ageable creature. + */ + public void grow(int age) { + setAge(this.age + age); + } + + /** + * Computes the growth amount using a specific material for the current ageable creature. + * Always returns 0 for an adult or if the material is not food for the creature. + * + * @param material The food used to compute the growth amount. + * @return The age gained using the given food. + */ + protected int computeGrowthAmount(Material material) { + return 0; + } } diff --git a/src/main/java/net/glowstone/entity/GlowAnimal.java b/src/main/java/net/glowstone/entity/GlowAnimal.java index e55ac597dc..cd15eba0a7 100644 --- a/src/main/java/net/glowstone/entity/GlowAnimal.java +++ b/src/main/java/net/glowstone/entity/GlowAnimal.java @@ -45,9 +45,8 @@ protected int getAmbientDelay() { @Override public boolean entityInteract(GlowPlayer player, InteractEntityMessage message) { - super.entityInteract(player, message); - - if (message.getAction() == InteractEntityMessage.Action.INTERACT.ordinal()) { + if (!super.entityInteract(player, message) + && message.getAction() == InteractEntityMessage.Action.INTERACT.ordinal()) { ItemStack item = InventoryUtil .itemOrEmpty(player.getInventory().getItem(message.getHandSlot())); @@ -60,7 +59,9 @@ public boolean entityInteract(GlowPlayer player, InteractEntityMessage message) && getBreedingFoods().contains(item.getType())) { // TODO set love mode if possible and spawn particles // TODO heal - player.getInventory().consumeItemInMainHand(); + // TODO only consume the item if the animal is healed or something else + player.getInventory().consumeItem(message.getHand()); + return true; } } @@ -74,4 +75,13 @@ && getBreedingFoods().contains(item.getType())) { public Set getBreedingFoods() { return DEFAULT_BREEDING_FOODS; } + + @Override + protected int computeGrowthAmount(Material material) { + if (!isAdult() && getBreedingFoods().contains(material)) { + return Math.abs(getAge() / 10); + } + + return 0; + } } diff --git a/src/main/java/net/glowstone/entity/passive/GlowAbstractHorse.java b/src/main/java/net/glowstone/entity/passive/GlowAbstractHorse.java index d0d7311ad9..c90d4a3417 100644 --- a/src/main/java/net/glowstone/entity/passive/GlowAbstractHorse.java +++ b/src/main/java/net/glowstone/entity/passive/GlowAbstractHorse.java @@ -1,27 +1,39 @@ package net.glowstone.entity.passive; import com.flowpowered.network.Message; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; import java.util.List; +import java.util.Map; import java.util.Set; import lombok.Getter; import lombok.Setter; import net.glowstone.entity.meta.MetadataIndex; import net.glowstone.entity.meta.MetadataMap; import net.glowstone.net.message.play.entity.EntityMetadataMessage; +import net.glowstone.util.InventoryUtil; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.ChestedHorse; import org.bukkit.entity.EntityType; import org.bukkit.entity.Horse; -import org.bukkit.inventory.HorseInventory; public abstract class GlowAbstractHorse extends GlowTameable implements AbstractHorse { private static final Set BREEDING_FOODS = Sets.immutableEnumSet(Material.GOLDEN_APPLE, Material.GOLDEN_CARROT); + private static final Map GROWING_FOODS = ImmutableMap + .builder() + .put(Material.SUGAR, 600) + .put(Material.WHEAT, 400) + .put(Material.APPLE, 1200) + .put(Material.GOLDEN_CARROT, 1200) + .put(Material.GOLDEN_APPLE, 4800) + .put(Material.HAY_BLOCK, 3600) + .build(); + @Getter @Setter private int domestication; @@ -67,7 +79,7 @@ private int getHorseFlags() { } if (this instanceof GlowHorse) { GlowHorse horse = (GlowHorse) this; - if (getInventory() != null && ((HorseInventory) getInventory()).getSaddle() != null) { + if (getInventory() != null && !InventoryUtil.isEmpty(getInventory().getSaddle())) { value |= 0x04; } if (horse.hasReproduced()) { @@ -90,4 +102,18 @@ private int getHorseFlags() { public Set getBreedingFoods() { return BREEDING_FOODS; } + + @Override + protected int computeGrowthAmount(Material material) { + // We need to be a baby and only tamed horses can be fed with hay block + if (!isAdult() && !(Material.HAY_BLOCK == material && !isTamed())) { + Integer mapResult = GROWING_FOODS.get(material); + + if (mapResult != null) { + return Math.min(mapResult, Math.abs(getAge())); + } + } + + return 0; + } } diff --git a/src/main/java/net/glowstone/entity/passive/GlowLlama.java b/src/main/java/net/glowstone/entity/passive/GlowLlama.java index 89b95c6255..102ca2535e 100644 --- a/src/main/java/net/glowstone/entity/passive/GlowLlama.java +++ b/src/main/java/net/glowstone/entity/passive/GlowLlama.java @@ -1,6 +1,9 @@ package net.glowstone.entity.passive; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; + +import java.util.Map; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import net.glowstone.entity.meta.MetadataIndex; @@ -11,10 +14,20 @@ import org.bukkit.entity.EntityType; import org.bukkit.entity.Llama; +/** + * Represents a llama. + * The data comes from https://minecraft.gamepedia.com/Llama + */ public class GlowLlama extends GlowChestedHorse implements Llama { private static final Set BREEDING_FOODS = Sets.immutableEnumSet(Material.HAY_BLOCK); + private static final Map GROWING_FOODS = ImmutableMap + .builder() + .put(Material.WHEAT, 200) + .put(Material.HAY_BLOCK, 1800) + .build(); + /** * Creates a llama entity. * @@ -81,4 +94,18 @@ protected GlowLlamaInventory createNewInventory() { public Set getBreedingFoods() { return BREEDING_FOODS; } + + @Override + protected int computeGrowthAmount(Material material) { + if (!isAdult()) { + Integer mapResult = GROWING_FOODS.get(material); + + if (mapResult != null) { + return Math.min(mapResult, Math.abs(getAge())); + } + } + + return 0; + } + } diff --git a/src/main/java/net/glowstone/entity/passive/GlowUndeadHorse.java b/src/main/java/net/glowstone/entity/passive/GlowUndeadHorse.java index f272dc4674..f7eece7f71 100644 --- a/src/main/java/net/glowstone/entity/passive/GlowUndeadHorse.java +++ b/src/main/java/net/glowstone/entity/passive/GlowUndeadHorse.java @@ -1,12 +1,19 @@ package net.glowstone.entity.passive; +import com.google.common.collect.Sets; +import java.util.EnumSet; +import java.util.Set; import net.glowstone.inventory.GlowHorseInventory; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.EntityType; public class GlowUndeadHorse extends GlowAbstractHorse implements AbstractHorse { + private static final Set BREEDING_FOODS = + Sets.immutableEnumSet(EnumSet.noneOf(Material.class)); + public GlowUndeadHorse(Location location, EntityType type, double maxHealth) { super(location, type, maxHealth); } @@ -25,4 +32,9 @@ public GlowHorseInventory getInventory() { public boolean isUndead() { return true; } + + @Override + public Set getBreedingFoods() { + return BREEDING_FOODS; + } } diff --git a/src/test/java/net/glowstone/entity/GlowAgeableTest.java b/src/test/java/net/glowstone/entity/GlowAgeableTest.java index acb6179a22..afb7ed68ae 100644 --- a/src/test/java/net/glowstone/entity/GlowAgeableTest.java +++ b/src/test/java/net/glowstone/entity/GlowAgeableTest.java @@ -7,11 +7,15 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; import java.util.function.Function; +import net.glowstone.net.message.play.player.InteractEntityMessage; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.entity.Ageable; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.inventory.ItemStack; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; @@ -153,4 +157,49 @@ public void testCreateBaby() { public void testGetSoundPitch() { // TODO } + + @Test + public void testComputeGrowthAmount() { + entity.setBaby(); + assertEquals(0, entity.computeGrowthAmount(null)); + assertEquals(0, entity.computeGrowthAmount(Material.WHEAT)); + assertEquals(0, entity.computeGrowthAmount(Material.HAY_BLOCK)); + assertEquals(0, entity.computeGrowthAmount(Material.CARROT_ITEM)); + } + + @Test + public void testComputeGrowthAmountAdult() { + entity.setAge(0); + assertEquals(0, entity.computeGrowthAmount(null)); + assertEquals(0, entity.computeGrowthAmount(Material.WHEAT)); + assertEquals(0, entity.computeGrowthAmount(Material.HAY_BLOCK)); + assertEquals(0, entity.computeGrowthAmount(Material.CARROT_ITEM)); + } + + @Test + public void testEntityInteractGrowsBaby() { + entity.setBaby(); + T mockedEntity = spy(entity); + inventory.setItemInMainHand(new ItemStack(Material.RAW_FISH, 60)); + InteractEntityMessage message = new InteractEntityMessage(0, InteractEntityMessage.Action.INTERACT.ordinal(), 0); + + Mockito.when(mockedEntity.computeGrowthAmount(any())).thenReturn(100); + + mockedEntity.entityInteract(player, message); + + assertEquals(-23900, mockedEntity.getAge()); + assertEquals(59, inventory.getItemInMainHand().getAmount()); + } + + @Test + public void testEntityInteractDoesNotGrowBaby() { + entity.setBaby(); + inventory.setItemInMainHand(new ItemStack(Material.BEDROCK, 60)); + InteractEntityMessage message = new InteractEntityMessage(0, InteractEntityMessage.Action.INTERACT.ordinal(), 0); + + entity.entityInteract(player, message); + + assertEquals(-24000, entity.getAge()); + assertEquals(60, inventory.getItemInMainHand().getAmount()); + } } diff --git a/src/test/java/net/glowstone/entity/GlowAnimalTest.java b/src/test/java/net/glowstone/entity/GlowAnimalTest.java index 77ce905f89..b9f122c819 100644 --- a/src/test/java/net/glowstone/entity/GlowAnimalTest.java +++ b/src/test/java/net/glowstone/entity/GlowAnimalTest.java @@ -7,6 +7,7 @@ import org.bukkit.Location; import org.bukkit.Material; import org.junit.Test; +import org.junit.jupiter.api.Assertions; public abstract class GlowAnimalTest extends GlowAgeableTest { protected GlowAnimalTest( @@ -23,4 +24,27 @@ public void testGetBreedingFoods() { public void testGetBreedingFoodsReturnsImmutableSet() { entity.getBreedingFoods().add(Material.SANDSTONE); } + + @Test + @Override + public void testComputeGrowthAmount() { + entity.setAge(-21000); + Assertions.assertEquals(0, entity.computeGrowthAmount(null)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.SAND)); + + for (Material food : entity.getBreedingFoods()) { + Assertions.assertEquals(2100, entity.computeGrowthAmount(food), food.name()); + } + } + + @Test + public void testComputeGrowthAmountAdult() { + entity.setAge(0); + Assertions.assertEquals(0, entity.computeGrowthAmount(null)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.SAND)); + + for (Material food : entity.getBreedingFoods()) { + Assertions.assertEquals(0, entity.computeGrowthAmount(food), food.name()); + } + } } diff --git a/src/test/java/net/glowstone/entity/GlowEntityTest.java b/src/test/java/net/glowstone/entity/GlowEntityTest.java index 3403ad640e..271f2f4d89 100644 --- a/src/test/java/net/glowstone/entity/GlowEntityTest.java +++ b/src/test/java/net/glowstone/entity/GlowEntityTest.java @@ -17,9 +17,11 @@ import net.glowstone.ServerProvider; import net.glowstone.block.GlowBlock; import net.glowstone.chunk.GlowChunk; +import net.glowstone.inventory.GlowPlayerInventory; import net.glowstone.scoreboard.GlowScoreboard; import net.glowstone.scoreboard.GlowScoreboardManager; import org.bukkit.Difficulty; +import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; @@ -66,6 +68,8 @@ public abstract class GlowEntityTest { protected EventFactory eventFactory; @Mock private PluginManager pluginManager; + @Mock + protected GlowPlayer player; // Real objects protected Location location; @@ -76,6 +80,7 @@ public abstract class GlowEntityTest { protected final Function entityCreator; protected T entity; private EventFactory oldEventFactory; + protected GlowPlayerInventory inventory; protected GlowEntityTest(Function entityCreator) { @@ -130,6 +135,9 @@ public void setUp() throws Exception { when(eventFactory.callEvent(any(Event.class))).thenAnswer(RETURN_FIRST_ARG); when(eventFactory.onEntityDamage(any(EntityDamageEvent.class))).thenAnswer( RETURN_FIRST_ARG); + inventory = new GlowPlayerInventory(player); + Mockito.when(player.getInventory()).thenReturn(inventory); + Mockito.when(player.getGameMode()).thenReturn(GameMode.SURVIVAL); } @After @@ -145,6 +153,7 @@ public void tearDown() { block = null; log = null; entity = null; + player = null; } @Test diff --git a/src/test/java/net/glowstone/entity/passive/GlowAbstractHorseTest.java b/src/test/java/net/glowstone/entity/passive/GlowAbstractHorseTest.java index 6df2b4378b..3ef0373671 100644 --- a/src/test/java/net/glowstone/entity/passive/GlowAbstractHorseTest.java +++ b/src/test/java/net/glowstone/entity/passive/GlowAbstractHorseTest.java @@ -8,6 +8,7 @@ import org.bukkit.Location; import org.bukkit.Material; import org.junit.Test; +import org.junit.jupiter.api.Assertions; public abstract class GlowAbstractHorseTest extends GlowAnimalTest { protected GlowAbstractHorseTest( @@ -21,4 +22,66 @@ public void testGetBreedingFoods() { assertEquals(EnumSet.of(Material.GOLDEN_APPLE, Material.GOLDEN_CARROT), entity.getBreedingFoods()); } + + @Test + @Override + public void testComputeGrowthAmount() { + entity.setBaby(); + entity.setTamed(true); + Assertions.assertEquals(0, entity.computeGrowthAmount(null)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.SAND)); + + Assertions.assertEquals(600, entity.computeGrowthAmount(Material.SUGAR)); + Assertions.assertEquals(400, entity.computeGrowthAmount(Material.WHEAT)); + Assertions.assertEquals(1200, entity.computeGrowthAmount(Material.APPLE)); + Assertions.assertEquals(1200, entity.computeGrowthAmount(Material.GOLDEN_CARROT)); + Assertions.assertEquals(4800, entity.computeGrowthAmount(Material.GOLDEN_APPLE)); + Assertions.assertEquals(3600, entity.computeGrowthAmount(Material.HAY_BLOCK)); + } + + @Test + public void testComputeGrowthAmountUntamed() { + entity.setBaby(); + entity.setTamed(false); + Assertions.assertEquals(0, entity.computeGrowthAmount(null)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.SAND)); + + Assertions.assertEquals(600, entity.computeGrowthAmount(Material.SUGAR)); + Assertions.assertEquals(400, entity.computeGrowthAmount(Material.WHEAT)); + Assertions.assertEquals(1200, entity.computeGrowthAmount(Material.APPLE)); + Assertions.assertEquals(1200, entity.computeGrowthAmount(Material.GOLDEN_CARROT)); + Assertions.assertEquals(4800, entity.computeGrowthAmount(Material.GOLDEN_APPLE)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.HAY_BLOCK)); + } + + + @Test + @Override + public void testComputeGrowthAmountAdult() { + entity.setAge(0); + Assertions.assertEquals(0, entity.computeGrowthAmount(null)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.SAND)); + + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.SUGAR)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.WHEAT)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.APPLE)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.GOLDEN_CARROT)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.GOLDEN_APPLE)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.HAY_BLOCK)); + } + + @Test + public void testComputeGrowthAmountAlmostAdult() { + entity.setAge(-1); + entity.setTamed(true); + Assertions.assertEquals(0, entity.computeGrowthAmount(null)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.SAND)); + + Assertions.assertEquals(1, entity.computeGrowthAmount(Material.SUGAR)); + Assertions.assertEquals(1, entity.computeGrowthAmount(Material.WHEAT)); + Assertions.assertEquals(1, entity.computeGrowthAmount(Material.APPLE)); + Assertions.assertEquals(1, entity.computeGrowthAmount(Material.GOLDEN_CARROT)); + Assertions.assertEquals(1, entity.computeGrowthAmount(Material.GOLDEN_APPLE)); + Assertions.assertEquals(1, entity.computeGrowthAmount(Material.HAY_BLOCK)); + } } diff --git a/src/test/java/net/glowstone/entity/passive/GlowLlamaTest.java b/src/test/java/net/glowstone/entity/passive/GlowLlamaTest.java index 3460397475..94180a49cc 100644 --- a/src/test/java/net/glowstone/entity/passive/GlowLlamaTest.java +++ b/src/test/java/net/glowstone/entity/passive/GlowLlamaTest.java @@ -5,6 +5,9 @@ import java.util.EnumSet; import org.bukkit.Material; import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.bukkit.Material; +import org.junit.Test; public class GlowLlamaTest extends GlowChestedHorseTest { public GlowLlamaTest() { @@ -16,4 +19,49 @@ public GlowLlamaTest() { public void testGetBreedingFoods() { assertEquals(EnumSet.of(Material.HAY_BLOCK), entity.getBreedingFoods()); } + + @Test + @Override + public void testComputeGrowthAmount() { + entity.setBaby(); + entity.setTamed(true); + Assertions.assertEquals(0, entity.computeGrowthAmount(null)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.SAND)); + + Assertions.assertEquals(200, entity.computeGrowthAmount(Material.WHEAT)); + Assertions.assertEquals(1800, entity.computeGrowthAmount(Material.HAY_BLOCK)); + } + + @Test + public void testComputeGrowthAmountUntamed() { + entity.setBaby(); + entity.setTamed(false); + Assertions.assertEquals(0, entity.computeGrowthAmount(null)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.SAND)); + + Assertions.assertEquals(200, entity.computeGrowthAmount(Material.WHEAT)); + Assertions.assertEquals(1800, entity.computeGrowthAmount(Material.HAY_BLOCK)); + } + + @Test + @Override + public void testComputeGrowthAmountAdult() { + entity.setAge(0); + Assertions.assertEquals(0, entity.computeGrowthAmount(null)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.SAND)); + + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.WHEAT)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.HAY_BLOCK)); + } + + @Test + public void testComputeGrowthAmountAlmostAdult() { + entity.setAge(-1); + entity.setTamed(true); + Assertions.assertEquals(0, entity.computeGrowthAmount(null)); + Assertions.assertEquals(0, entity.computeGrowthAmount(Material.SAND)); + + Assertions.assertEquals(1, entity.computeGrowthAmount(Material.WHEAT)); + Assertions.assertEquals(1, entity.computeGrowthAmount(Material.HAY_BLOCK)); + } } diff --git a/src/test/java/net/glowstone/entity/passive/GlowUndeadHorseTest.java b/src/test/java/net/glowstone/entity/passive/GlowUndeadHorseTest.java index 4d6da56495..21a2f3265a 100644 --- a/src/test/java/net/glowstone/entity/passive/GlowUndeadHorseTest.java +++ b/src/test/java/net/glowstone/entity/passive/GlowUndeadHorseTest.java @@ -3,8 +3,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import java.util.EnumSet; import java.util.function.Function; import org.bukkit.Location; +import org.bukkit.Material; +import org.junit.Assert; import org.junit.Test; public abstract class GlowUndeadHorseTest extends GlowAbstractHorseTest { @@ -40,4 +43,10 @@ public void testSetBreedTrueAdult() { assertAdult(entity); assertFalse(entity.canBreed()); } + + @Test + @Override + public void testGetBreedingFoods() { + Assert.assertEquals(EnumSet.noneOf(Material.class), entity.getBreedingFoods()); + } }