diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreedingListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreedingListener.java index 8f49dc2b6..145519092 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreedingListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BreedingListener.java @@ -1,10 +1,14 @@ package world.bentobox.bentobox.listeners.flags.protection; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import com.google.common.collect.ImmutableMap; import org.bukkit.Material; import org.bukkit.entity.Animals; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.player.PlayerInteractAtEntityEvent; @@ -25,23 +29,42 @@ public class BreedingListener extends FlagListener { /** * A list of items that cause breeding if a player has them in their hand and they click an animal * This list may need to be extended with future versions of Minecraft. + * See this Minecraft Wiki page for reference: https://minecraft.gamepedia.com/Breeding#Breeding_foods. */ - private static final List BREEDING_ITEMS = Arrays.asList( - Material.EGG, - Material.WHEAT, - Material.CARROT, - Material.WHEAT_SEEDS, - Material.SEAGRASS); + private static final ImmutableMap> BREEDING_ITEMS = new ImmutableMap.Builder>() + .put(EntityType.HORSE, Arrays.asList(Material.GOLDEN_APPLE, Material.GOLDEN_CARROT)) + .put(EntityType.DONKEY, Arrays.asList(Material.GOLDEN_APPLE, Material.GOLDEN_CARROT)) + .put(EntityType.COW, Collections.singletonList(Material.WHEAT)) + .put(EntityType.MUSHROOM_COW, Collections.singletonList(Material.WHEAT)) + .put(EntityType.SHEEP, Collections.singletonList(Material.WHEAT)) + .put(EntityType.PIG, Arrays.asList(Material.CARROT, Material.POTATO, Material.BEETROOT)) + .put(EntityType.CHICKEN, Arrays.asList(Material.WHEAT_SEEDS, Material.PUMPKIN_SEEDS, Material.MELON_SEEDS, Material.BEETROOT_SEEDS)) + .put(EntityType.WOLF, Arrays.asList(Material.PORKCHOP, Material.COOKED_PORKCHOP, Material.BEEF, Material.COOKED_BEEF, + Material.CHICKEN, Material.COOKED_CHICKEN, Material.RABBIT, Material.COOKED_RABBIT, + Material.MUTTON, Material.COOKED_MUTTON, Material.ROTTEN_FLESH)) + .put(EntityType.CAT, Arrays.asList(Material.COD, Material.SALMON)) + .put(EntityType.OCELOT, Arrays.asList(Material.COD, Material.SALMON)) + .put(EntityType.RABBIT, Arrays.asList(Material.DANDELION, Material.CARROT, Material.GOLDEN_CARROT)) + .put(EntityType.LLAMA, Collections.singletonList(Material.HAY_BLOCK)) + .put(EntityType.TRADER_LLAMA, Collections.singletonList(Material.HAY_BLOCK)) + .put(EntityType.TURTLE, Collections.singletonList(Material.SEAGRASS)) + .put(EntityType.PANDA, Collections.singletonList(Material.BAMBOO)) + .put(EntityType.FOX, Collections.singletonList(Material.SWEET_BERRIES)) + .build(); + + //TODO: add bees when switching to 1.15.x only @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled=true) public void onPlayerInteract(final PlayerInteractAtEntityEvent e) { - if (e.getRightClicked() instanceof Animals) { - ItemStack inHand = e.getPlayer().getInventory().getItemInMainHand(); + Player p = e.getPlayer(); + if (e.getRightClicked() instanceof Animals && BREEDING_ITEMS.containsKey(e.getRightClicked().getType())) { + Animals animal = (Animals) e.getRightClicked(); + ItemStack inHand = p.getInventory().getItemInMainHand(); if (e.getHand().equals(EquipmentSlot.OFF_HAND)) { - inHand = e.getPlayer().getInventory().getItemInOffHand(); + inHand = p.getInventory().getItemInOffHand(); } - if (BREEDING_ITEMS.contains(inHand.getType()) && !checkIsland(e, e.getPlayer(), e.getRightClicked().getLocation(), Flags.BREEDING)) { - ((Animals)e.getRightClicked()).setBreed(false); + if (BREEDING_ITEMS.get(animal.getType()).contains(inHand.getType()) && !checkIsland(e, p, animal.getLocation(), Flags.BREEDING)) { + animal.setBreed(false); } } } diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BreedingListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BreedingListenerTest.java index 0463dc200..ed6a2822a 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BreedingListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BreedingListenerTest.java @@ -1,6 +1,3 @@ -/** - * - */ package world.bentobox.bentobox.listeners.flags.protection; import static org.junit.Assert.assertFalse; @@ -8,12 +5,12 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.logging.Logger; @@ -26,6 +23,7 @@ import org.bukkit.World; import org.bukkit.entity.Animals; import org.bukkit.entity.Cow; import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.entity.Slime; import org.bukkit.entity.Zombie; @@ -80,12 +78,9 @@ public class BreedingListenerTest { private ItemStack itemInOffHand; private IslandWorldManager iwm; - private static final List BREEDING_ITEMS = Arrays.asList( - Material.EGG, - Material.WHEAT, - Material.CARROT, - Material.WHEAT_SEEDS, - Material.SEAGRASS); + private static final EntityType ENTITY_TYPE = EntityType.COW; + private static final Material BREEDABLE_WITH = Material.WHEAT; + private static final Material NOT_BREEDABLE_WITH = Material.SEAGRASS; @Before public void setUp() { @@ -211,7 +206,7 @@ public class BreedingListenerTest { public void testOnPlayerInteractNotAnimal() { Entity clickedEntity = mock(Entity.class); Vector position = new Vector(0,0,0); - PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity , position); + PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity, position); new BreedingListener().onPlayerInteract(e); assertFalse("Not animal failed", e.isCancelled()); } @@ -221,10 +216,9 @@ public class BreedingListenerTest { */ @Test public void testOnPlayerInteractAnimalNothingInMainHand() { - Animals clickedEntity = mock(Animals.class); Vector position = new Vector(0,0,0); - PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity , position); + PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity, position); new BreedingListener().onPlayerInteract(e); assertFalse("Animal, nothing in main hand failed", e.isCancelled()); } @@ -236,7 +230,7 @@ public class BreedingListenerTest { public void testOnPlayerInteractAnimalNothingInOffHand() { Animals clickedEntity = mock(Animals.class); Vector position = new Vector(0,0,0); - PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity , position, EquipmentSlot.OFF_HAND); + PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity, position, EquipmentSlot.OFF_HAND); new BreedingListener().onPlayerInteract(e); assertFalse("Animal, nothing in off hand failed", e.isCancelled()); } @@ -249,16 +243,19 @@ public class BreedingListenerTest { public void testOnPlayerInteractAnimalBreedingFoodInMainHandNotRightWorld() { Animals clickedEntity = mock(Animals.class); when(clickedEntity.getLocation()).thenReturn(location); + when(clickedEntity.getType()).thenReturn(ENTITY_TYPE); when(iwm.inWorld(any(World.class))).thenReturn(false); when(iwm.inWorld(any(Location.class))).thenReturn(false); Vector position = new Vector(0,0,0); - PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity , position); + PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity, position); BreedingListener bl = new BreedingListener(); - for (Material breedingMat : BREEDING_ITEMS) { - when(itemInMainHand.getType()).thenReturn(breedingMat); - bl.onPlayerInteract(e); - assertFalse("Animal, breeding item in main hand, wrong world failed " + breedingMat, e.isCancelled()); - } + + Material breedingMat = BREEDABLE_WITH; + + when(itemInMainHand.getType()).thenReturn(breedingMat); + bl.onPlayerInteract(e); + assertFalse("Animal, breeding item in main hand, wrong world failed " + breedingMat, e.isCancelled()); + // verify breeding was prevented Mockito.verify(clickedEntity, Mockito.never()).setBreed(false); } @@ -270,16 +267,19 @@ public class BreedingListenerTest { public void testOnPlayerInteractAnimalBreedingFoodInMainHand() { Animals clickedEntity = mock(Animals.class); when(clickedEntity.getLocation()).thenReturn(location); + when(clickedEntity.getType()).thenReturn(EntityType.COW); Vector position = new Vector(0,0,0); - PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity , position); + PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity, position); BreedingListener bl = new BreedingListener(); - for (Material breedingMat : BREEDING_ITEMS) { - when(itemInMainHand.getType()).thenReturn(breedingMat); - bl.onPlayerInteract(e); - assertTrue("Animal, breeding item in main hand failed " + breedingMat, e.isCancelled()); - } + + Material breedingMat = BREEDABLE_WITH; + + when(itemInMainHand.getType()).thenReturn(breedingMat); + bl.onPlayerInteract(e); + assertTrue("Animal, breeding item in main hand failed " + breedingMat, e.isCancelled()); + // verify breeding was prevented - Mockito.verify(clickedEntity, Mockito.times(5)).setBreed(false); + Mockito.verify(clickedEntity).setBreed(false); } /** @@ -292,34 +292,56 @@ public class BreedingListenerTest { when(iwm.inWorld(any(World.class))).thenReturn(false); when(iwm.inWorld(any(Location.class))).thenReturn(false); Vector position = new Vector(0,0,0); - PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity , position, EquipmentSlot.OFF_HAND); + PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity, position, EquipmentSlot.OFF_HAND); BreedingListener bl = new BreedingListener(); - for (Material breedingMat : BREEDING_ITEMS) { - when(itemInOffHand.getType()).thenReturn(breedingMat); - bl.onPlayerInteract(e); - assertFalse("Animal, breeding item in off hand, wrong world failed " + breedingMat, e.isCancelled()); - } + + Material breedingMat = BREEDABLE_WITH; + + when(itemInOffHand.getType()).thenReturn(breedingMat); + bl.onPlayerInteract(e); + assertFalse("Animal, breeding item in off hand, wrong world failed " + breedingMat, e.isCancelled()); + // verify breeding was not prevented Mockito.verify(clickedEntity, Mockito.never()).setBreed(false); } /** - * I am not sure if breeding with off hand is possible! * Test method for {@link BreedingListener#onPlayerInteract(org.bukkit.event.player.PlayerInteractAtEntityEvent)}. */ @Test public void testOnPlayerInteractAnimalBreedingFoodInOffHand() { Animals clickedEntity = mock(Animals.class); when(clickedEntity.getLocation()).thenReturn(location); + when(clickedEntity.getType()).thenReturn(ENTITY_TYPE); Vector position = new Vector(0,0,0); - PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity , position, EquipmentSlot.OFF_HAND); + PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity, position, EquipmentSlot.OFF_HAND); BreedingListener bl = new BreedingListener(); - for (Material breedingMat : BREEDING_ITEMS) { - when(itemInOffHand.getType()).thenReturn(breedingMat); - bl.onPlayerInteract(e); - assertTrue("Animal, breeding item in off hand failed " + breedingMat, e.isCancelled()); - } + + Material breedingMat = BREEDABLE_WITH; + when(itemInOffHand.getType()).thenReturn(breedingMat); + bl.onPlayerInteract(e); + assertTrue("Animal, breeding item in off hand failed " + breedingMat, e.isCancelled()); + // verify breeding was prevented - Mockito.verify(clickedEntity, Mockito.times(5)).setBreed(false); + Mockito.verify(clickedEntity).setBreed(false); + } + + @Test + public void testOnPlayerIntereactAnimalBreedingWrongFood() { + Animals clickedEntity = mock(Animals.class); + when(clickedEntity.getLocation()).thenReturn(location); + when(clickedEntity.getType()).thenReturn(EntityType.COW); + Vector position = new Vector(0,0,0); + PlayerInteractAtEntityEvent e = new PlayerInteractAtEntityEvent(player, clickedEntity, position); + BreedingListener bl = new BreedingListener(); + + Material breedingMat = NOT_BREEDABLE_WITH; + + when(itemInMainHand.getType()).thenReturn(breedingMat); + bl.onPlayerInteract(e); + assertFalse("Animal, breeding item in main hand was prevented " + breedingMat, e.isCancelled()); + + // verify breeding was prevented + Mockito.verify(clickedEntity, never()).setBreed(false); } }