/* * ExperienceMod - Bukkit server plugin for modifying the experience system in Minecraft. * Copyright (C) 2012 Kristian S. Stangeland * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ package com.comphenix.xp.lookup; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.bukkit.entity.Ageable; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Skeleton; import org.bukkit.entity.Zombie; import org.bukkit.entity.Skeleton.SkeletonType; import org.bukkit.entity.Slime; import org.bukkit.entity.Tameable; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import com.comphenix.xp.parser.Utility; import com.google.common.collect.Lists; public class MobQuery implements Query { // DON'T CARE fields are empty private List<Short> typeID; private List<DamageCause> deathCause; private List<Integer> size; private List<SkeletonType> skeletonType; private List<Boolean> spawner; private List<Boolean> baby; private List<Boolean> tamed; private List<Boolean> playerKill; private List<Boolean> villaged; // Optimize away object creations private static List<Short> noTypes = Utility.getElementList((Short) null); private static List<Integer> noSizes = Utility.getElementList((Integer) null); private static List<DamageCause> noDamages = Utility.getElementList((DamageCause) null); private static List<SkeletonType> noSkeletons = Utility.getElementList((SkeletonType) null); private static List<Boolean> noBooleans = Utility.getElementList((Boolean) null); /** * Universal query. */ public static MobQuery fromAny() { return new MobQuery(noTypes, noDamages, noSizes, noSkeletons, noBooleans, noBooleans, noBooleans, noBooleans, noBooleans); } /** * Creates a query where NULL values match any possible value in that category. * @param type - the unique type to search for, or NULL for all mob types. * @return The new mob query. */ public static MobQuery fromAny(EntityType type) { return fromAny(type, null, null, null, null, null, null); } /** * Creates a query where NULL values match any possible value in that category. * @param type - the unique type to search for, or NULL for all mob types. * @param cause - the damage type that killed the mob, or NULL for all damage types. * @return The new mob query. */ public static MobQuery fromAny(EntityType type, DamageCause cause) { return fromAny(type, cause, null, null, null, null, null); } /** * Creates a query where NULL values match any possible value in that category. * @param type - the unique type to search for, or NULL for all mob types. * @param deathCause - the damage type that killed the mob, or NULL for all damage types. * @param reason - the cause for spawning this mob, or NULL for all possible causes. * @param baby - TRUE to match babies, FALSE to match adults, and NULL to match both. * @param tamed - TRUE to matched tamed animals (wolfs), FALSE to match non-tamed or not-tamable, NULL for both. * @param playerKill - TRUE to match mobs killed by players, FALSE to match mobs killed by anything else, NULL for both. * @param villaged - TRUE to match zombie villagers, FALSE to match mobs killed by anything else, NULL for both. * @return The new mob query. */ public static MobQuery fromAny(EntityType type, DamageCause deathCause, SpawnReason reason, Boolean baby, Boolean tamed, Boolean playerKill, Boolean villaged) { // Don't specify a size return fromAny(type, deathCause, null, reason, baby, tamed, playerKill, villaged); } /** * Creates a query where NULL values match any possible value in that category. * @param type - the unique type to search for, or NULL for all mob types. * @param deathCause - the damage type that killed the mob, or NULL for all damage types. * @param size - size of the slime/magma cube, or NULL for all sizes (or mobs without different sizes). * @param reason - the cause for spawning this mob, or NULL for all possible causes. * @param baby - TRUE to match babies, FALSE to match adults, and NULL to match both. * @param tamed - TRUE to matched tamed animals (wolfs), FALSE to match non-tamed or not-tamable, NULL for both. * @param playerKill - TRUE to match mobs killed by players, FALSE to match mobs killed by anything else, NULL for both. * @param villaged - TRUE to match zombie villagers, FALSE to match mobs killed by anything else, NULL for both. * @return The new mob query. */ public static MobQuery fromAny(EntityType type, DamageCause deathCause, Integer size, SpawnReason reason, Boolean baby, Boolean tamed, Boolean playerKill, Boolean villaged) { // Convert type to type ID return fromAny(type != null ? type.getTypeId() : null, deathCause, size, reason, baby, tamed, playerKill, villaged); } /** * Creates a query where NULL values match any possible value in that category. * @param typeID - the unique type ID to search for, or NULL for all mob types. * @param deathCause - the damage type that killed the mob, or NULL for all damage types. * @param size - size of the slime/magma cube, or NULL for all sizes (or mobs without different sizes). * @param reason - the cause for spawning this mob, or NULL for all possible causes. * @param baby - TRUE to match babies, FALSE to match adults, and NULL to match both. * @param tamed - TRUE to matched tamed animals (wolfs), FALSE to match non-tamed or not-tamable, NULL for both. * @param playerKill - TRUE to match mobs killed by players, FALSE to match mobs killed by anything else, NULL for both. * @param villaged - TRUE to match zombie villagers, FALSE to match mobs killed by anything else, NULL for both. * @return The new mob query. */ public static MobQuery fromAny(Short typeID, DamageCause deathCause, Integer size, SpawnReason reason, Boolean baby, Boolean tamed, Boolean playerKill, Boolean villaged) { return fromAny(typeID, deathCause, size, null, reason, baby, tamed, playerKill, villaged); } /** * Creates a query where NULL values match any possible value in that category. * @param typeID - the unique type ID to search for, or NULL for all mob types. * @param deathCause - the damage type that killed the mob, or NULL for all damage types. * @param size - size of the slime/magma cube, or NULL for all sizes (or mobs without different sizes). * @param skeleton - the type of the skeleton to find, or NULL for all skeleton types (or non-skeletons). * @param reason - the cause for spawning this mob, or NULL for all possible causes. * @param baby - TRUE to match babies, FALSE to match adults, and NULL to match both. * @param tamed - TRUE to matched tamed animals (wolfs), FALSE to match non-tamed or not-tamable, NULL for both. * @param playerKill - TRUE to match mobs killed by players, FALSE to match mobs killed by anything else, NULL for both. * @param villaged - TRUE to match zombie villagers, FALSE to match mobs killed by anything else, NULL for both. * @return The new mob query. */ public static MobQuery fromAny(Short typeID, DamageCause deathCause, Integer size, SkeletonType skeleton, SpawnReason reason, Boolean baby, Boolean tamed, Boolean playerKill, Boolean villaged) { List<Boolean> spawner; if (reason != null) spawner = Arrays.asList(reason == SpawnReason.SPAWNER); else spawner = new ArrayList<Boolean>(); return new MobQuery( Utility.getElementList(typeID), Utility.getElementList(deathCause), Utility.getElementList(size), Utility.getElementList(skeleton), spawner, Utility.getElementList(baby), Utility.getElementList(tamed), Utility.getElementList(playerKill), Utility.getElementList(villaged) ); } /** * Creates an exact query - where null values only match unspecified "any" queries - from the given * entity, spawn reason and killer. * @param entity - the entity to create from. * @param reason - the action that spawned the given entity. * @param playerKill - TRUE if this mob was killed by a player, FALSE if it was killed by anything else. * @return The exact query. */ public static MobQuery fromExact(LivingEntity entity, SpawnReason reason, boolean playerKill) { EntityDamageEvent cause = entity.getLastDamageCause(); DamageCause deathCause = null; Integer size = null; SkeletonType skeleton = null; Boolean villaged = false; Boolean paramBaby = false; Boolean paramTamed = false; if (cause != null) { deathCause = cause.getCause(); } // Check age and tame status if (entity instanceof Ageable) { paramBaby = !((Ageable) entity).isAdult(); } if (entity instanceof Tameable) { paramTamed = ((Tameable)entity).isTamed(); } // Check size if (entity instanceof Slime) { size = ((Slime)entity).getSize(); } // Check skeleton type if (entity instanceof Skeleton) { skeleton = ((Skeleton) entity).getSkeletonType(); } // Check villaged if (entity instanceof Zombie) { villaged = ((Zombie) entity).isVillager(); } // Load directly return fromExact(entity.getType(), deathCause, size, skeleton, reason, paramBaby, paramTamed, playerKill, villaged); } /** * Creates an exact query - where null values only match unspecified "any" queries - from the given * entity, spawn reason and killer. * @param type - the entity type to search for. * @param deathCause - the last damage to include in the search. * @param reason - the action that spawned the given entity. * @param baby - TRUE to search for babies, FALSE to search for adults. * @param tamed - TRUE to search for tamed animals, FALSE for everything else. * @param playerKill - TRUE to search for mobs killed by a player, FALSE for anything else. * @param villaged - TRUE if we are looking for village mobs, such as zombie villagers. * @return The exact query. */ public static MobQuery fromExact(EntityType type, DamageCause deathCause, SpawnReason reason, Boolean baby, Boolean tamed, Boolean playerKill, Boolean villaged) { return fromExact(type != null ? type.getTypeId() : null, deathCause, null, reason, baby, tamed, playerKill, villaged); } /** * Creates an exact query - where null values only match unspecified "any" queries - from the given * entity, spawn reason and killer. * @param type - the entity type to search for. * @param deathCause - the last damage to include in the search. * @param size - the slime/magma cube size to search for. * @param reason - the action that spawned the given entity. * @param baby - TRUE to search for babies, FALSE to search for adults. * @param tamed - TRUE to search for tamed animals, FALSE for everything else. * @param playerKill - TRUE to search for mobs killed by a player, FALSE for anything else. * @param villaged - TRUE if we are looking for village mobs, such as zombie villagers. * @return The exact query. */ public static MobQuery fromExact(EntityType type, DamageCause deathCause, Integer size, SpawnReason reason, Boolean baby, Boolean tamed, Boolean playerKill, Boolean villaged) { return fromExact(type != null ? type.getTypeId() : null, deathCause, size, reason, baby, tamed, playerKill, villaged); } /** * Creates an exact query - where null values only match unspecified "any" queries - from the given * entity, spawn reason and killer. * @param type - the entity type to search for. * @param deathCause - the last damage to include in the search. * @param size - the slime/magma cube size to search for. * @param skeleton - the type of skeleton to search for. * @param reason - the action that spawned the given entity. * @param baby - TRUE to search for babies, FALSE to search for adults. * @param tamed - TRUE to search for tamed animals, FALSE for everything else. * @param playerKill - TRUE to search for mobs killed by a player, FALSE for anything else. * @param villaged - TRUE if we are looking for village mobs, such as zombie villagers. * @return The exact query. */ public static MobQuery fromExact(EntityType type, DamageCause deathCause, Integer size, SkeletonType skeleton, SpawnReason reason, Boolean baby, Boolean tamed, Boolean playerKill, Boolean villaged) { return fromExact(type != null ? type.getTypeId() : null, deathCause, size, skeleton, reason, baby, tamed, playerKill, villaged); } /** * Creates an exact query - where null values only match unspecified "any" queries - from the given * entity, spawn reason and killer. * @param typeID - the unique entity type ID to search for. * @param deathCause - the last damage to include in the search. * @param size - the slime/magma cube size to search for. * @param reason - the action that spawned the given entity. * @param baby - TRUE to search for babies, FALSE to search for adults. * @param tamed - TRUE to search for tamed animals, FALSE for everything else. * @param playerKill - TRUE to search for mobs killed by a player, FALSE for anything else. * @param villaged - TRUE if we are looking for village mobs, such as zombie villagers. * @return The exact query. */ public static MobQuery fromExact(Short typeID, DamageCause deathCause, Integer size, SpawnReason reason, Boolean baby, Boolean tamed, Boolean playerKill, Boolean villaged) { return new MobQuery( Lists.newArrayList(typeID), Lists.newArrayList(deathCause), Lists.newArrayList(size), Lists.newArrayList(reason == null ? null : (reason == SpawnReason.SPAWNER)), Lists.newArrayList(baby), Lists.newArrayList(tamed), Lists.newArrayList(playerKill), Lists.newArrayList(villaged) ); } /** * Creates an exact query - where null values only match unspecified "any" queries - from the given * entity, spawn reason and killer. * @param typeID - the unique entity type ID to search for. * @param deathCause - the last damage to include in the search. * @param size - the slime/magma cube size to search for. * @param skeleton - the type of skeleton to search for. * @param reason - the action that spawned the given entity. * @param baby - TRUE to search for babies, FALSE to search for adults. * @param tamed - TRUE to search for tamed animals, FALSE for everything else. * @param playerKill - TRUE to search for mobs killed by a player, FALSE for anything else. * @param villaged - TRUE if we are looking for village mobs, such as zombie villagers. * @return The exact query. */ public static MobQuery fromExact(Short typeID, DamageCause deathCause, Integer size, SkeletonType skeleton, SpawnReason reason, Boolean baby, Boolean tamed, Boolean playerKill, Boolean villaged) { return new MobQuery( Lists.newArrayList(typeID), Lists.newArrayList(deathCause), Lists.newArrayList(size), Lists.newArrayList(skeleton), Lists.newArrayList(reason == null ? null : (reason == SpawnReason.SPAWNER)), Lists.newArrayList(baby), Lists.newArrayList(tamed), Lists.newArrayList(playerKill), Lists.newArrayList(villaged) ); } public MobQuery(List<Short> typeID, List<DamageCause> deathCause, List<Integer> size, List<Boolean> spawner, List<Boolean> baby, List<Boolean> tamed, List<Boolean> playerKill, List<Boolean> villaged) { this(typeID, deathCause, size, noSkeletons, spawner, baby, tamed, playerKill, villaged); } public MobQuery(List<Short> typeID, List<DamageCause> deathCause, List<Integer> size, List<SkeletonType> skeleton, List<Boolean> spawner, List<Boolean> baby, List<Boolean> tamed, List<Boolean> playerKill, List<Boolean> villaged) { this.typeID = typeID; this.deathCause = deathCause; this.size = size; this.skeletonType = skeleton; this.spawner = spawner; this.baby = baby; this.tamed = tamed; this.playerKill = playerKill; this.villaged = villaged; } public List<DamageCause> getDeathCause() { return deathCause; } public List<Short> getType() { return typeID; } public List<Integer> getSize() { return size; } public List<Boolean> getSpawner() { return spawner; } public List<Boolean> getBaby() { return baby; } public List<Boolean> getTamed() { return tamed; } public List<Boolean> getVillaged() { return villaged; } public List<Boolean> getPlayerKill() { return playerKill; } public List<SkeletonType> getSkeletonType() { return skeletonType; } public boolean hasType() { return typeID != null && !typeID.isEmpty(); } public boolean hasDeathCause() { return deathCause != null && !deathCause.isEmpty(); } public boolean hasSize() { return size != null && !size.isEmpty(); } public boolean hasSpawner() { return spawner != null && !spawner.isEmpty(); } public boolean hasBaby() { return baby != null && !baby.isEmpty(); } public boolean hasTamed() { return tamed != null && !tamed.isEmpty(); } public boolean hasPlayerKill() { return playerKill != null && !playerKill.isEmpty(); } public boolean hasVillaged() { return villaged != null && !villaged.isEmpty(); } public boolean hasSkeletonType() { return skeletonType != null && !skeletonType.isEmpty(); } @Override public int hashCode() { return new HashCodeBuilder(17, 31). append(typeID). append(deathCause). append(size). append(skeletonType). append(spawner). append(baby). append(tamed). append(playerKill). append(villaged). toHashCode(); } @Override public boolean equals(Object obj) { if (obj == null) return false; if (obj == this) return true; if (obj.getClass() != getClass()) return false; MobQuery other = (MobQuery) obj; return new EqualsBuilder(). append(typeID, other.typeID). append(deathCause, other.deathCause). append(size, other.size). append(skeletonType, other.skeletonType). append(spawner, other.spawner). append(baby, other.baby). append(tamed, other.tamed). append(playerKill, other.playerKill). append(villaged, other.villaged). isEquals(); } @Override public boolean match(Query other) { // Yes, we could construct a MobTree, put the current MobQuery // into it and query after other, but this is faster. Probably. if (other instanceof MobQuery) { MobQuery query = (MobQuery) other; // Make sure the current query is the superset of other return QueryMatching.matchParameter(typeID, query.typeID) && QueryMatching.matchParameter(deathCause, query.deathCause) && QueryMatching.matchParameter(size, query.size) && QueryMatching.matchParameter(skeletonType, query.skeletonType) && QueryMatching.matchParameter(spawner, query.spawner) && QueryMatching.matchParameter(baby, query.baby) && QueryMatching.matchParameter(tamed, query.tamed) && QueryMatching.matchParameter(playerKill, query.playerKill) && QueryMatching.matchParameter(villaged, query.villaged); } // Query must be of the same type return false; } @Override public String toString() { return String.format("%s|%s|%s|%s|%s|%s|%s|%s|%s", hasType() ? StringUtils.join(typeID, ", ") : "", hasDeathCause() ? StringUtils.join(deathCause, ", ") : "", hasSize() ? StringUtils.join(size, ", ") : "", hasSkeletonType() ? StringUtils.join(skeletonType, ", ") : "", Utility.formatBoolean("spawner", spawner), Utility.formatBoolean("baby", baby), Utility.formatBoolean("tamed", tamed), Utility.formatBoolean("playerKill", playerKill), Utility.formatBoolean("villaged", villaged)); } @Override public Types getQueryType() { return Types.MOBS; } }