package electroblob.wizardry.data; import electroblob.wizardry.Wizardry; import electroblob.wizardry.event.SpellCastEvent; import electroblob.wizardry.packet.PacketDispenserCastSpell; import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.registry.Spells; import electroblob.wizardry.spell.None; import electroblob.wizardry.spell.Spell; import electroblob.wizardry.util.NBTExtras; import electroblob.wizardry.util.SpellModifiers; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.INBTSerializable; import net.minecraftforge.fml.common.network.simpleimpl.IMessage; /** * Base class for {@link DispenserCastingData}. Originally this was written because command blocks had a similar system, * but that was later removed in favour of spell emitters - however, this class has been kept so that others can use it * for different spellcasting blocks if they wish. * * @since Wizardry 4.2 * @author Electroblob */ public abstract class BlockCastingData implements INBTSerializable { /** The tile entity this BlockCastingData instance belongs to. */ protected final T tileEntity; /** The continuous spell this tile entity is currently casting, or the {@link None} spell if it is not casting. */ protected Spell spell; /** The coordinates of the current continuous spell's origin. */ protected double x, y, z; /** The time for which this tile entity has been casting a continuous spell. Increments by 1 each tick. */ protected int castingTick; /** SpellModifiers object for the current continuous spell. */ protected SpellModifiers modifiers; public BlockCastingData(T tileEntity){ this.tileEntity = tileEntity; this.spell = Spells.none; this.modifiers = new SpellModifiers(); this.castingTick = 0; } /** Returns whether this tile entity is currently casting a continuous spell. */ public boolean isCasting(){ return this.spell != null && this.spell != Spells.none; } /** Returns the continuous spell this tile entity is currently casting, or the {@link None} spell if it isn't * casting anything. */ public Spell currentlyCasting(){ return spell; } /** Starts casting the given continuous spell from this tile entity. */ protected void startCasting(Spell spell, double x, double y, double z, SpellModifiers modifiers){ if(!spell.isContinuous){ Wizardry.logger.warn("Tried to start casting a continuous spell from a tile entity, but the given spell was not continuous!"); return; } this.spell = spell; this.x = x; this.y = y; this.z = z; this.castingTick = 0; this.modifiers = modifiers; } /** Stops casting the current spell. */ protected void stopCasting(){ this.spell = Spells.none; this.castingTick = 0; this.modifiers.reset(); } /** Stops casting the current spell and sends a packet to clients to update them. If called client-side, this just * delegates to {@link BlockCastingData#stopCasting()}. */ protected void stopCastingAndNotify(){ stopCasting(); if(!tileEntity.getWorld().isRemote){ IMessage msg = new PacketDispenserCastSpell.Message(x, y, z, getDirection(), tileEntity.getPos(), spell, 0, modifiers); WizardryPacketHandler.net.sendToDimension(msg, tileEntity.getWorld().provider.getDimension()); } } /** Called once per tick to update the block casting data. This is not called automatically, subclasses must * do so using their own tick event handlers. */ protected void update(){ if(this.tileEntity.isInvalid()){ return; } if(this.isCasting() && this.spell.isContinuous){ // If the dispenser has stopped receiving power, the spell stops immediately. if(!shouldContinueCasting()){ this.stopCasting(); // This seems to work fine on both sides, so no point sending a packet return; } EnumFacing direction = getDirection(); if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(getSource(), spell, tileEntity.getWorld(), x, y, z, direction, modifiers, castingTick))){ // When the event is canceled client-side, this will stop the spell on the client only, as specified in // the javadoc for SpellCastEvent.Tick. this.stopCastingAndNotify(); return; } this.spell.cast(tileEntity.getWorld(), x, y, z, direction, castingTick, -1, modifiers); castingTick++; }else{ this.castingTick = 0; } } /** Returns the direction to cast the current spell in. */ protected abstract EnumFacing getDirection(); /** Returns the source of spells cast from this block. */ protected abstract SpellCastEvent.Source getSource(); /** Called each tick during continuous spell casting to determine if the spell should continue or stop. */ protected abstract boolean shouldContinueCasting(); @Override public NBTTagCompound serializeNBT(){ NBTTagCompound nbt = new NBTTagCompound(); nbt.setInteger("spell", spell.metadata()); nbt.setInteger("castingTick", castingTick); NBTExtras.storeTagSafely(nbt, "modifiers", modifiers.toNBT()); return nbt; } @Override public void deserializeNBT(NBTTagCompound nbt){ if(nbt != null){ this.spell = Spell.byMetadata(nbt.getInteger("spell")); this.castingTick = nbt.getInteger("castingTick"); this.modifiers = SpellModifiers.fromNBT(nbt.getCompoundTag("modifiers")); } } // The two methods below broke EVERYTHING, somehow they made the server think it was the client... // // Only fired server-side // @SubscribeEvent // public static void onWorldTickEvent(TickEvent.WorldTickEvent event){ // // if(!event.world.isRemote && event.phase == TickEvent.Phase.END){ // // This will fire once for each dimension, but since we want dispenser-casting to work in all dimensions, // // this is correct (the loaded tile entity list will of course be different in each case. // this.update(); // } // } // // // Only called client-side // @SubscribeEvent // public static void onClientTickEvent(TickEvent.ClientTickEvent event){ // World world = net.minecraft.client.Minecraft.getMinecraft().world; // if(event.phase == TickEvent.Phase.END && !net.minecraft.client.Minecraft.getMinecraft().isGamePaused() // && world != null){ // this.update(); // } // } }