package zmaster587.advancedRocketry.atmosphere; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerChangedDimensionEvent; import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerLoggedOutEvent; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.DamageSource; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; import zmaster587.advancedRocketry.api.AreaBlob; import zmaster587.advancedRocketry.api.Configuration; import zmaster587.advancedRocketry.api.IAtmosphere; import zmaster587.advancedRocketry.api.event.AtmosphereEvent; import zmaster587.advancedRocketry.api.util.IBlobHandler; import zmaster587.advancedRocketry.dimension.DimensionManager; import zmaster587.advancedRocketry.network.PacketAtmSync; import zmaster587.advancedRocketry.util.AtmosphereBlob; import zmaster587.advancedRocketry.util.SealableBlockHandler; import zmaster587.libVulpes.network.PacketHandler; import zmaster587.libVulpes.util.BlockPosition; import java.util.HashMap; import java.util.LinkedList; import java.util.List; public class AtmosphereHandler { public static final DamageSource vacuumDamage = new DamageSource("Vacuum").setDamageBypassesArmor().setDamageIsAbsolute(); public static long lastSuffocationTime = Integer.MIN_VALUE; private static final int MAX_BLOB_RADIUS = ((Configuration.atmosphereHandleBitMask & 1) == 1) ? 256 : Configuration.oxygenVentSize; private static HashMap<Integer, AtmosphereHandler> dimensionOxygen = new HashMap<Integer, AtmosphereHandler>(); private static HashMap<EntityPlayer, IAtmosphere> prevAtmosphere = new HashMap<EntityPlayer, IAtmosphere>(); private HashMap<IBlobHandler,AreaBlob> blobs; int dimId; //Stores current Atm on the CLIENT public static IAtmosphere currentAtm; public static int currentPressure; /** * Registers the Atmosphere handler for the dimension given * @param dimId the dimension id to register the dimension for */ public static void registerWorld(int dimId) { //If O2 is allowed and if(Configuration.enableOxygen && (Configuration.overrideGCAir || dimId != Configuration.MoonId || DimensionManager.getInstance().getDimensionProperties(dimId).isNativeDimension)) { dimensionOxygen.put(dimId, new AtmosphereHandler(dimId)); MinecraftForge.EVENT_BUS.register(dimensionOxygen.get(dimId)); FMLCommonHandler.instance().bus().register(dimensionOxygen.get(dimId)); } } /** * Unregisters the Atmosphere handler for the dimension given * @param dimId the dimension id to register the dimension for */ public static void unregisterWorld(int dimId) { AtmosphereHandler handler = dimensionOxygen.remove(dimId); if(Configuration.enableOxygen && handler != null) { MinecraftForge.EVENT_BUS.unregister(handler); FMLCommonHandler.instance().bus().unregister(handler); } } private AtmosphereHandler(int dimId) { this.dimId = dimId; blobs = new HashMap<IBlobHandler,AreaBlob>(); } @SubscribeEvent public void onTick(LivingUpdateEvent event) { if(!event.entity.worldObj.isRemote && event.entity.worldObj.provider.dimensionId == this.dimId) { IAtmosphere atmosType = getAtmosphereType(event.entity); if(event.entity instanceof EntityPlayer && atmosType != prevAtmosphere.get(event.entity)) { PacketHandler.sendToPlayer(new PacketAtmSync(atmosType.getUnlocalizedName(), getAtmospherePressure(event.entity)), (EntityPlayer)event.entity); prevAtmosphere.put((EntityPlayer)event.entity, atmosType); } if(atmosType.canTick()) { AtmosphereEvent event2 = new AtmosphereEvent.AtmosphereTickEvent(event.entity, atmosType); MinecraftForge.EVENT_BUS.post(event2); if(!event2.isCanceled() && !Configuration.bypassEntity.contains(event.entity.getClass())) atmosType.onTick((EntityLivingBase)event.entityLiving); } } } @SubscribeEvent public void onPlayerChangeDim(PlayerChangedDimensionEvent event) { prevAtmosphere.remove(event.player); } @SubscribeEvent public void onPlayerLogoutEvent(PlayerLoggedOutEvent event) { prevAtmosphere.remove(event.player); } private void onBlockRemove(BlockPosition pos) { List<AreaBlob> blobs = getBlobWithinRadius(pos, MAX_BLOB_RADIUS); for(AreaBlob blob : blobs) { //Make sure that a block can actually be attached to the blob for(ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) if(blob.contains(pos.getPositionAtOffset(dir.offsetX, dir.offsetY, dir.offsetZ))) { blob.addBlock(pos, blobs); break; } } } /** * @return true if the dimension has an atmospherehandler Object associated with it */ public static boolean hasAtmosphereHandler(int dimId) { return dimensionOxygen.containsKey(dimId); } //Called from World.setBlockMetaDataWithNotify public static void onBlockMetaChange(World world, int x , int y, int z) { if(Configuration.enableOxygen && !world.isRemote && world.getChunkFromBlockCoords(x, z).isChunkLoaded) { AtmosphereHandler handler = getOxygenHandler(world.provider.dimensionId); BlockPosition pos = new BlockPosition(x, y, z); int meta = world.getBlockMetadata(x, y, z); if(handler == null) return; //WTF List<AreaBlob> nearbyBlobs = handler.getBlobWithinRadius(pos, MAX_BLOB_RADIUS); for(AreaBlob blob : nearbyBlobs) { if(blob.contains(pos) && !blob.isPositionAllowed(world, pos, nearbyBlobs)) blob.removeBlock(x, y, z); else if(!blob.contains(pos) && blob.isPositionAllowed(world, pos, nearbyBlobs)) handler.onBlockRemove(pos); else if(!blob.contains(pos) && !blob.isPositionAllowed(world, pos ,nearbyBlobs) && blob.getBlobSize() == 0) { blob.addBlock(blob.getRootPosition(), nearbyBlobs); } } } } //Called from setBlock in World.class public static void onBlockChange(World world, int x, int y, int z) { if(Configuration.enableOxygen && !world.isRemote && world.getChunkFromBlockCoords(x, z).isChunkLoaded) { BlockPosition pos = new BlockPosition(x, y, z); AtmosphereHandler handler = getOxygenHandler(world.provider.dimensionId); if(handler == null) return; //WTF List<AreaBlob> nearbyBlobs = handler.getBlobWithinRadius(pos, MAX_BLOB_RADIUS); for(AreaBlob blob : nearbyBlobs) { if(world.isAirBlock(x, y, z)) handler.onBlockRemove(pos); else { //Place block if( blob.contains(pos) && SealableBlockHandler.isFulBlock(world, pos)) { blob.removeBlock(x, y, z); } else if(!blob.contains(blob.getRootPosition())) { blob.addBlock(blob.getRootPosition(), nearbyBlobs); } } } } } /** * Gets a list of AreaBlobs within a radius * @param pos position * @param radius distance from the position to find blobs within * @return List of AreaBlobs within the radius from the position */ protected List<AreaBlob> getBlobWithinRadius(BlockPosition pos, int radius) { LinkedList<AreaBlob> list = new LinkedList<AreaBlob>(); for(AreaBlob blob : blobs.values()) { if(blob.getRootPosition().getDistance(pos) - radius <= 0) { list.add(blob); } } return list; } /** * @param dimNumber dimension number for which to get the oxygenhandler * @return the oxygen handler for the planet or null if none exists */ public static AtmosphereHandler getOxygenHandler(int dimNumber) { //Get your oxyclean! return dimensionOxygen.get(dimNumber); } /** * Registers a Blob with the atmosphere handler. * Must be called before use * @param handler IBlobHander to register with * @param x * @param y * @param z */ public void registerBlob(IBlobHandler handler, int x, int y , int z) { AreaBlob blob = blobs.get(handler); if(blob == null) { blob = new AtmosphereBlob(handler); blobs.put(handler, blob); blob.setData(AtmosphereType.PRESSURIZEDAIR); } } /** * Unregisters a blob from the atmosphere handler * @param handler IBlobHandlerObject the blob is associated with */ public void unregisterBlob(IBlobHandler handler) { blobs.remove(handler); } /** * Removes all blocks from the blob associated with this handler * @param handler the handler associated with this blob */ public void clearBlob(IBlobHandler handler) { if(blobs.containsKey(handler)) { blobs.get(handler).clearBlob(); } } /** * Adds a block to the blob * @param handler * @param x * @param y * @param z */ public void addBlock(IBlobHandler handler, int x, int y, int z){ addBlock(handler, new BlockPosition(x, y, z)); } /** * Adds a block to the blob * @param handler */ public void addBlock(IBlobHandler handler, BlockPosition pos){ AreaBlob blob = blobs.get(handler); blob.addBlock(pos, getBlobWithinRadius(pos, MAX_BLOB_RADIUS)); } /** * @param x * @param y * @param z * @return AtmosphereType at this location */ public IAtmosphere getAtmosphereType(int x, int y, int z) { if(Configuration.enableOxygen) { BlockPosition pos = new BlockPosition(x,y,z); for(AreaBlob blob : blobs.values()) { if(blob.contains(pos)) { return (IAtmosphere)blob.getData(); } } return DimensionManager.getInstance().getDimensionProperties(dimId).getAtmosphere(); } return AtmosphereType.AIR; } /** * Gets the atmosphere type at the location of this entity * @param entity the entity to check against * @return The atmosphere type this entity is inside of */ public IAtmosphere getAtmosphereType(Entity entity) { if(Configuration.enableOxygen) { BlockPosition pos = new BlockPosition((int)Math.floor(entity.posX), (int)Math.ceil(entity.posY), (int)Math.floor(entity.posZ )); for(AreaBlob blob : blobs.values()) { if(blob.contains(pos)) { return (IAtmosphere)blob.getData(); } } return DimensionManager.getInstance().getDimensionProperties(dimId).getAtmosphere(); } return AtmosphereType.AIR; } /** * @return the default atmosphere type used by this planet */ public IAtmosphere getDefaultAtmosphereType() { return DimensionManager.getInstance().getDimensionProperties(dimId).getAtmosphere(); } /** * Gets the pressure at the location of this entity * @param entity the entity to check against * @return The atmosphere pressure this entity is inside of, or -1 to use default */ public int getAtmospherePressure(Entity entity) { if(Configuration.enableOxygen) { BlockPosition pos = new BlockPosition((int)(entity.posX), (int)Math.ceil(entity.posY), (int)(entity.posZ)); for(AreaBlob blob : blobs.values()) { if(blob.contains(pos)) { return ((AtmosphereBlob)blob).getPressure(); } } } return -1; } /** * @param entity entity to check against * @return true if the entity can breathe in the this atmosphere */ public boolean canEntityBreathe(EntityLiving entity) { if(Configuration.enableOxygen) { BlockPosition pos = new BlockPosition((int)Math.floor(entity.posX), (int)Math.ceil(entity.posY), (int)Math.floor(entity.posZ)); for(AreaBlob blob : blobs.values()) { if(blob.contains(pos) && ((IAtmosphere)blob.getData()).isImmune(entity)) { return true; } } return DimensionManager.getInstance().getDimensionProperties(dimId).getAtmosphere().isImmune(entity); } return true; } /** * @param handler the handler registered to this blob * @return The current size of the blob */ public int getBlobSize(IBlobHandler handler) { return blobs.get(handler).getBlobSize(); } /** * Changes the atmosphere type of this blob * @param handler the handler for the blob * @param data the AtmosphereType to set this blob to. */ public void setAtmosphereType(IBlobHandler handler, IAtmosphere data) { blobs.get(handler).setData(data); } /** * Gets the atmosphere type of this blob * @param handler the handler for the blob */ public IAtmosphere getAtmosphereType(IBlobHandler handler) { return (IAtmosphere)blobs.get(handler).getData(); } }