@Override
 public void handleFairyUpgrade(EntityItem item, EntityPlayer player, TileEntityDungeonCore core) {
   if (PlayerUtils.hasItem(player, ZSSItems.swordMasterTrue)) {
     item.setDead();
     player.triggerAchievement(ZSSAchievements.shieldMirror);
     WorldUtils.spawnItemWithRandom(
         core.getWorldObj(),
         new ItemStack(ZSSItems.shieldMirror),
         core.xCoord,
         core.yCoord + 2,
         core.zCoord);
     core.getWorldObj()
         .playSoundEffect(
             core.xCoord + 0.5D,
             core.yCoord + 1,
             core.zCoord + 0.5D,
             Sounds.SECRET_MEDLEY,
             1.0F,
             1.0F);
   } else {
     core.getWorldObj()
         .playSoundEffect(
             core.xCoord + 0.5D,
             core.yCoord + 1,
             core.zCoord + 0.5D,
             Sounds.FAIRY_LAUGH,
             1.0F,
             1.0F);
     PlayerUtils.sendTranslatedChat(player, "chat.zss.fairy.laugh.sword");
   }
 }
 /** This event fires on BOTH sides */
 @SubscribeEvent
 public void onInteract(EntityInteractEvent event) {
   ItemStack stack = event.entityPlayer.getHeldItem();
   if (event.target instanceof EntityVillager
       && Result.DEFAULT
           != NpcHelper.convertVillager(event.entityPlayer, (EntityVillager) event.target, true)) {
     event.setCanceled(true);
   }
   // Check if the held item has any special interaction upon right-clicking an entity
   if (!event.isCanceled() && stack != null && stack.getItem() instanceof IRightClickEntity) {
     event.setCanceled(
         ((IRightClickEntity) stack.getItem())
             .onRightClickEntity(stack, event.entityPlayer, event.target));
   }
   // If the event is not yet canceled, check for Mask interactions
   if (!event.isCanceled() && event.target instanceof INpc) {
     ItemStack helm = event.entityPlayer.getCurrentArmor(ArmorIndex.WORN_HELM);
     if (helm != null && helm.getItem() instanceof ItemMask) {
       event.setCanceled(
           ((ItemMask) helm.getItem()).onInteract(helm, event.entityPlayer, event.target));
     }
   }
   // Finally, check for interactions with the Cursed Man
   if (!event.isCanceled()
       && event.target.getClass() == EntityVillager.class
       && ("Cursed Man").equals(((EntityVillager) event.target).getCustomNameTag())) {
     EntityVillager villager = (EntityVillager) event.target;
     if (stack == null
         || (stack.getItem() != ZSSItems.skulltulaToken && stack.getItem() != Items.name_tag)) {
       int tokens = ZSSPlayerInfo.get(event.entityPlayer).getSkulltulaTokens();
       if (villager.worldObj.isRemote) {
         // don't send chat - will be sent from server
       } else if (villager.isChild()) {
         PlayerUtils.sendTranslatedChat(event.entityPlayer, "chat.zss.npc.cursed_man.child");
       } else if (tokens > 0) {
         PlayerUtils.sendTranslatedChat(
             event.entityPlayer, "chat.zss.npc.cursed_man.amount", tokens);
       } else {
         PlayerUtils.sendTranslatedChat(
             event.entityPlayer,
             "chat.zss.npc.cursed_man." + event.entity.worldObj.rand.nextInt(4));
       }
       event.setCanceled(true);
     }
   }
 }
 /**
  * Called when left-clicking a villager with the item in hand
  *
  * @param stack The player's currently held item (stack.getItem() is 'this')
  */
 protected void handleTrade(ItemStack stack, EntityPlayer player, EntityVillager villager) {
   MerchantRecipeList trades = villager.getRecipes(player);
   if (villager.isChild()) {
     PlayerUtils.sendTranslatedChat(player, "chat.zss.trade.generic.child");
   } else if (villager.getClass() != EntityVillager.class) {
     PlayerUtils.sendTranslatedChat(player, "chat.zss.trade.generic.sorry.0");
   } else if (trades != null && sellPrice > 0) {
     MerchantRecipe trade =
         new MerchantRecipe(stack.copy(), new ItemStack(Items.emerald, sellPrice));
     if (player.worldObj.rand.nextFloat() < 0.2F
         && MerchantRecipeHelper.addToListWithCheck(trades, trade)) {
       PlayerUtils.sendTranslatedChat(player, "chat.zss.trade.generic.sell.0");
     } else {
       PlayerUtils.sendTranslatedChat(player, "chat.zss.trade.generic.sorry.1");
     }
   } else {
     PlayerUtils.sendTranslatedChat(player, "chat.zss.trade.generic.sorry.0");
   }
 }
 @Override
 public boolean onBlockActivated(
     World world,
     int x,
     int y,
     int z,
     EntityPlayer player,
     int side,
     float hitX,
     float hitY,
     float hitZ) {
   int meta = world.getBlockMetadata(x, y, z);
   ItemStack stack = player.getHeldItem();
   if (stack != null && stack.getItem() instanceof ItemInstrument) {
     AbstractZeldaSong song = warpBlockSongs.get(meta);
     ZSSPlayerSongs songs = ZSSPlayerSongs.get(player);
     if (!world.isRemote) {
       if (song != null) { // && songs.isSongKnown(song)) { // otherwise have to click again after
         // learning the song
         songs.onActivatedWarpStone(x, y, z, meta);
         PlayerUtils.sendTranslatedChat(
             player,
             "chat.zss.block.warp_stone.activate",
             new ChatComponentTranslation(song.getTranslationString()),
             x,
             y,
             z);
       }
     } else if (!player.isSneaking()) {
       if (song != null) {
         songs.songToLearn = song;
         player.openGui(ZSSMain.instance, GuiHandler.GUI_LEARN_SONG, player.worldObj, x, y, z);
       } else {
         ZSSMain.logger.warn(
             String.format(
                 "Warp stone at %d/%d/%d had invalid metadata: did not return a song!", x, y, z));
       }
     }
     return true;
   } else {
     // TODO play failure sound
   }
   return false;
 }