Implement client-side spell counter for NPCs, fixes #345
Also makes all the continuous-spell-related methods in ISpellCaster default methods so they don't need implementing unless actually necessary.
This commit is contained in:
@@ -347,18 +347,21 @@ public final class WizardryEventHandler {
|
||||
|
||||
Spell spell = ((ISpellCaster)event.getEntity()).getContinuousSpell();
|
||||
SpellModifiers modifiers = ((ISpellCaster)event.getEntity()).getModifiers();
|
||||
int count = ((ISpellCaster)event.getEntity()).getSpellCounter();
|
||||
|
||||
if(spell != null && spell != Spells.none){ // IntelliJ is wrong, do NOT remove the null check!
|
||||
|
||||
if(!MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(SpellCastEvent.Source.NPC, spell, event.getEntityLiving(),
|
||||
modifiers, 0))){
|
||||
modifiers, count))){
|
||||
|
||||
spell.cast(event.getEntity().world, (EntityLiving)event.getEntity(), EnumHand.MAIN_HAND, 0,
|
||||
spell.cast(event.getEntity().world, (EntityLiving)event.getEntity(), EnumHand.MAIN_HAND, count,
|
||||
// TODO: This implementation of modifiers relies on them being accessible client-side.
|
||||
// Right now that doesn't matter because NPCs don't use modifiers, but they might in future
|
||||
((EntityLiving)event.getEntity()).getAttackTarget(), modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
((ISpellCaster)event.getEntity()).setSpellCounter(count + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,6 +434,7 @@ public class ClientProxy extends CommonProxy {
|
||||
if(caster instanceof ISpellCaster){
|
||||
if(spell.isContinuous || spell instanceof None){
|
||||
((ISpellCaster)caster).setContinuousSpell(spell);
|
||||
((ISpellCaster)caster).setSpellCounter(spell instanceof None ? 0 : 1);
|
||||
((EntityLiving)caster).setAttackTarget((EntityLivingBase)target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +213,7 @@ public class EntityAIAttackSpell<T extends EntityLiving & ISpellCaster> extends
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is only called when spell casting starts so ticksInUse is always zero
|
||||
if(spell.cast(attacker.world, attacker, EnumHand.MAIN_HAND, 0, target, modifiers)){
|
||||
|
||||
if(spell.isContinuous){
|
||||
|
||||
@@ -74,6 +74,7 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
|
||||
// Field implementations
|
||||
private List<Spell> spells = new ArrayList<Spell>(4);
|
||||
private Spell continuousSpell;
|
||||
private int spellCounter;
|
||||
|
||||
public EntityEvilWizard(World world){
|
||||
|
||||
@@ -175,6 +176,16 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
|
||||
public Spell getContinuousSpell(){
|
||||
return this.continuousSpell;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpellCounter(int count){
|
||||
spellCounter = count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpellCounter(){
|
||||
return spellCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAimingError(EnumDifficulty difficulty){
|
||||
|
||||
@@ -30,6 +30,7 @@ public class EntityPhoenix extends EntitySummonedCreature implements ISpellCaste
|
||||
private EntityAIAttackSpell<EntityPhoenix> spellAttackAI = new EntityAIAttackSpell<>(this, AISpeed, 15f, 60, 140);
|
||||
|
||||
private Spell continuousSpell;
|
||||
private int spellCounter;
|
||||
|
||||
private static final List<Spell> attack = Collections.singletonList(Spells.flame_ray);
|
||||
|
||||
@@ -76,6 +77,16 @@ public class EntityPhoenix extends EntitySummonedCreature implements ISpellCaste
|
||||
continuousSpell = spell;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpellCounter(int count){
|
||||
spellCounter = count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpellCounter(){
|
||||
return spellCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRangedAttack(){
|
||||
return true;
|
||||
|
||||
@@ -61,21 +61,6 @@ public class EntityShadowWraith extends EntitySummonedCreature implements ISpell
|
||||
return attack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellModifiers getModifiers(){
|
||||
return new SpellModifiers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spell getContinuousSpell(){
|
||||
return Spells.none;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContinuousSpell(Spell spell){
|
||||
// Doesn't use continuous spells.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEntityAttributes(){
|
||||
super.applyEntityAttributes();
|
||||
|
||||
@@ -59,21 +59,6 @@ public class EntityStormElemental extends EntitySummonedCreature implements ISpe
|
||||
return attack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellModifiers getModifiers(){
|
||||
return new SpellModifiers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spell getContinuousSpell(){
|
||||
return Spells.none;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContinuousSpell(Spell spell){
|
||||
// Doesn't use continuous spells.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEntityAttributes(){
|
||||
super.applyEntityAttributes();
|
||||
|
||||
@@ -83,6 +83,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
|
||||
// Field implementations
|
||||
private List<Spell> spells = new ArrayList<Spell>(4);
|
||||
private Spell continuousSpell;
|
||||
private int spellCounter;
|
||||
|
||||
/** A set of the positions of the blocks that are part of this wizard's tower. */
|
||||
private Set<BlockPos> towerBlocks;
|
||||
@@ -187,7 +188,17 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
|
||||
public Spell getContinuousSpell(){
|
||||
return this.continuousSpell;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setSpellCounter(int count){
|
||||
spellCounter = count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpellCounter(){
|
||||
return spellCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAimingError(EnumDifficulty difficulty){
|
||||
// Being more intelligent than skeletons, wizards are a little more accurate.
|
||||
|
||||
@@ -19,7 +19,9 @@ import java.util.List;
|
||||
* packets) is handled by that class, and all the implementor needs to do is decide which spell(s) to select.
|
||||
* <p></p>
|
||||
* This class also allows Wizardry to do all the syncing necessary for continuous spell casting. All the implementor
|
||||
* needs to do is store the actual fields involved.
|
||||
* needs to do is store the actual fields involved, by implementing {@link ISpellCaster#setContinuousSpell(Spell)},
|
||||
* {@link ISpellCaster#getContinuousSpell()}, {@link ISpellCaster#setSpellCounter(int)}, {@link ISpellCaster#getSpellCounter()}
|
||||
* and {@link ISpellCaster#getModifiers()}.
|
||||
*/
|
||||
/* Perhaps this should be a capability? Though I can't help thinking they're mainly for attaching data to vanilla
|
||||
* classes, rather than custom ones. For now, the main purpose of this is to centralise code within wizardry itself, and
|
||||
@@ -39,7 +41,7 @@ public interface ISpellCaster {
|
||||
* list <b>must</b> be castable by NPCs (i.e. {@link Spell#canBeCastBy(net.minecraft.entity.EntityLiving, boolean)} returns true).
|
||||
*/
|
||||
@Nonnull
|
||||
public List<Spell> getSpells();
|
||||
List<Spell> getSpells();
|
||||
|
||||
/**
|
||||
* Called each time the entity attacks to get the modifiers to apply to the spell.
|
||||
@@ -48,31 +50,47 @@ public interface ISpellCaster {
|
||||
* required, pass in an empty {@code SpellModifiers} object.
|
||||
*/
|
||||
@Nonnull
|
||||
public SpellModifiers getModifiers();
|
||||
default SpellModifiers getModifiers(){
|
||||
return new SpellModifiers(); // May seem wasteful but this should never be called so it doesn't matter
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the continuous spell that is currently being cast, or the None spell if there is none. Implementors
|
||||
* should simply store this as a private field and return it here. Will be synced by the AI class, but whether it is
|
||||
* saved to NBT is up to you. If the implementing class does not deal with continuous spells, just return
|
||||
* {@link Spells#none}. If the implementing class only ever uses one continuous spell, do <b>not</b> just return
|
||||
* that spell; the field must still be stored.
|
||||
* saved to NBT is up to you. If the implementing class only ever uses one continuous spell, do <b>not</b> just
|
||||
* return that spell; the field must still be stored.
|
||||
*/
|
||||
@Nonnull
|
||||
public Spell getContinuousSpell();
|
||||
default Spell getContinuousSpell(){
|
||||
return Spells.none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the continuous spell that is currently being cast, or the None spell if there is none. Implementors should
|
||||
* simply store this as a private field and assign it here. Will be synced by the AI class, but whether it is saved
|
||||
* to NBT is up to you. If the implementing class does not deal with continuous spells, leave this method blank.
|
||||
*/
|
||||
public void setContinuousSpell(Spell spell);
|
||||
default void setContinuousSpell(Spell spell){
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/** Returns the number of ticks the current spell has been cast for. Implementors should simply store this as a
|
||||
* private field and return it here. This is only used client-side. */
|
||||
default int getSpellCounter(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Sets the number of ticks the current spell has been cast for. Implementors should simply store this as a
|
||||
* private field and assign it here. This is only used client-side. */
|
||||
default void setSpellCounter(int count){
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the aiming error for the given difficulty, used in projectile spells. Defaults to the values used by
|
||||
* skeletons, which are: Easy - 10, Normal - 6, Hard - 2, Peaceful - 10 (rarely used).
|
||||
*/
|
||||
// This is what default methods are actually intended for!
|
||||
public default int getAimingError(EnumDifficulty difficulty) {
|
||||
default int getAimingError(EnumDifficulty difficulty) {
|
||||
return WizardryUtilities.getDefaultAimingError(difficulty);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user