/**
  * Called when the shield blocks an attack when held in the normal fashion (i.e. non-BG2) used by
  * Deku Shield to damage / destroy the stack and by Mirror Shield to reflect projectiles
  *
  * @return Return the amount of damage remaining, if any; 0 cancels the hurt event
  */
 public float onBlock(EntityPlayer player, ItemStack shield, DamageSource source, float damage) {
   ZSSPlayerInfo.get(player).onAttackBlocked(shield, damage);
   WorldUtils.playSoundAtEntity(player, Sounds.HAMMER, 0.4F, 0.5F);
   float damageBlocked = damage;
   if (toolMaterial == ToolMaterial.WOOD) {
     if (source.isProjectile()
         && !source.isExplosion()
         && source.getSourceOfDamage() instanceof IProjectile) {
       if (ZSSMain.isBG2Enabled
           && player.getHeldItem() == shield
           && shield.getItem() instanceof IArrowCatcher) {
         if (((IArrowCatcher) shield.getItem())
             .catchArrow(shield, player, (IProjectile) source.getSourceOfDamage())) {
           ((InventoryPlayerBattle) player.inventory).hasChanged = true;
         }
       }
     } else if (source instanceof IDamageAoE && ((IDamageAoE) source).isAoEDamage()) {
       damageBlocked *= magicReduction;
     }
     int dmg = Math.round(source.isFireDamage() ? damage + 10.0F : damage - 2.0F);
     if (dmg > 0) {
       shield.damageItem(dmg, player);
       if (shield.stackSize <= 0) {
         ForgeEventFactory.onPlayerDestroyItem(player, shield);
         if (ZSSMain.isBG2Enabled && BattlegearUtils.isPlayerInBattlemode(player)) {
           BattlegearUtils.setPlayerOffhandItem(player, null);
         } else {
           player.destroyCurrentEquippedItem();
         }
       }
     }
   } else if (toolMaterial == ToolMaterial.EMERALD) {
     if (source.isProjectile() && !source.isExplosion() && source.getSourceOfDamage() != null) {
       float chance = (source.isMagicDamage() ? (1F / 3F) : 1.0F);
       if (source.getSourceOfDamage() instanceof IReflectable) {
         ((IReflectable) source.getSourceOfDamage())
             .getReflectChance(shield, player, source.getEntity());
       }
       if (player.worldObj.rand.nextFloat() < chance) {
         Entity projectile = null;
         try {
           projectile =
               source
                   .getSourceOfDamage()
                   .getClass()
                   .getConstructor(World.class)
                   .newInstance(player.worldObj);
         } catch (Exception e) {;
         }
         if (projectile != null) {
           NBTTagCompound data = new NBTTagCompound();
           source.getSourceOfDamage().writeToNBT(data);
           projectile.readFromNBT(data);
           projectile.getEntityData().setBoolean("isReflected", true);
           projectile.posX -= projectile.motionX;
           projectile.posY -= projectile.motionY;
           projectile.posZ -= projectile.motionZ;
           double motionX =
               (double)
                   (-MathHelper.sin(player.rotationYaw / 180.0F * (float) Math.PI)
                       * MathHelper.cos(player.rotationPitch / 180.0F * (float) Math.PI));
           double motionZ =
               (double)
                   (MathHelper.cos(player.rotationYaw / 180.0F * (float) Math.PI)
                       * MathHelper.cos(player.rotationPitch / 180.0F * (float) Math.PI));
           double motionY =
               (double) (-MathHelper.sin(player.rotationPitch / 180.0F * (float) Math.PI));
           TargetUtils.setEntityHeading(
               projectile,
               motionX,
               motionY,
               motionZ,
               1.0F,
               2.0F + (20.0F * player.worldObj.rand.nextFloat()),
               false);
           if (projectile instanceof IReflectable) {
             ((IReflectable) projectile)
                 .onReflected(shield, player, source.getEntity(), source.getSourceOfDamage());
           }
           player.worldObj.spawnEntityInWorld(projectile);
         }
       } else if (source.isUnblockable()
           || (source instanceof IDamageAoE
               && ((IDamageAoE) source).isAoEDamage())) { // failed to reflect projectile
         damageBlocked *= magicReduction;
       }
     }
   } else if (source.isUnblockable()
       || (source instanceof IDamageAoE && ((IDamageAoE) source).isAoEDamage())) {
     damageBlocked *=
         magicReduction; // default shield behavior blocks half damage from AoE magic attacks
   }
   return (damage - damageBlocked);
 }
  @Override
  public void updateWeather() {
    super.updateWeather();

    if (!this.worldObj.isRemote) {
      if (this.dataNotLoaded) {
        this.savefile = OrbitSpinSaveData.initWorldData(this.worldObj);
        this.readFromNBT(this.savefile.datacompound);
        if (ConfigManagerCore.enableDebug)
          System.out.println(
              "Loading data from save: " + this.savefile.datacompound.getFloat("omegaSky"));
        this.dataNotLoaded = false;
      }

      if (this.doSpinning) {
        boolean updateNeeded = true;
        if (this.angularVelocityTarget < this.angularVelocityRadians) {
          float newAngle = this.angularVelocityRadians - this.angularVelocityAccel;
          if (newAngle < this.angularVelocityTarget) {
            newAngle = this.angularVelocityTarget;
          }
          this.setSpinRate(newAngle);
          this.thrustersFiring = true;
        } else if (this.angularVelocityTarget > this.angularVelocityRadians) {
          float newAngle = this.angularVelocityRadians + this.angularVelocityAccel;
          if (newAngle > this.angularVelocityTarget) {
            newAngle = this.angularVelocityTarget;
          }
          this.setSpinRate(newAngle);
          this.thrustersFiring = true;
        } else if (this.thrustersFiring) {
          this.thrustersFiring = false;
        } else {
          updateNeeded = false;
        }

        if (updateNeeded) {
          this.writeToNBT(this.savefile.datacompound);
          this.savefile.markDirty();

          List<Object> objList = new ArrayList<Object>();
          objList.add(Float.valueOf(this.angularVelocityRadians));
          objList.add(Boolean.valueOf(this.thrustersFiring));
          GalacticraftCore.packetPipeline.sendToDimension(
              new PacketSimple(EnumSimplePacket.C_UPDATE_STATION_SPIN, objList),
              this.spaceStationDimensionID);
        }

        // Update entity positions if in freefall
        this.loadedEntities.clear();
        this.loadedEntities.addAll(this.worldObj.loadedEntityList);
        for (Entity e : this.loadedEntities) {
          if ((e instanceof EntityItem
                  || e instanceof EntityLivingBase && !(e instanceof EntityPlayer)
                  || e instanceof EntityTNTPrimed
                  || e instanceof EntityFallingBlock)
              && !e.onGround) {
            boolean freefall = true;
            if (e.boundingBox.maxX >= this.ssBoundsMinX
                && e.boundingBox.minX <= this.ssBoundsMaxX
                && e.boundingBox.maxY >= this.ssBoundsMinY
                && e.boundingBox.minY <= this.ssBoundsMaxY
                && e.boundingBox.maxZ >= this.ssBoundsMinZ
                && e.boundingBox.minZ <= this.ssBoundsMaxZ) {
              // Entity is somewhere within the space station boundaries

              // Check if the entity's bounding box is in the same block coordinates as any
              // non-vacuum block (including torches etc)
              // If so, it's assumed the entity has something close enough to catch onto, so is not
              // in freefall
              // Note: breatheable air here means the entity is definitely not in freefall
              int xmx = MathHelper.floor_double(e.boundingBox.maxX + 0.2D);
              int ym = MathHelper.floor_double(e.boundingBox.minY - 0.1D);
              int yy = MathHelper.floor_double(e.boundingBox.maxY + 0.1D);
              int zm = MathHelper.floor_double(e.boundingBox.minZ - 0.2D);
              int zz = MathHelper.floor_double(e.boundingBox.maxZ + 0.2D);
              BLOCKCHECK:
              for (int x = MathHelper.floor_double(e.boundingBox.minX - 0.2D); x <= xmx; x++) {
                for (int y = ym; y <= yy; y++) {
                  for (int z = zm; z <= zz; z++) {
                    if (this.worldObj.blockExists(x, y, z)
                        && this.worldObj.getBlock(x, y, z) != Blocks.air) {
                      freefall = false;
                      break BLOCKCHECK;
                    }
                  }
                }
              }
            }

            if (freefall) {
              // Do the rotation
              if (this.angularVelocityRadians != 0F) {
                float angle;
                final double xx = e.posX - this.spinCentreX;
                final double zz = e.posZ - this.spinCentreZ;
                double arc = Math.sqrt(xx * xx + zz * zz);
                if (xx == 0D) {
                  angle = zz > 0 ? 3.141592536F / 2 : -3.141592536F / 2;
                } else {
                  angle = (float) Math.atan(zz / xx);
                }
                if (xx < 0D) {
                  angle += 3.141592536F;
                }
                angle += this.angularVelocityRadians / 3F;
                arc = arc * this.angularVelocityRadians;
                final double offsetX = -arc * MathHelper.sin(angle);
                final double offsetZ = arc * MathHelper.cos(angle);
                e.posX += offsetX;
                e.posZ += offsetZ;
                e.lastTickPosX += offsetX;
                e.lastTickPosZ += offsetZ;

                // Rotated into an unloaded chunk (probably also drifted out to there): byebye
                if (!this.worldObj.blockExists(
                    MathHelper.floor_double(e.posX), 64, MathHelper.floor_double(e.posZ))) {
                  e.setDead();
                }

                e.boundingBox.offset(offsetX, 0.0D, offsetZ);
                // TODO check for block collisions here - if so move the entity appropriately and
                // apply fall damage
                // Moving the entity = slide along / down
                e.rotationYaw += this.skyAngularVelocity;
                while (e.rotationYaw > 360F) {
                  e.rotationYaw -= 360F;
                }
              }

              // Undo deceleration
              if (e instanceof EntityLivingBase) {
                e.motionX /= 0.91F;
                e.motionZ /= 0.91F;
                if (e instanceof EntityFlying) {
                  e.motionY /= 0.91F;
                } else if (e instanceof EntityFallingBlock) {
                  e.motionY /= 0.9800000190734863D;
                  // e.motionY += 0.03999999910593033D;
                  // e.posY += 0.03999999910593033D;
                  // e.lastTickPosY += 0.03999999910593033D;
                } else {
                  e.motionY /= 0.9800000190734863D;
                }
              } else {
                e.motionX /= 0.9800000190734863D;
                e.motionY /= 0.9800000190734863D;
                e.motionZ /= 0.9800000190734863D;
              }
            }
          }
        }
      }
    }
  }