package electroblob.wizardry.util; import electroblob.wizardry.entity.living.*; import net.minecraft.entity.Entity; import net.minecraft.entity.boss.EntityDragon; import net.minecraft.entity.boss.EntityWither; import net.minecraft.entity.monster.*; import net.minecraft.util.DamageSource; import net.minecraft.util.EntityDamageSource; import java.util.*; // A note on the use of the vanilla damagesources: // When using indirect damage sources, the SECOND argument is the original entity (i.e. the caster), and the // FIRST argument is the actual projectile or whatever that does the damage. getTrueSource() will return // the original entity, and getImmediateSource() will return the projectile. // The vanilla approach to damage types is inconsistent, to say the least. Poison is simply 'magic', and relies on // EntityLivingBase.isPotionApplicable to determine whether an entity is affected or not. Wither, on the other hand, is // its own damage type, but again relies on the potion to determine what is immune (which in vanilla is nothing). Fire // has a proper implementation of course, with both block-based and projectile-based types, and any other damagesource // can also be designated as fire damage with setFireDamage(). Likewise, projectile and explosion damage can also be // applied to any damagesource, but these aren't considered types in their own right; rather, they seem to be damage // type 'attributes'. This is my attempt to collect all of these into a reasonably coherent system. /** * "Ouch, that hurt!" *

* As of wizardry 1.1, this class has replaced the damagesource-related methods in WizardryUtilities, allowing a * {@link DamageType} to be specified with the damage. The main reason for this is so that damage sources can fit with * the vanilla behaviour on armour enchantments and such like whilst still being classified as wizardry damage for the * purposes of friendly fire, etc. *

* In the future, there is scope for an entirely standalone mod based on this idea. Perhaps 'Elemental Damage' could * be a config-only mod which allows its users to define any number of specific damage types, then give any creature a * resistance, immunity or vulnerability to each, as well as being able to specify the sources for each type, such as * enchantments, potions, specific items, certain entities, even particular situations and string damagesource names * from other mods. * * @see IndirectMagicDamage * @see IElementalDamage * @since Wizardry 1.1 * @author Electroblob */ public class MagicDamage extends EntityDamageSource implements IElementalDamage { /** The name of the damagesource for direct magic damage from the wizardry mod. */ public static final String DIRECT_MAGIC_DAMAGE = "wizardry_magic"; /** The name of the damagesource for indirect magic damage from the wizardry mod. */ public static final String INDIRECT_MAGIC_DAMAGE = "indirect_wizardry_magic"; // Technically, I don't need to specify that the classes in this map must extend entity, but it's good practice to. private static final Map, EnumSet> immunityMapping = new HashMap<>(); private final DamageType type; private final boolean isRetaliatory; /** * A simple set of constants for the types of damage. The names are deliberately different to those in EnumElement * to avoid confusion. All types are classified as magic damage in the vanilla system, so witches are resistant to * them. */ public enum DamageType { /** Generic magic damage from wizardry. Like vanilla magic damage, except it doesn't bypass armour. */ MAGIC, /** Fire damage from wizardry. Counts as fire damage in the vanilla system, so is blocked by any mobs * that are immune to fire and entities with the fire resistance effect, and is affected by the fire protection * enchantment. */ FIRE, /** Frost (ice) damage from wizardry. Snow golems, ice wraiths and ice giants are immune. */ FROST, /** Shock (lightning) damage from wizardry. Lightning wraiths and storm elementals are immune. */ SHOCK, /** Wither damage from wizardry. Withers, wither skeletons and shadow wraiths are immune. */ WITHER, /** Poison damage from wizardry. Spiders, cave spiders and undead mobs are immune. */ POISON, /** Force damage from wizardry. */ // Insubstantial creatures (ghast, shadow wraith, etc.) are immune? FORCE, /** Blast damage from wizardry. Affected by the blast protection enchantment. */ BLAST, /** Radiant damage from wizardry. */ RADIANT } static { // Of course, the entities that are immune to fire already are since there's a vanilla system for that, but // they're included here anyway for completeness and in case anyone wants to check if an entity is immune to // an unspecified element for reasons other than dealing damage. setEntityImmunities(EntityPhoenix.class, DamageType.FIRE); setEntityImmunities(EntityBlaze.class, DamageType.FIRE); setEntityImmunities(EntityBlazeMinion.class, DamageType.FIRE); setEntityImmunities(EntityPigZombie.class, DamageType.FIRE, DamageType.POISON); setEntityImmunities(EntityMagmaCube.class, DamageType.FIRE); setEntityImmunities(EntityGhast.class, DamageType.FIRE); setEntityImmunities(EntityDragon.class, DamageType.FIRE); setEntityImmunities(EntityStormElemental.class, DamageType.FIRE, DamageType.SHOCK); setEntityImmunities(EntityWither.class, DamageType.FIRE, DamageType.WITHER); setEntityImmunities(EntitySnowman.class, DamageType.FROST); setEntityImmunities(EntityPolarBear.class, DamageType.FROST); setEntityImmunities(EntityIceWraith.class, DamageType.FROST); setEntityImmunities(EntityIceGiant.class, DamageType.FROST); setEntityImmunities(EntityLightningWraith.class, DamageType.SHOCK); setEntityImmunities(EntityShadowWraith.class, DamageType.WITHER); setEntityImmunities(EntityWitherSkeleton.class, DamageType.WITHER); setEntityImmunities(EntitySpider.class, DamageType.POISON); setEntityImmunities(EntitySpiderMinion.class, DamageType.POISON); setEntityImmunities(EntityCaveSpider.class, DamageType.POISON); setEntityImmunities(EntityZombie.class, DamageType.POISON); setEntityImmunities(EntitySkeleton.class, DamageType.POISON); setEntityImmunities(EntityZombieMinion.class, DamageType.POISON); setEntityImmunities(EntitySkeletonMinion.class, DamageType.POISON); } public MagicDamage(String name, Entity caster, DamageType type, boolean isRetaliatory){ super(name, caster); this.type = type; this.isRetaliatory = isRetaliatory; this.setMagicDamage(); if(type == DamageType.FIRE) this.setFireDamage(); if(type == DamageType.BLAST) this.setExplosion(); } /** * Returns true if the given entity is immune to the given damage type according to the entity immunity mappings, * false otherwise. When you want to check for resistances, check this method rather than just checking the result * of attackEntityFrom, since that could return false for all sorts of reasons besides immunities. However, if you * don't need to know whether the damage succeeded or not, there's no point in checking this method. A common use of * this method is to check for immunity and if so display the "[mob] resisted [spell]" chat message. See * {@link electroblob.wizardry.spell.Arc Arc} for a good example of this, and also of when not to use it. */ public static boolean isEntityImmune(DamageType type, Entity entity){ if(type == DamageType.FIRE && entity.isImmuneToFire()) return true; EnumSet immunities = immunityMapping.get(entity.getClass()); return immunities != null && immunities.contains(type); } /** Registers the given type of entity as immune to all of the passed in damage types. */ public static void setEntityImmunities(Class entityType, DamageType... immunities){ EnumSet immunitySet = EnumSet.noneOf(DamageType.class); Collections.addAll(immunitySet, immunities); immunityMapping.put(entityType, immunitySet); } /** Adds the passed in damage type to the list of damage types to which the given entity is immune. */ public static void addEntityImmunity(Class entityType, DamageType immunity){ EnumSet immunitySet = immunityMapping.get(entityType); if (immunitySet == null) immunitySet = EnumSet.of(immunity); else immunitySet.add(immunity); immunityMapping.put(entityType, immunitySet); } /** * Returns a DamageSource called "wizardry_magic" with the given entity as the caster. Use in preference to vanilla * types to allow things to distinguish between magic and regular melee/swords. Unlike DamageSource.MAGIC, it does * not bypass armour and has a player as the source (rather than nothing). It is still classed as magic damage (for * the record, all this does in vanilla is make witches 85% resistant to it - but that seems kinda right anyway). * isRetaliatory defaults to false. *

* Now that this is its own class, this static method is largely redundant, but it's not worth refactoring the * entire mod just to get rid of this method and use the constructor instead. * * @param caster The player or other living entity causing the damage * @param type The type that this damage belongs to; used for resistances and wand perks. Use * {@link DamageType#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element * even though the spell has one - for example, not all necromancy spells are 'withery', so some of them * might reasonably affect creatures that are usually immune to wither effects). * @return A damagesource object of type EntityDamageSource */ public static DamageSource causeDirectMagicDamage(Entity caster, DamageType type){ return causeDirectMagicDamage(caster, type, false); } /** * Returns a DamageSource called "wizardry_magic" with the given entity as the caster. Use in preference to vanilla * types to allow things to distinguish between magic and regular melee/swords. Unlike DamageSource.MAGIC, it does * not bypass armour and has a player as the source (rather than nothing). It is still classed as magic damage (for * the record, all this does in vanilla is make witches 85% resistant to it - but that seems kinda right anyway). *

* Now that this is its own class, this static method is largely redundant, but it's not worth refactoring the * entire mod just to get rid of this method and use the constructor instead. * * @param caster The player or other living entity causing the damage * @param type The type that this damage belongs to; used for resistances and wand perks. Use * {@link DamageType#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element * even though the spell has one - for example, not all necromancy spells are 'withery', so some of them * might reasonably affect creatures that are usually immune to wither effects). * @param isRetaliatory whether this damage source came from a retaliatory attack; prevents infinite loops occurring * when two entities damage each other with retaliatory effects. * @return A damagesource object of type EntityDamageSource */ public static DamageSource causeDirectMagicDamage(Entity caster, DamageType type, boolean isRetaliatory){ return new MagicDamage(DIRECT_MAGIC_DAMAGE, caster, type, isRetaliatory); } /** * Returns a DamageSource called "indirect_wizardry_magic" with the player as the caster. Use in preference to vanilla * types to allow things to distinguish between magic and regular arrows/throwables. Unlike * DamageSource.causeIndirectMagicDamage, it does not bypass armour. It is still classed as magic damage (for the * record, all this does in vanilla is make witches 85% resistant to it - but that seems kinda right anyway). * isRetaliatory defaults to false. *

* Now that this is its own class, this static method is largely redundant, but it's not worth refactoring the * entire mod just to get rid of this method and use the constructor instead. * * @param magic The entity that actually caused the damage * @param caster The player or other living entity that cast the spell originally * @param type The type that this damage belongs to; used for resistances and wand perks. Use * {@link DamageType#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element * even though the spell has one - for example, not all necromancy spells are 'withery', so some of them * might reasonably affect creatures that are usually immune to wither effects). * @return A damagesource object of type EntityDamageSourceIndirect */ public static DamageSource causeIndirectMagicDamage(Entity magic, Entity caster, DamageType type){ return causeIndirectMagicDamage(magic, caster, type, false); } /** * Returns a DamageSource called "indirect_wizardry_magic" with the player as the caster. Use in preference to vanilla * types to allow things to distinguish between magic and regular arrows/throwables. Unlike * DamageSource.causeIndirectMagicDamage, it does not bypass armour. It is still classed as magic damage (for the * record, all this does in vanilla is make witches 85% resistant to it - but that seems kinda right anyway). *

* Now that this is its own class, this static method is largely redundant, but it's not worth refactoring the * entire mod just to get rid of this method and use the constructor instead. * * @param magic The entity that actually caused the damage * @param caster The player or other living entity that cast the spell originally * @param type The type that this damage belongs to; used for resistances and wand perks. Use * {@link DamageType#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element * even though the spell has one - for example, not all necromancy spells are 'withery', so some of them * might reasonably affect creatures that are usually immune to wither effects). * @param isRetaliatory whether this damage source came from a retaliatory attack; prevents infinite loops occurring * when two entities damage each other with retaliatory effects. * @return A damagesource object of type EntityDamageSourceIndirect */ public static DamageSource causeIndirectMagicDamage(Entity magic, Entity caster, DamageType type, boolean isRetaliatory){ return new IndirectMagicDamage(INDIRECT_MAGIC_DAMAGE, magic, caster, type, isRetaliatory); } @Override public DamageType getType(){ return type; } @Override public boolean isRetaliatory(){ return isRetaliatory; } }