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:
Electroblob77
2020-01-27 23:27:56 +00:00
parent aa166814ea
commit e4bf09f15a
9 changed files with 69 additions and 43 deletions
@@ -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);
}
}