package toadmess.explosives.config.entity; import static toadmess.explosives.config.ConfProps.*; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; import toadmess.explosives.Bounds; import toadmess.explosives.config.ConfProps; /** * Represents a single entity's configuration within a single world. * * @author John Revill */ public class EntityConf { /** * Sub configuration all have a reference to their parent containing * configuration, which allows inheritance of their properties. * This will be null for root configurations. */ private final EntityConf parent; /** Used when conjuring up a new multiplier based on some random chance */ private final Random rng; /** * Contains all of the configuration properties. An array indexed by the ConfProps's ordinal value. * If the property has not been specified for this configuration then the value will be * null at that index. */ private Object[] properties; /** * True if the MiningTNT plugin was detected in the list of plugins. False otherwise. * Set by the Plugin main class. */ public static boolean hasConflictWithMiningTNT = false; /** * Creates a normal EntityConfig with a specific parent to inherit from. * If the parent is null, no properties are inherited. */ protected EntityConf(final EntityConf parent, final Random rng) { this.parent = parent; this.rng = rng; } /** * Creates an EntityConf by copying an existing EntityConf, but using a different parent. * This constructor is most likely used for a configuration with a modified inheritance * chain, such as a chain of Permissions based configurations tailored to a specific player. */ public EntityConf(final EntityConf configToCopy, final EntityConf newParent, final Random rng) { this(newParent, rng); this.setProperties(configToCopy.properties); } protected void setProperties(final Object[] allTheReadConfigProperties) { this.properties = allTheReadConfigProperties; } @SuppressWarnings("unchecked") private float getNextMultiplier(final ConfProps multiplierProperty) { return getNextMultiplier((List<List<Float>>) this.getInheritedProp(multiplierProperty)); } private float getNextMultiplier(final List<List<Float>> sortedMultipliers) { if(sortedMultipliers == null || sortedMultipliers.size() == 0) { // Default to 1.0 to be safe return 1.0F; } final float randomNum = this.rng.nextFloat(); float cumulativeChance = 0.0F; for (final List<Float> multiplierPair : sortedMultipliers) { cumulativeChance += multiplierPair.get(0); if (cumulativeChance > randomNum) { return multiplierPair.get(1); } } // Well, the chance values didn't add up to 1.0. Just return the most likely multiplier (the first in the list) return sortedMultipliers.get(0).get(1); } public float getYield() { final Float yield = (Float) this.getInheritedProp(CONF_ENTITY_YIELD); if(EntityConf.hasConflictWithMiningTNT && yield == null) { // There's no yield specified in the config.yml, but because the // MiningTNT plugin is in use on this server, we set the yield to 1.0. // This is MiningTNT's default. return 1.0F; } return yield; } // HACK: Breaks encapsulation and allow modification of the array's contents, but // why would any caller modify this array's contents? public Float[] getSpecificYieldConfig() { return (Float[]) this.getInheritedProp(ConfProps.CONF_ENTITY_YIELD_SPECIFIC); } public Bounds getActiveBounds() { return (Bounds) this.getInheritedProp(CONF_BOUNDS); } public boolean getFire() { return (Boolean) this.getInheritedProp(CONF_ENTITY_FIRE); } public boolean getPreventTerrainDamage() { return (Boolean) this.getInheritedProp(ConfProps.CONF_ENTITY_PREVENT_TERRAIN_DAMAGE); } public boolean getTNTPrimePrevented() { return (Boolean) this.getInheritedProp(ConfProps.CONF_ENTITY_TNT_TRIGGER_PREVENTED); } public float getNextRadiusMultiplier() { return getNextMultiplier(ConfProps.CONF_ENTITY_RADIUSMULT); } public float getNextPlayerDamageMultiplier() { return getNextMultiplier(ConfProps.CONF_ENTITY_PLAYER_DAMAGEMULT); } public float getNextCreatureDamageMultiplier() { return getNextMultiplier(ConfProps.CONF_ENTITY_CREATURE_DAMAGEMULT); } public float getNextItemDamageMultiplier() { return getNextMultiplier(ConfProps.CONF_ENTITY_ITEM_DAMAGEMULT); } public float getNextTNTFuseMultiplier() { return getNextMultiplier(ConfProps.CONF_ENTITY_TNT_FUSEMULT); } public EntityConf getTNTPrimeByHandConfig() { return (EntityConf) this.getInheritedProp(CONF_ENTITY_TNT_TRIGGER_HAND); } public EntityConf getTNTPrimeByFireConfig() { return (EntityConf) this.getInheritedProp(CONF_ENTITY_TNT_TRIGGER_FIRE); } public EntityConf getTNTPrimeByRedstoneConfig() { return (EntityConf) this.getInheritedProp(CONF_ENTITY_TNT_TRIGGER_REDSTONE); } public EntityConf getTNTPrimeByExplosionConfig() { return (EntityConf) this.getInheritedProp(CONF_ENTITY_TNT_TRIGGER_EXPLOSION); } public EntityConf getCreeperChargedConfig() { return (EntityConf) this.getInheritedProp(CONF_ENTITY_CREEPER_CHARGED); } @SuppressWarnings("unchecked") public List<EntityConf> getPermissionsBasedConfigs() { return (List<EntityConf>) this.getInheritedProp(CONF_PERMISSIONS_LIST); } public String getPermissionNodeName() { // Not inherited. Only makes sense for sub configs in a permissionsBasedConfigs (CONF_PERMISSIONS_LIST) list. return (String) this.getOwnProp(CONF_PERMISSIONS_NODE_NAME); } public String getPermissionGroupName() { // Not inherited. Only makes sense for sub configs in a permissionsBasedConfigs (CONF_PERMISSIONS_LIST) list. return (String) this.getOwnProp(CONF_PERMISSIONS_GROUP_NAME); } public boolean hasFireConfig() { return this.hasInheritedProp(CONF_ENTITY_FIRE); } public boolean hasRadiusConfig() { return this.hasInheritedProp(ConfProps.CONF_ENTITY_RADIUSMULT); } public boolean hasCreatureDamageConfig() { return this.hasInheritedProp(ConfProps.CONF_ENTITY_CREATURE_DAMAGEMULT); } public boolean hasPlayerDamageConfig() { return this.hasInheritedProp(ConfProps.CONF_ENTITY_PLAYER_DAMAGEMULT); } public boolean hasItemDamageConfig() { return this.hasInheritedProp(ConfProps.CONF_ENTITY_ITEM_DAMAGEMULT); } public boolean hasTNTFuseConfig() { return this.hasInheritedProp(ConfProps.CONF_ENTITY_TNT_FUSEMULT); } public boolean hasYieldConfig() { return this.hasInheritedProp(CONF_ENTITY_YIELD) || EntityConf.hasConflictWithMiningTNT; } public boolean hasSpecificYieldConfig() { return this.hasInheritedProp(ConfProps.CONF_ENTITY_YIELD_SPECIFIC); } public boolean hasPreventTerrainDamageConfig() { return this.hasInheritedProp(ConfProps.CONF_ENTITY_PREVENT_TERRAIN_DAMAGE); } public boolean hasTNTPrimePrevented() { return this.hasInheritedProp(ConfProps.CONF_ENTITY_TNT_TRIGGER_PREVENTED); } public boolean hasTNTPrimeByHandConfig() { return this.hasInheritedProp(CONF_ENTITY_TNT_TRIGGER_HAND); } public boolean hasTNTPrimeByFireConfig() { return this.hasInheritedProp(CONF_ENTITY_TNT_TRIGGER_FIRE); } public boolean hasTNTPrimeByRedstoneConfig() { return this.hasInheritedProp(CONF_ENTITY_TNT_TRIGGER_REDSTONE); } public boolean hasTNTPrimeByExplosionConfig() { return this.hasInheritedProp(CONF_ENTITY_TNT_TRIGGER_EXPLOSION); } public boolean hasCreeperChargedConfig() { return this.hasInheritedProp(CONF_ENTITY_CREEPER_CHARGED); } public boolean hasPermissionsBasedConfigs() { return this.hasInheritedProp(CONF_PERMISSIONS_LIST); } protected boolean hasOwnProp(final ConfProps property) { return (hasInheritedProp(property) && (getOwnProp(property) != null)); } protected Object getOwnProp(final ConfProps property) { return this.properties[property.ordinal()]; } protected boolean hasInheritedProp(final ConfProps property) { return null != getInheritedProp(property); } protected Object getInheritedProp(final ConfProps property) { final Object ownProp = this.properties[property.ordinal()]; if(null != ownProp) { return ownProp; } if(isSubConfig()) { return this.parent.getInheritedProp(property); } return null; } public boolean isEmptyConfig() { for(final ConfProps property : ConfProps.values()) { switch(property) { case CONF_BOUNDS: // Skip the bounds configuration. // It's not so interesting if the config has no properties. // And it defaults to having empty bounds as well. continue; case CONF_ENTITY_YIELD: // Yield has a special workaround for MiningTNT if(this.hasYieldConfig()) { return false; } break; default: if(this.hasInheritedProp(property)) { return false; } } } return true; } public boolean isSubConfig() { return this.parent != null; } public String toString() { return (new EntityConfPrinter(this)).stringify(); } public Set<EntityConf> getConfigAndAllSubConfigs() { final Set<EntityConf> allConfigs = new HashSet<EntityConf>(); allConfigs.add(this); if(this.hasTNTPrimeByHandConfig()) { final EntityConf tntPrimeHandConf = this.getTNTPrimeByHandConfig(); if(allConfigs.add(tntPrimeHandConf)) { allConfigs.addAll(tntPrimeHandConf.getConfigAndAllSubConfigs()); } } if(this.hasTNTPrimeByFireConfig()) { allConfigs.add(this.getTNTPrimeByFireConfig()); } if(this.hasTNTPrimeByRedstoneConfig()) { allConfigs.add(this.getTNTPrimeByRedstoneConfig()); } if(this.hasTNTPrimeByExplosionConfig()) { allConfigs.add(this.getTNTPrimeByExplosionConfig()); } if(this.hasCreeperChargedConfig()) { allConfigs.add(this.getCreeperChargedConfig()); } if(this.hasPermissionsBasedConfigs()) { allConfigs.addAll(this.getPermissionsBasedConfigs()); } return Collections.unmodifiableSet(allConfigs); } }