Add chainable method for assigning spell NPC selector

This commit is contained in:
Electroblob77
2020-01-27 17:51:32 +00:00
parent ba83cb7985
commit d37ab6072f
9 changed files with 73 additions and 53 deletions
@@ -43,6 +43,7 @@ import net.minecraftforge.registries.IForgeRegistryEntry;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -150,8 +151,16 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
/** The pitch variation of the sound played when this spell is cast. Defaults to 0. */
protected float pitchVariation = 0;
// The following two fields are supposed to eliminate the need for boilerplate classes in spell packs.
// As an example, spells in the twilight forest spell pack only appear on custom spell book and scroll items and
// NPCs can only cast them if they spawned in the twilight forest. Without these convenience fields, spells that
// would not otherwise require their own classes have to have one just so they can override those two behaviours.
/** List of items for which this spell is applicable (used by default behaviour of {@link Spell#applicableForItem(Item)}). */
protected Item[] applicableItems;
/** Predicate that specifies a condition that NPCs must satisfy in order to spawn with this spell equipped (used by
* default behaviour of {@link Spell#canBeCastBy(EntityLiving, boolean)}). */
protected BiPredicate<EntityLiving, Boolean> npcSelector; // Kinda ugly but it's better than boilerplate classes
private static int nextSpellId = 0;
/** The spell's integer ID, mainly used for networking. */
@@ -191,6 +200,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
this.sounds = createSounds();
this.id = nextSpellId++;
this.items(WizardryItems.spell_book, WizardryItems.scroll);
this.npcSelector((e, o) -> canBeCastByNPCs()); // Fallback to old behaviour until we remove it entirely
}
// ========================================= Initialisation methods ===========================================
@@ -454,13 +464,17 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
/**
* Whether the given entity can cast this spell. If you have overridden
* {@link Spell#cast(World, EntityLiving, EnumHand, int, EntityLivingBase, SpellModifiers)}, you should override
* this to return true (either always or under certain circumstances).
* this to return true (either always or under certain circumstances), or alternatively assign an NPC selector via
* {@link Spell#npcSelector(BiPredicate)} (recommended for general spell classes).
* @param npc The entity to query.
* @param override True if a player in creative mode is assigning this spell the given entity, false otherwise.
* @param override True if a player in creative mode is assigning this spell to the given entity, false otherwise.
* Usually this means situational conditions should be ignored.
*/
// We could make this final and force everyone to move over to the predicate system, but for particularly complex
// behaviour (i.e. several lines of code) it gets too ugly, and then you end up moving the contents of the predicate
// to a static method anyway and referring to it via method reference... so we may as well leave people the option.
public boolean canBeCastBy(EntityLiving npc, boolean override){
return canBeCastByNPCs();
return npcSelector.test(npc, override);
}
/**
@@ -592,37 +606,6 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
return properties.getBaseValue(identifier);
}
/** Returns whether the spell is enabled in any of the given {@link electroblob.wizardry.util.SpellProperties.Context Context}s.
* A spell may be disabled globally in the config, or it may be disabled for one or more specific contexts in
* its JSON file using a resource pack. If called with no arguments, defaults to any context, i.e. only returns
* false if the spell is completely disabled in all contexts. */
public final boolean isEnabled(SpellProperties.Context... contexts){
return enabled && (contexts.length == 0 || properties.isEnabled(contexts));
}
/** Sets whether the spell is enabled or not. */
public final void setEnabled(boolean isEnabled){
this.enabled = isEnabled;
}
/** Returns true if the given item has a variant for this spell. By default, returns true if the given item is
* in this spell's {@link Spell#applicableItems} list (set using {@link Spell#items(Item...)}). Override to do
* something more complex. */
public boolean applicableForItem(Item item){
return Arrays.asList(applicableItems).contains(item);
}
/**
* Sets which items this spell can appear on (these default to the regular spell book and scroll).
* @param applicableItems The items this spell should naturally appear on (or no items at all).
* @return The spell instance, allowing this method to be chained onto the constructor. Note that since this method
* only returns a {@code Spell}, if you are chaining multiple methods onto the constructor this should be called last.
*/
public Spell items(Item... applicableItems){
this.applicableItems = applicableItems;
return this;
}
/**
* Returns the unlocalised name of the spell, without any prefixes or suffixes, e.g. "flame_ray". <b>This should
* only be used for translation purposes.</b>
@@ -809,6 +792,51 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
}
}
// ============================================= Misc methods ===============================================
/** Returns whether the spell is enabled in any of the given {@link electroblob.wizardry.util.SpellProperties.Context Context}s.
* A spell may be disabled globally in the config, or it may be disabled for one or more specific contexts in
* its JSON file using a resource pack. If called with no arguments, defaults to any context, i.e. only returns
* false if the spell is completely disabled in all contexts. */
public final boolean isEnabled(SpellProperties.Context... contexts){
return enabled && (contexts.length == 0 || properties.isEnabled(contexts));
}
/** Sets whether the spell is enabled or not. */
public final void setEnabled(boolean isEnabled){
this.enabled = isEnabled;
}
/** Returns true if the given item has a variant for this spell. By default, returns true if the given item is
* in this spell's {@link Spell#applicableItems} list (set using {@link Spell#items(Item...)}). Override to do
* something more complex. */
public boolean applicableForItem(Item item){
return Arrays.asList(applicableItems).contains(item);
}
/**
* Sets which items this spell can appear on (these default to the regular spell book and scroll).
* @param applicableItems The items this spell should naturally appear on (or no items at all).
* @return The spell instance, allowing this method to be chained onto the constructor. Note that since this method
* only returns a {@code Spell}, if you are chaining multiple methods onto the constructor this should be called last.
*/
public Spell items(Item... applicableItems){
this.applicableItems = applicableItems;
return this;
}
/**
* Specifies a condition that NPCs must satisfy in order to spawn with this spell equipped (this defaults to always
* true).
* @param selector A condition that NPCs must satisfy in order to spawn with this spell equipped.
* @return The spell instance, allowing this method to be chained onto the constructor. Note that since this method
* only returns a {@code Spell}, if you are chaining multiple methods onto the constructor this should be called last.
*/
public Spell npcSelector(BiPredicate<EntityLiving, Boolean> selector){
this.npcSelector = selector;
return this;
}
// Spells are sorted according to tier and element. Where several spells have the same tier and element,
// they will remain in the order they were registered.
@Override