package electroblob.wizardry.spell; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; import electroblob.wizardry.Wizardry; import electroblob.wizardry.constants.Element; import electroblob.wizardry.constants.SpellType; import electroblob.wizardry.constants.Tier; import electroblob.wizardry.entity.living.EntityWizard; import electroblob.wizardry.registry.Spells; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.client.resources.I18n; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; import net.minecraft.util.EnumHand; import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.world.World; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.registries.ForgeRegistry; import net.minecraftforge.registries.IForgeRegistry; import net.minecraftforge.registries.IForgeRegistryEntry; /** * Generic spell class which is the superclass to all spells in wizardry. When extending this class, you must do the * following: *
* - Have a constructor which passes all necessary constants into the super constructor. I define the constants here so * that the constructor for an individual spell has no parameters, but you may prefer to pass in the parameters when the * spell is registered, so all the mana costs etc. are in one place like a sort of sandbox. *
* - Implement the {@link Spell#cast(World, EntityPlayer, EnumHand, int, SpellModifiers)} method, in which you should * execute the code that makes the spell work, and return true or false depending on whether the spell succeeded and * therefore whether mana should be used up. *
* - Register the spell using {@link RegistryEvent.Register}, with {@link Spell} as the type parameter. Each spell * should have a single instance, like blocks and items. As of Wizardry 2.1, spells use the Forge registry system. * Related methods such as {@link Spell#id()} and {@link Spell#get(int)} have been re-routed to use this system, leaving * minimal external changes. Note also that the constructor automatically sets the registry name for you, though you may * change it afterwards if necessary. *
* Also note that you can override some other methods from this class. For example, to add a specific kind of formatting * to a spell name or description, you can override {@link Spell#getDisplayName()}, * {@link Spell#getDisplayNameWithFormatting()} or {@link Spell#getDescription()} and append the formatting code (though * you will have to call super() to get the name itself, since the unlocalised name is private). See * {@link SummonShadowWraith#getDescription()} for an example. *
* {@link Spell#get(int)} gets a spell instance from its integer id, which corresponds to the metadata of its spell
* book.
* {@link Spell#get(String)} gets a spell instance from its unlocalised name.
* {@link Spell#getSpells(Predicate)} returns a list of spell instances that match the given {@link Predicate}.
* {@link Spell#getTotalSpellCount()} returns the total number of registered spells.
*
* Each spell must return true when it works or the spell will not use up mana. Note that (!world.isRemote) does not * count as a condition; return true should be outside it - in other words, return a value on both the client and * the server. *
* It's worth noting that on the client side, this method only gets called if the server side cast() method * succeeded, so you can put any particle spawning code outside of any success conditions if there are discrepancies * between client and server. * * @param world A reference to the world object. Again this is for convenience, you can also use caster.world. * @param caster The EntityPlayer that cast the spell. * @param hand The hand that is holding the item used to cast the spell. If no item was used, this will be the main * hand. * @param ticksInUse The number of ticks the spell has already been cast for. For all non-continuous spells, this is * 0 and is not used. For continuous spells, it is passed in as the maximum use duration of the item minus * the count parameter in onUsingItemTick and therefore it increases by 1 each tick. * @param modifiers A {@link SpellModifiers} object containing the modifiers that have been applied to the spell. * See the javadoc for that class for more information. If no modifiers are required, pass in * {@code new SpellModifiers()}. * @return True if the spell succeeded and mana should be used up, false if not. */ public abstract boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers); /** * Casts the spell, but with an EntityLiving as the caster. Each subclass can optionally override this method and * within it execute the code to make the spell work. Returns a boolean to allow whatever calls this method to check * if the spell was actually cast or whether a spell specific condition caused it not to be (for example, heal won't * work if the caster is on full health). *
* This method is intended for use by NPCs (see {@link EntityWizard}) so that they can cast spells. Override it if * you want a spell to be cast by wizards. Note that you must also override {@link Spell#canBeCastByNPCs()} to * return true to allow wizards to select the spell. For some spells, this method may well be exactly the same as * the regular cast method; for others it won't be - for example, projectile-based spells are normally done using * the player's look vector, but NPCs need to use a target-based method instead. *
* Each spell must return true when it works. Note that (!world.isRemote) does not count as a condition; return true * should be outside it - in other words, return a value on both the client and the server. *
* It's worth noting that on the client side, this method only gets called if the server side cast() method * succeeded, so you can put any particle spawning code outside of any success conditions if there are discrepancies * between client and server. * * @param world A reference to the world object. This is for convenience, you can also use caster.world. * @param caster The EntityLiving that cast the spell. * @param hand The hand that is holding the item used to cast the spell. This will almost certainly be the main * hand. * @param ticksInUse The number of ticks the spell has already been cast for. For all non-continuous spells, this is * 0 and is not used. * @param target The EntityLivingBase that is targeted by the spell. May be null in some cases. * @param modifiers A {@link SpellModifiers} object containing the modifiers that have been applied to the spell. * See the javadoc for that class for more information. If no modifiers are required, pass in * {@code new SpellModifiers()}. * @return True if the spell succeeded, false if not. Returns false by default. */ public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ return false; } /** * Whether NPCs such as wizards can cast this spell. If you have overridden * {@link Spell#cast(World, EntityLiving, EnumHand, int, EntityLivingBase, SpellModifiers)}, you should override * this to return true. */ public boolean canBeCastByNPCs(){ return false; } /** * Whether this spell requires a packet to be sent when it is cast. Returns true by default, but can be overridden * to return false if the spell's cast() method does not use any code that must be executed client-side (i.e. * particle spawning). Does nothing for continuous spells, because they never need to send packets. *
* If in doubt, leave this method as is; it is purely an optimisation.
*
* @return false if the spell code should only be run on the server and the client of the player casting
* it
* If you are calling this from inside a loop in which you are iterating through the spells, there is probably a
* better way; see {@link Spell#getSpells(Predicate)}.
*/
public static Spell get(int id){
if(id < 0 || id >= registry.getValuesCollection().size()){
return Spells.none;
}
Spell spell = ((ForgeRegistry
* {@link Spell#allSpells} will allow all spells to be returned
* true if the spell code should be run on the server and all clients in the dimension
*/
// Edit: Turns out that swingItem() actually sends packets to all nearby clients, but not the client doing the
// swinging.
// Also, now I think about it, this method isn't going to make the slightest bit of difference to the item usage
// actions since setItemInUse() is called in ItemWand, not the spell class - so the only thing that matters here is
// the particles.
public boolean doesSpellRequirePacket(){
return true;
}
/**
* Returns this spell's id number, which now corresponds to its position in the spell registry. Returns -1 if the
* spell has not been registered.
*/
// This is final so nothing can override it, because that would cause all kinds of problems!
public final int id(){
return ((ForgeRegistry
* {@link Spell#enabledSpells} will filter out any spells that are disabled in the config
* {@link Spell#npcSpells} will only allow enabled spells that can be cast by NPCs (see
* {@link Spell#canBeCastByNPCs()})
* {@link Spell#nonContinuousSpells} will filter out continuous spells but not disabled spells
* {@link Spell.TierElementFilter} will only allow enabled spells of the specified tier and element
*
* @param filter A Predicate<Spell> that the returned spells must satisfy.
*
* @return A local, modifiable list of spells matching the given predicate. Note that this list may be
* empty.
*/
public static List