/** * This class was created by <Vazkii>. It's distributed as * part of the Botania Mod. Get the Source Code in github: * https://github.com/Vazkii/Botania * * Botania is Open Source and distributed under the * Botania License: http://botaniamod.net/license.php * * File Created @ [Feb 13, 2015, 10:52:40 PM (GMT)] */ package vazkii.botania.common.entity; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; import com.google.common.base.Optional; import com.google.common.base.Predicates; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumDyeColor; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.datasync.DataParameter; import net.minecraft.network.datasync.DataSerializers; import net.minecraft.network.datasync.EntityDataManager; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RayTraceResult; import net.minecraft.world.World; import vazkii.botania.api.corporea.ICorporeaSpark; import vazkii.botania.api.corporea.InvWithLocation; import vazkii.botania.common.core.helper.InventoryHelper; import vazkii.botania.common.item.ModItems; public class EntityCorporeaSpark extends Entity implements ICorporeaSpark { private static final int SCAN_RANGE = 8; private static final String TAG_MASTER = "master"; private static final String TAG_NETWORK = "network"; private static final String TAG_INVIS = "invis"; private static final DataParameter<Boolean> MASTER = EntityDataManager.createKey(EntityCorporeaSpark.class, DataSerializers.BOOLEAN); private static final DataParameter<Integer> NETWORK = EntityDataManager.createKey(EntityCorporeaSpark.class, DataSerializers.VARINT); private static final DataParameter<Integer> ITEM_DISPLAY_TICKS = EntityDataManager.createKey(EntityCorporeaSpark.class, DataSerializers.VARINT); private static final DataParameter<ItemStack> DISPLAY_STACK = EntityDataManager.createKey(EntityCorporeaSpark.class, DataSerializers.OPTIONAL_ITEM_STACK); private ICorporeaSpark master; private List<ICorporeaSpark> connections = new ArrayList<>(); private List<ICorporeaSpark> relatives = new ArrayList<>(); private boolean firstTick = true; public EntityCorporeaSpark(World world) { super(world); isImmuneToFire = true; } @Override protected void entityInit() { setSize(0.1F, 0.5F); dataManager.register(MASTER, false); dataManager.register(NETWORK, 0); dataManager.register(ITEM_DISPLAY_TICKS, 0); dataManager.register(DISPLAY_STACK, ItemStack.EMPTY); } @Nonnull @Override public ItemStack getPickedResult(RayTraceResult target) { return isMaster() ? new ItemStack(ModItems.corporeaSpark, 1, 1) : new ItemStack(ModItems.corporeaSpark); } @Override public boolean canBeCollidedWith() { return true; } @Override public void onUpdate() { super.onUpdate(); if(world.isRemote) return; InvWithLocation inv = getSparkInventory(); if(inv == null) { dropAndKill(); return; } if(isMaster()) master = this; if(firstTick) { if(isMaster()) restartNetwork(); else findNetwork(); firstTick = false; } if(master != null && (((Entity) master).isDead || master.getNetwork() != getNetwork())) master = null; int displayTicks = getItemDisplayTicks(); if(displayTicks > 0) setItemDisplayTicks(displayTicks - 1); else if(displayTicks < 0) setItemDisplayTicks(displayTicks + 1); } private void dropAndKill() { entityDropItem(new ItemStack(ModItems.corporeaSpark, 1, isMaster() ? 1 : 0), 0F); setDead(); } @Override public void setDead() { super.setDead(); connections.remove(this); restartNetwork(); } @Override public void registerConnections(ICorporeaSpark master, ICorporeaSpark referrer, List<ICorporeaSpark> connections) { relatives.clear(); for(ICorporeaSpark spark : getNearbySparks()) { if(spark == null || connections.contains(spark) || spark.getNetwork() != getNetwork() || spark.isMaster() || ((Entity) spark).isDead) continue; connections.add(spark); relatives.add(spark); spark.registerConnections(master, this, connections); } this.master = master; this.connections = connections; } private List<ICorporeaSpark> getNearbySparks() { return (List) world.getEntitiesWithinAABB(Entity.class, new AxisAlignedBB(posX - SCAN_RANGE, posY - SCAN_RANGE, posZ - SCAN_RANGE, posX + SCAN_RANGE, posY + SCAN_RANGE, posZ + SCAN_RANGE), Predicates.instanceOf(ICorporeaSpark.class)); } private void restartNetwork() { connections = new ArrayList<>(); relatives = new ArrayList<>(); if(master != null) { ICorporeaSpark oldMaster = master; master = null; oldMaster.registerConnections(oldMaster, this, new ArrayList<>()); } } private void findNetwork() { for(ICorporeaSpark spark : getNearbySparks()) if(spark.getNetwork() == getNetwork() && !((Entity) spark).isDead) { ICorporeaSpark master = spark.getMaster(); if(master != null) { this.master = master; restartNetwork(); break; } } } private static void displayRelatives(EntityPlayer player, List<ICorporeaSpark> checked, ICorporeaSpark spark) { if(spark == null) return; List<ICorporeaSpark> sparks = spark.getRelatives(); if(sparks.isEmpty()) EntitySpark.particleBeam(player, (Entity) spark, (Entity) spark.getMaster()); else for(ICorporeaSpark endSpark : sparks) { if(!checked.contains(endSpark)) { EntitySpark.particleBeam(player, (Entity) spark, (Entity) endSpark); checked.add(endSpark); displayRelatives(player, checked, endSpark); } } } @Override public InvWithLocation getSparkInventory() { int x = MathHelper.floor(posX); int y = MathHelper.floor(posY - 1); int z = MathHelper.floor(posZ); return InventoryHelper.getInventoryWithLocation(world, new BlockPos(x, y, z), EnumFacing.UP); } @Override public List<ICorporeaSpark> getConnections() { return connections; } @Override public List<ICorporeaSpark> getRelatives() { return relatives; } @Override public void onItemExtracted(ItemStack stack) { setItemDisplayTicks(10); setDisplayedItem(stack); } @Override public void onItemsRequested(List<ItemStack> stacks) { if(!stacks.isEmpty()) { setItemDisplayTicks(-10); setDisplayedItem(stacks.get(0)); } } @Override public ICorporeaSpark getMaster() { return master; } public void setMaster(boolean master) { dataManager.set(MASTER, master); } @Override public boolean isMaster() { return dataManager.get(MASTER); } public void setNetwork(EnumDyeColor network) { dataManager.set(NETWORK, network.getMetadata()); } @Override public EnumDyeColor getNetwork() { return EnumDyeColor.byMetadata(dataManager.get(NETWORK)); } public int getItemDisplayTicks() { return dataManager.get(ITEM_DISPLAY_TICKS); } public void setItemDisplayTicks(int ticks) { dataManager.set(ITEM_DISPLAY_TICKS, ticks); } public ItemStack getDisplayedItem() { return dataManager.get(DISPLAY_STACK); } public void setDisplayedItem(ItemStack stack) { dataManager.set(DISPLAY_STACK, stack); } @Override public boolean processInitialInteract(EntityPlayer player, EnumHand hand) { ItemStack stack = player.getHeldItem(hand); if(!stack.isEmpty()) { if(player.world.isRemote) { boolean valid = stack.getItem() == ModItems.twigWand || stack.getItem() == ModItems.dye || stack.getItem() == ModItems.phantomInk; if(valid) player.swingArm(hand); return valid; } if(stack.getItem() == ModItems.twigWand) { if(player.isSneaking()) { dropAndKill(); if(isMaster()) restartNetwork(); } else { displayRelatives(player, new ArrayList<>(), master); } return true; } else if(stack.getItem() == ModItems.dye) { int color = stack.getItemDamage(); if(color != getNetwork().getMetadata()) { setNetwork(EnumDyeColor.byMetadata(color)); if(master != null) restartNetwork(); else findNetwork(); stack.shrink(1); return true; } } else if(stack.getItem() == ModItems.phantomInk) { setInvisible(true); return true; } } return false; } @Override protected void readEntityFromNBT(@Nonnull NBTTagCompound cmp) { setMaster(cmp.getBoolean(TAG_MASTER)); setNetwork(EnumDyeColor.byMetadata(cmp.getInteger(TAG_NETWORK))); setInvisible(cmp.getInteger(TAG_INVIS) == 1); } @Override protected void writeEntityToNBT(@Nonnull NBTTagCompound cmp) { cmp.setBoolean(TAG_MASTER, isMaster()); cmp.setInteger(TAG_NETWORK, getNetwork().getMetadata()); cmp.setInteger(TAG_INVIS, isInvisible() ? 1 : 0); } }