Fixed and improved Breeding protection

It wasn't catching breeding with some newer animals (Pandas, Foxes...), was a bit overprotective (it could prevent feeding some animals), or wasn't catching some breeding items (Golden Carrots, ...).
I rewrote it so that every animal is now protected with the foods that actually make them breed (based off the MC wiki page about Breeding).
Once we will move to 1.15 API, we will have to add BEES in that.
This commit is contained in:
Florian CUNY 2020-02-12 11:36:08 +01:00
parent cc0d981e62
commit 120be13b36
2 changed files with 97 additions and 52 deletions

View File

@ -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<Material> BREEDING_ITEMS = Arrays.asList(
Material.EGG,
Material.WHEAT,
Material.CARROT,
Material.WHEAT_SEEDS,
Material.SEAGRASS);
private static final ImmutableMap<EntityType, List<Material>> BREEDING_ITEMS = new ImmutableMap.Builder<EntityType, List<Material>>()
.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);
}
}
}

View File

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