package thaumcraft.api.visnet; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.util.Vec3; import net.minecraft.world.World; import thaumcraft.api.ThaumcraftApi; import thaumcraft.api.ThaumcraftApiHelper; import thaumcraft.api.WorldCoordinates; import thaumcraft.api.aspects.Aspect; public class VisNetHandler { // NODE DRAINING /** * This method drains vis from a relay or source near the passed in * location. The amount received can be less than the amount requested so * take that into account. * * @param world * @param x the x position of the draining block or entity * @param y the y position of the draining block or entity * @param z the z position of the draining block or entity * @param aspect what aspect to drain * @param amount how much to drain * @return how much was actually drained */ public static int drainVis(World world, int x, int y, int z, Aspect aspect, int amount) { int drainedAmount = 0; WorldCoordinates drainer = new WorldCoordinates(x, y, z, world.provider.dimensionId); if (!nearbyNodes.containsKey(drainer)) { calculateNearbyNodes(world, x, y, z); } ArrayList<WeakReference<TileVisNode>> nodes = nearbyNodes.get(drainer); if (nodes!=null && nodes.size()>0) for (WeakReference<TileVisNode> noderef : nodes) { TileVisNode node = noderef.get(); if (node == null) continue; int a = node.consumeVis(aspect, amount); drainedAmount += a; amount -= a; if (a>0) { int color = Aspect.getPrimalAspects().indexOf(aspect); generateVisEffect(world.provider.dimensionId, x, y, z, node.xCoord, node.yCoord, node.zCoord, color); } if (amount <= 0) { break; } } return drainedAmount; } public static void generateVisEffect(int dim, int x, int y, int z, int x2, int y2, int z2, int color) { ThaumcraftApi.internalMethods.generateVisEffect(dim, x, y, z, x2, y2, z2, color); } public static HashMap<Integer, HashMap<WorldCoordinates, WeakReference<TileVisNode>>> sources = new HashMap<Integer, HashMap<WorldCoordinates, WeakReference<TileVisNode>>>(); public static void addSource(World world, TileVisNode vs) { HashMap<WorldCoordinates, WeakReference<TileVisNode>> sourcelist = sources .get(world.provider.dimensionId); if (sourcelist == null) { sourcelist = new HashMap<WorldCoordinates, WeakReference<TileVisNode>>(); } sourcelist.put(vs.getLocation(), new WeakReference(vs)); sources.put(world.provider.dimensionId, sourcelist); nearbyNodes.clear(); } public static boolean isNodeValid(WeakReference<TileVisNode> node) { if (node == null || node.get() == null || node.get().isInvalid()) return false; return true; } public static WeakReference<TileVisNode> addNode(World world, TileVisNode vn) { WeakReference ref = new WeakReference(vn); HashMap<WorldCoordinates, WeakReference<TileVisNode>> sourcelist = sources .get(world.provider.dimensionId); if (sourcelist == null) { sourcelist = new HashMap<WorldCoordinates, WeakReference<TileVisNode>>(); return null; } ArrayList<Object[]> nearby = new ArrayList<Object[]>(); for (WeakReference<TileVisNode> root : sourcelist.values()) { if (!isNodeValid(root)) continue; TileVisNode source = root.get(); float r = inRange(world, vn.getLocation(), source.getLocation(), vn.getRange()); if (r > 0) { nearby.add(new Object[] { source, r - vn.getRange() * 2 }); } nearby = findClosestNodes(vn, source, nearby); cache.clear(); } float dist = Float.MAX_VALUE; TileVisNode closest = null; if (nearby.size() > 0) { for (Object[] o : nearby) { if ((Float) o[1] < dist && (vn.getAttunement() == -1 || ((TileVisNode) o[0]).getAttunement() == -1 || vn.getAttunement() == ((TileVisNode) o[0]).getAttunement())//) { && canNodeBeSeen(vn,(TileVisNode)o[0])) { dist = (Float) o[1]; closest = (TileVisNode) o[0]; } } } if (closest != null) { closest.getChildren().add(ref); nearbyNodes.clear(); return new WeakReference(closest); } return null; } static ArrayList<WorldCoordinates> cache = new ArrayList<WorldCoordinates>(); public static ArrayList<Object[]> findClosestNodes(TileVisNode target, TileVisNode parent, ArrayList<Object[]> in) { if (cache.size() > 512 || cache.contains(new WorldCoordinates(parent))) return in; cache.add(new WorldCoordinates(parent)); for (WeakReference<TileVisNode> childWR : parent.getChildren()) { TileVisNode child = childWR.get(); if (child != null && !child.equals(target) && !child.equals(parent)) { float r2 = inRange(child.getWorldObj(), child.getLocation(), target.getLocation(), target.getRange()); if (r2 > 0) { in.add(new Object[] { child, r2 }); } in = findClosestNodes(target, child, in); } } return in; } private static float inRange(World world, WorldCoordinates cc1, WorldCoordinates cc2, int range) { float distance = cc1.getDistanceSquaredToWorldCoordinates(cc2); return distance > range * range ? -1 : distance; } private static HashMap<WorldCoordinates, ArrayList<WeakReference<TileVisNode>>> nearbyNodes = new HashMap<WorldCoordinates, ArrayList<WeakReference<TileVisNode>>>(); private static void calculateNearbyNodes(World world, int x, int y, int z) { HashMap<WorldCoordinates, WeakReference<TileVisNode>> sourcelist = sources .get(world.provider.dimensionId); if (sourcelist == null) { sourcelist = new HashMap<WorldCoordinates, WeakReference<TileVisNode>>(); return; } ArrayList<WeakReference<TileVisNode>> cn = new ArrayList<WeakReference<TileVisNode>>(); WorldCoordinates drainer = new WorldCoordinates(x, y, z, world.provider.dimensionId); ArrayList<Object[]> nearby = new ArrayList<Object[]>(); for (WeakReference<TileVisNode> root : sourcelist.values()) { if (!isNodeValid(root)) continue; TileVisNode source = root.get(); TileVisNode closest = null; float range = Float.MAX_VALUE; float r = inRange(world, drainer, source.getLocation(), source.getRange()); if (r > 0) { range = r; closest = source; } ArrayList<WeakReference<TileVisNode>> children = new ArrayList<WeakReference<TileVisNode>>(); children = getAllChildren(source,children); for (WeakReference<TileVisNode> child : children) { TileVisNode n = child.get(); if (n != null && !n.equals(root)) { float r2 = inRange(n.getWorldObj(), n.getLocation(), drainer, n.getRange()); if (r2 > 0 && r2 < range) { range = r2; closest = n; } } } if (closest != null) { cn.add(new WeakReference(closest)); } } nearbyNodes.put(drainer, cn); } private static ArrayList<WeakReference<TileVisNode>> getAllChildren(TileVisNode source, ArrayList<WeakReference<TileVisNode>> list) { for (WeakReference<TileVisNode> child : source.getChildren()) { TileVisNode n = child.get(); if (n != null && n.getWorldObj()!=null && isChunkLoaded(n.getWorldObj(), n.xCoord, n.zCoord)) { list.add(child); list = getAllChildren(n,list); } } return list; } public static boolean isChunkLoaded(World world, int x, int z) { int xx = x >> 4; int zz = z >> 4; return world.getChunkProvider().chunkExists(xx, zz); } public static boolean canNodeBeSeen(TileVisNode source,TileVisNode target) { MovingObjectPosition mop = ThaumcraftApiHelper.rayTraceIgnoringSource(source.getWorldObj(), Vec3.createVectorHelper(source.xCoord+.5, source.yCoord+.5,source.zCoord+.5), Vec3.createVectorHelper(target.xCoord+.5, target.yCoord+.5,target.zCoord+.5), false, true, false); return mop == null || (mop.typeOfHit==MovingObjectType.BLOCK && mop.blockX==target.xCoord && mop.blockY==target.yCoord && mop.blockZ==target.zCoord); } // public static HashMap<WorldCoordinates,WeakReference<TileVisNode>> // noderef = new HashMap<WorldCoordinates,WeakReference<TileVisNode>>(); // // public static TileVisNode getClosestNodeWithinRadius(World world, int x, // int y, int z, int radius) { // TileVisNode out = null; // WorldCoordinates wc = null; // float cd = Float.MAX_VALUE; // for (int sx = x - radius; sx <= x + radius; sx++) { // for (int sy = y - radius; sy <= y + radius; sy++) { // for (int sz = z - radius; sz <= z + radius; sz++) { // wc = new WorldCoordinates(sx,sy,sz,world.provider.dimensionId); // if (noderef.containsKey(wc)) { // float d = wc.getDistanceSquared(x, y, z); // if (d<radius*radius && noderef.get(wc).get()!=null && // !noderef.get(wc).get().isReceiver() && // isNodeValid(noderef.get(wc).get().getParent()) // ) { // out = noderef.get(wc).get(); // cd = d; // } // } // } // } // } // return out; // } }