package makeo.gadomancy.common.blocks.tiles; import cpw.mods.fml.common.network.NetworkRegistry; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import makeo.gadomancy.client.util.UtilsFX; import makeo.gadomancy.common.entities.EntityPermNoClipItem; import makeo.gadomancy.common.network.PacketHandler; import makeo.gadomancy.common.network.packets.PacketStartAnimation; import makeo.gadomancy.common.utils.NBTHelper; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.command.IEntitySelector; import net.minecraft.entity.Entity; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import thaumcraft.api.aspects.Aspect; import thaumcraft.api.aspects.AspectList; import thaumcraft.api.aspects.IAspectContainer; import thaumcraft.api.research.ResearchCategories; import thaumcraft.api.research.ResearchItem; import thaumcraft.common.entities.EntityPermanentItem; import thaumcraft.common.entities.EntitySpecialItem; import thaumcraft.common.items.ItemResearchNotes; import thaumcraft.common.lib.events.EssentiaHandler; import thaumcraft.common.lib.research.ResearchManager; import thaumcraft.common.lib.research.ResearchNoteData; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; /** * [email protected] * Date: 19.04.2016 / 14:53 * on Gadomancy * TileKnowledgeBook */ public class TileKnowledgeBook extends SynchronizedTileEntity implements EntityPermNoClipItem.IItemMasterTile, IAspectContainer { private static final Random rand = new Random(); private static final int LOWEST_AMOUNT = 10; private static final int COGNITIO_TICKS = 150; private static final int MAX_NEEDED_KNOWLEDGE = 200; private static final int SURROUNDINGS_SEARCH_XZ = 4; private static final int SURROUNDINGS_SEARCH_Y = 3; private static final double MULTIPLIER = 4; @Deprecated public static Map<BlockSnapshot, Integer> knowledgeIncreaseMap = new HashMap<BlockSnapshot, Integer>(); private FloatingBookAttributes bookAttributes = new FloatingBookAttributes(); //The itemstack this is connected to. private ItemStack storedResearchNote = null; private EntityPermNoClipItem.ItemChangeTask scheduledTask = null; //Ticks private int timeSinceLastItemInfo = 0; private int ticksExisted = 0; private int ticksCognitio = 0; //Sound effect stuff. I didn't like it when it's like changing pages 4 times in a row... private boolean turnedPagesLastTick = false; //General research stuff private boolean researching = false; private AspectList workResearchAspects = null; private int surroundingKnowledge = 0; @Override public void updateEntity() { bookAttributes.updateFloatingBook(); ticksExisted++; timeSinceLastItemInfo++; if (!worldObj.isRemote) { if(timeSinceLastItemInfo > 8) { informItemRemoval(); } if(updateResearchStatus()) { worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); markDirty(); } if(researching) { doResearchCycle(); } } else { if(researching && hasCognitio()) { doResearchEffects(); } } if (storedResearchNote == null) { tryVortexUnfinishedResearchNotes(); } } public boolean hasCognitio() { return ticksCognitio > 0; } public boolean isResearching() { return researching; } private void doResearchCycle() { drainCognitio(); if(!hasCognitio()) { return; } checkSurroundings(); int chance = Math.max(0, MAX_NEEDED_KNOWLEDGE - this.surroundingKnowledge) + 100; if(rand.nextInt(chance) == 0) { doResearchProgress(); worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); markDirty(); } } private void doResearchProgress() { List<Aspect> aspects = new ArrayList<Aspect>(this.workResearchAspects.aspects.keySet()); if(aspects.isEmpty()) { finishResearch(); return; } Aspect a = aspects.get(rand.nextInt(aspects.size())); int value = this.workResearchAspects.aspects.get(a); value--; if(value <= 0) { this.workResearchAspects.aspects.remove(a); } else { this.workResearchAspects.aspects.put(a, value); } if(this.workResearchAspects.aspects.isEmpty()) { finishResearch(); } } private void checkSurroundings() { this.surroundingKnowledge = 0; for (int xx = -SURROUNDINGS_SEARCH_XZ; xx <= SURROUNDINGS_SEARCH_XZ; xx++) { for (int zz = -SURROUNDINGS_SEARCH_XZ; zz <= SURROUNDINGS_SEARCH_XZ; zz++) { lblYLoop: for (int yy = -SURROUNDINGS_SEARCH_Y; yy <= SURROUNDINGS_SEARCH_Y; yy++) { int absX = xx + xCoord; int absY = yy + yCoord; int absZ = zz + zCoord; Block at = worldObj.getBlock(absX, absY, absZ); int meta = worldObj.getBlockMetadata(absX, absY, absZ); TileEntity te = worldObj.getTileEntity(absX, absY, absZ); if(at.equals(Blocks.bookshelf)) { this.surroundingKnowledge += 1; } else if(te != null && te instanceof IKnowledgeProvider) { this.surroundingKnowledge += ((IKnowledgeProvider) te).getProvidedKnowledge(worldObj, absX, absY, absZ); } else if(at instanceof IKnowledgeProvider) { this.surroundingKnowledge += ((IKnowledgeProvider) at).getProvidedKnowledge(worldObj, absX, absY, absZ); } else { for (BlockSnapshot sn : knowledgeIncreaseMap.keySet()) { if(sn.block.equals(at) && sn.metadata == meta) { this.surroundingKnowledge += knowledgeIncreaseMap.get(sn); continue lblYLoop; } } } } } } } private void drainCognitio() { if(ticksCognitio <= 0) { searchForCognitio(); } else { ticksCognitio--; if(ticksCognitio <= 40) { searchForCognitio(); } if(ticksCognitio <= 0) { worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); markDirty(); } } } private void searchForCognitio() { if((ticksExisted & 31) == 0) { int drainRange = 4; ForgeDirection[] toTry = ForgeDirection.VALID_DIRECTIONS; for (ForgeDirection dir : toTry) { if(dir == null) continue; //LUL should not happen... if(EssentiaHandler.drainEssentia(this, Aspect.MIND, dir, drainRange)) { ticksCognitio += COGNITIO_TICKS; worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); markDirty(); break; } } } } @SideOnly(Side.CLIENT) private void doResearchEffects() { if((ticksExisted & 15) == 0) { UtilsFX.doRuneEffects(Minecraft.getMinecraft().theWorld, xCoord, yCoord - 1, zCoord, 0); } if((ticksExisted & 31) == 0) { switch (rand.nextInt(5)) { case 0: if(!turnedPagesLastTick) { worldObj.playSound(xCoord + 0.5, yCoord + 0.3, zCoord + 0.5, "thaumcraft:page", 0.4F, 1.0F, false); turnedPagesLastTick = true; break; } case 1: case 2: case 3: worldObj.playSound(xCoord + 0.5, yCoord + 0.3, zCoord + 0.5, "thaumcraft:write", 0.2F, 1.0F, false); turnedPagesLastTick = false; break; } } } private void finishResearch() { stopResearch(); scheduleFinishItemChange(); PacketStartAnimation packet = new PacketStartAnimation(PacketStartAnimation.ID_SPARKLE_SPREAD, xCoord, yCoord, zCoord); PacketHandler.INSTANCE.sendToAllAround(packet, getTargetPoint(32)); worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); markDirty(); } private NetworkRegistry.TargetPoint getTargetPoint(double radius) { return new NetworkRegistry.TargetPoint(worldObj.provider.dimensionId, xCoord, yCoord, zCoord, radius); } private void scheduleFinishItemChange() { scheduledTask = new EntityPermNoClipItem.ItemChangeTask() { @Override public void changeItem(EntityPermNoClipItem item) { ItemStack stack = item.getEntityItem(); stack.stackTagCompound.setBoolean("complete", true); stack.setItemDamage(64); } }; } //Returns true, if updating/sync is required. private boolean updateResearchStatus() { if(researching) { if(storedResearchNote != null) { ResearchNoteData nd = ResearchManager.getData(storedResearchNote); if(nd == null || nd.isComplete()) { stopResearch(); return true; } } else { stopResearch(); return true; } } else { if(storedResearchNote != null) { ResearchNoteData nd = ResearchManager.getData(storedResearchNote); if(nd != null && !nd.isComplete()) { ResearchItem ri = ResearchCategories.getResearch(nd.key); if(ri != null) { beginResearch(ri.tags); return true; } } } } return false; } private void beginResearch(AspectList researchTags) { AspectList workResearchList = new AspectList(); for (Aspect a : researchTags.aspects.keySet()) { int value = researchTags.aspects.get(a); int newVal = (int) Math.max(LOWEST_AMOUNT, ((double) value) * MULTIPLIER); workResearchList.add(a, newVal); } this.workResearchAspects = workResearchList; this.researching = true; } private void stopResearch() { this.storedResearchNote = null; this.workResearchAspects = null; this.researching = false; } @Override public boolean canStillHoldItem() { return true; } @Override public void informMaster() { this.timeSinceLastItemInfo = 0; } @Override public void informItemRemoval() { this.storedResearchNote = null; stopResearch(); worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); markDirty(); } @Override public EntityPermNoClipItem.ItemChangeTask getAndRemoveScheduledChangeTask() { if(scheduledTask != null) { EntityPermNoClipItem.ItemChangeTask buffer = scheduledTask; scheduledTask = null; return buffer; } return null; } @Override public void broadcastItemStack(ItemStack itemStack) { this.storedResearchNote = itemStack; } private void tryVortexUnfinishedResearchNotes() { float centerY = yCoord + 0.4F; List entityItems = worldObj.selectEntitiesWithinAABB(EntityItem.class, AxisAlignedBB.getBoundingBox(xCoord - 0.5, centerY - 0.5, zCoord - 0.5, xCoord + 0.5, centerY + 0.5, zCoord + 0.5).expand(8, 8, 8), new IEntitySelector() { @Override public boolean isEntityApplicable(Entity e) { return !(e instanceof EntityPermanentItem) && !(e instanceof EntitySpecialItem) && e instanceof EntityItem && ((EntityItem) e).getEntityItem() != null && ((EntityItem) e).getEntityItem().getItem() instanceof ItemResearchNotes && shouldVortexResearchNote(((EntityItem) e).getEntityItem()); } }); Entity dummy = new EntityItem(worldObj); dummy.posX = xCoord + 0.5; dummy.posY = centerY + 0.5; dummy.posZ = zCoord + 0.5; //MC code. EntityItem entity = null; double d0 = Double.MAX_VALUE; for (Object entityItem : entityItems) { EntityItem entityIt = (EntityItem) entityItem; if (entityIt != dummy) { double d1 = dummy.getDistanceSqToEntity(entityIt); if (d1 <= d0) { entity = entityIt; d0 = d1; } } } if(entity == null) return; if(dummy.getDistanceToEntity(entity) < 1 && !worldObj.isRemote) { ItemStack inter = entity.getEntityItem(); inter.stackSize--; this.storedResearchNote = inter.copy(); this.storedResearchNote.stackSize = 1; EntityPermNoClipItem item = new EntityPermNoClipItem(entity.worldObj, xCoord + 0.5F, centerY + 0.3F, zCoord + 0.5F, storedResearchNote, xCoord, yCoord, zCoord); entity.worldObj.spawnEntityInWorld(item); item.motionX = 0; item.motionY = 0; item.motionZ = 0; item.hoverStart = entity.hoverStart; item.age = entity.age; item.noClip = true; timeSinceLastItemInfo = 0; if(inter.stackSize <= 0) entity.setDead(); entity.noClip = false; item.delayBeforeCanPickup = 60; worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); markDirty(); } else { entity.noClip = true; applyMovementVectors(entity); } } //Special to masterTile only! private void applyMovementVectors(EntityItem entity) { double var3 = (this.xCoord + 0.5D - entity.posX) / 15.0D; double var5 = (this.yCoord + 0.5D - entity.posY) / 15.0D; double var7 = (this.zCoord + 0.5D - entity.posZ) / 15.0D; double var9 = Math.sqrt(var3 * var3 + var5 * var5 + var7 * var7); double var11 = 1.0D - var9; if (var11 > 0.0D) { var11 *= var11; entity.motionX += var3 / var9 * var11 * 0.15D; entity.motionY += var5 / var9 * var11 * 0.25D; entity.motionZ += var7 / var9 * var11 * 0.15D; } } private boolean shouldVortexResearchNote(ItemStack stack) { ResearchNoteData nd = ResearchManager.getData(stack); if(nd == null) return false; if(nd.isComplete()) return false; ResearchItem ri = ResearchCategories.getResearch(nd.key); return ri != null; } public FloatingBookAttributes getBookAttributes() { return bookAttributes; } @Override public void readCustomNBT(NBTTagCompound compound) { this.ticksCognitio = compound.getInteger("cognitio"); this.workResearchAspects = NBTHelper.getAspectList(compound, "workAspects"); this.researching = compound.getBoolean("researching"); this.storedResearchNote = NBTHelper.getStack(compound, "crystalStack"); } @Override public void writeCustomNBT(NBTTagCompound compound) { if(workResearchAspects != null) { NBTHelper.setAspectList(compound, "workAspects", workResearchAspects); } compound.setInteger("cognitio", ticksCognitio); compound.setBoolean("researching", researching); if(storedResearchNote != null) NBTHelper.setStack(compound, "crystalStack", storedResearchNote); } @Override public AspectList getAspects() { return workResearchAspects; } @Override public void setAspects(AspectList list) {} @Override public boolean doesContainerAccept(Aspect aspect) { return false; } @Override public int addToContainer(Aspect aspect, int i) { return 0; } @Override public boolean takeFromContainer(Aspect aspect, int i) { return false; } @Override public boolean takeFromContainer(AspectList list) { return false; } @Override public boolean doesContainerContainAmount(Aspect aspect, int i) { return false; } @Override public boolean doesContainerContain(AspectList list) { return false; } @Override public int containerContains(Aspect aspect) { return 0; } public static class BlockSnapshot { public final Block block; public final int metadata; public BlockSnapshot(Block block, int metadata) { this.block = block; this.metadata = metadata; } } public static interface IKnowledgeProvider { public int getProvidedKnowledge(World world, int blockX, int blockY, int blockZ); } public class FloatingBookAttributes { //Ugh. EnchantmentTable stuff... I DON'T UNDERSTAND THIS FLOATING BOOK //Well not that i tried... copy pasta please. public int field_145926_a; public float field_145933_i; public float field_145931_j; public float field_145932_k; public float field_145929_l; public float field_145930_m; public float field_145927_n; public float field_145928_o; public float field_145925_p; public float field_145924_q; private void updateFloatingBook() { this.field_145927_n = this.field_145930_m; this.field_145925_p = this.field_145928_o; EntityPlayer entityplayer = TileKnowledgeBook.this.worldObj.getClosestPlayer((double)((float)TileKnowledgeBook.this.xCoord + 0.5F), (double)((float)TileKnowledgeBook.this.yCoord + 0.5F), (double)((float)TileKnowledgeBook.this.zCoord + 0.5F), 3.0D); if (entityplayer != null) { double d0 = entityplayer.posX - (double)((float)TileKnowledgeBook.this.xCoord + 0.5F); double d1 = entityplayer.posZ - (double)((float)TileKnowledgeBook.this.zCoord + 0.5F); this.field_145924_q = (float)Math.atan2(d1, d0); this.field_145930_m += 0.1F; if (this.field_145930_m < 0.5F || rand.nextInt(40) == 0) { float f1 = this.field_145932_k; do { this.field_145932_k += (float)(rand.nextInt(4) - rand.nextInt(4)); } while (f1 == this.field_145932_k); } } else { this.field_145924_q += 0.02F; this.field_145930_m -= 0.1F; } while (this.field_145928_o >= (float)Math.PI) { this.field_145928_o -= ((float)Math.PI * 2F); } while (this.field_145928_o < -(float)Math.PI) { this.field_145928_o += ((float)Math.PI * 2F); } while (this.field_145924_q >= (float)Math.PI) { this.field_145924_q -= ((float)Math.PI * 2F); } while (this.field_145924_q < -(float)Math.PI) { this.field_145924_q += ((float)Math.PI * 2F); } float f2; for (f2 = this.field_145924_q - this.field_145928_o; f2 >= (float)Math.PI; f2 -= ((float)Math.PI * 2F)) {} while (f2 < -(float)Math.PI) { f2 += ((float)Math.PI * 2F); } this.field_145928_o += f2 * 0.4F; if (this.field_145930_m < 0.0F) { this.field_145930_m = 0.0F; } if (this.field_145930_m > 1.0F) { this.field_145930_m = 1.0F; } ++this.field_145926_a; this.field_145931_j = this.field_145933_i; float f = (this.field_145932_k - this.field_145933_i) * 0.4F; float f3 = 0.2F; if (f < -f3) { f = -f3; } if (f > f3) { f = f3; } this.field_145929_l += (f - this.field_145929_l) * 0.9F; this.field_145933_i += this.field_145929_l; } } }