diff --git a/src/main/java/cr0s/warpdrive/render/RenderOverlayAir.java b/src/main/java/cr0s/warpdrive/render/RenderOverlayAir.java index 97bcdf7b..fd76236b 100644 --- a/src/main/java/cr0s/warpdrive/render/RenderOverlayAir.java +++ b/src/main/java/cr0s/warpdrive/render/RenderOverlayAir.java @@ -1,8 +1,13 @@ package cr0s.warpdrive.render; import cr0s.warpdrive.BreathingManager; +import cr0s.warpdrive.api.ExceptionChunkNotLoaded; +import cr0s.warpdrive.block.breathing.BlockAirShield; import cr0s.warpdrive.data.CelestialObjectManager; import cr0s.warpdrive.data.CelestialObject; +import cr0s.warpdrive.data.StateAir; + +import javax.annotation.Nonnull; import javax.annotation.Nonnull; @@ -10,14 +15,19 @@ import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Gui; import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.BlockPos.MutableBlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; import net.minecraftforge.client.GuiIngameForge; import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType; +import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -49,11 +59,7 @@ public class RenderOverlayAir { } // get air stats - final boolean hasVoidNearby = isVoid(entityPlayer.world, x, y, z) - || isVoid(entityPlayer.world, x - 2, y, z) - || isVoid(entityPlayer.world, x + 2, y, z) - || isVoid(entityPlayer.world, x, y, z - 2) - || isVoid(entityPlayer.world, x, y, z + 2); + final int rangeToVoid = getRangeToVoid(entityPlayer, x, y, z); final boolean hasValidSetup = BreathingManager.hasValidSetup(entityPlayer); final float ratioAirReserve = BreathingManager.getAirReserveRatio(entityPlayer); @@ -62,7 +68,8 @@ public class RenderOverlayAir { // show splash message int alpha = 255; - if (hasVoidNearby || entityPlayer.ticksExisted < WARNING_ON_JOIN_TICKS) { + if ( rangeToVoid >= 0 + || entityPlayer.ticksExisted < WARNING_ON_JOIN_TICKS ) { if (!hasValidSetup) { alpha = RenderCommons.drawSplashAlarm(width, height, "warpdrive.breathing.alarm", "warpdrive.breathing.invalid_setup"); } else if (ratioAirReserve <= 0.0F) { @@ -119,10 +126,60 @@ public class RenderOverlayAir { GlStateManager.disableBlend(); } - private boolean isVoid(final IBlockAccess blockAccess, final int x, final int y, final int z) { - final BlockPos blockPos = new BlockPos(x, y, z); - final IBlockState blockState = blockAccess.getBlockState(blockPos); - return blockState.getBlock().isAir(blockState, blockAccess, blockPos) && !BreathingManager.isAirBlock(blockState.getBlock()); + private int cache_rangeToVoid = -1; + private int cache_ticksVoidCheck = -1; + private int getRangeToVoid(@Nonnull final EntityLivingBase entityLivingBase, final int x, final int y, final int z) { + if (entityLivingBase.ticksExisted == cache_ticksVoidCheck) { + return cache_rangeToVoid; + } + cache_rangeToVoid = getRangeToVoid_noCache(entityLivingBase.world, x, y, z); + cache_ticksVoidCheck = entityLivingBase.ticksExisted; + return cache_rangeToVoid; + } + private int getRangeToVoid_noCache(@Nonnull final World world, final int x, final int y, final int z) { + final MutableBlockPos mutableBlockPos = new MutableBlockPos(x, y, z); + final IBlockState blockStateSelf = world.getBlockState(mutableBlockPos); + if (isVoid(blockStateSelf, world, mutableBlockPos)) { + return 0; + } + for (final EnumFacing enumFacing : EnumFacing.HORIZONTALS) { + // check the block directly next + mutableBlockPos.setPos(x + enumFacing.getXOffset(), + y + enumFacing.getYOffset(), + z + enumFacing.getZOffset() ); + final IBlockState blockStateClose = world.getBlockState(mutableBlockPos); + if (isVoid(blockStateClose, world, mutableBlockPos)) { + return 1; + } + final StateAir stateAirClose = new StateAir(null); + try { + stateAirClose.refresh(world, mutableBlockPos.getX(), mutableBlockPos.getY(), mutableBlockPos.getZ()); + } catch (final ExceptionChunkNotLoaded exceptionChunkNotLoaded) { + return -1; + } + + // skip if we're sealed, not in an air lock + final boolean isNonAirShieldSealer = !stateAirClose.isAir() + && !(blockStateClose.getBlock() instanceof BlockAirShield); + if (isNonAirShieldSealer) { + continue; + } + + // check 1 more block away + mutableBlockPos.setPos(x + 2 * enumFacing.getXOffset(), + y + 2 * enumFacing.getYOffset(), + z + 2 * enumFacing.getZOffset() ); + final IBlockState blockStateFar = world.getBlockState(mutableBlockPos); + if (isVoid(blockStateFar, world, mutableBlockPos)) { + return 2; + } + } + return -1; + } + + private boolean isVoid(@Nonnull final IBlockState blockState, @Nonnull final IBlockAccess blockAccess, final BlockPos blockPos) { + return blockState.getBlock().isAir(blockState, blockAccess, blockPos) + && !BreathingManager.isAirBlock(blockState.getBlock()); } @SubscribeEvent