/** Copyright (C) <2016> <coolAlias> This file is part of coolAlias' Zelda Sword Skills Minecraft Mod; as such, you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package zeldaswordskills.entity.npc; import java.util.UUID; import net.minecraft.entity.passive.EntityVillager; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.BlockPos; import net.minecraft.util.IChatComponent; import net.minecraft.village.MerchantRecipe; import net.minecraft.village.MerchantRecipeList; import net.minecraft.world.World; import net.minecraftforge.common.util.Constants; import net.minecraftforge.fml.common.eventhandler.Event.Result; import zeldaswordskills.api.entity.INpcVillager; import zeldaswordskills.api.entity.ISongTeacher; import zeldaswordskills.entity.player.ZSSPlayerSongs; import zeldaswordskills.entity.player.quests.IQuest; import zeldaswordskills.entity.player.quests.IQuestHandler; import zeldaswordskills.entity.player.quests.QuestBase; import zeldaswordskills.entity.player.quests.QuestLightArrows; import zeldaswordskills.entity.player.quests.QuestMasterSword; import zeldaswordskills.entity.player.quests.QuestPendants; import zeldaswordskills.entity.player.quests.QuestZeldaTalk; import zeldaswordskills.entity.player.quests.QuestZeldasLetter; import zeldaswordskills.entity.player.quests.ZSSQuests; import zeldaswordskills.item.ItemInstrument; import zeldaswordskills.item.ZSSItems; import zeldaswordskills.network.PacketDispatcher; import zeldaswordskills.network.bidirectional.PlayRecordPacket; import zeldaswordskills.network.client.SyncQuestPacket; import zeldaswordskills.songs.ZeldaSongs; import zeldaswordskills.util.MerchantRecipeHelper; import zeldaswordskills.util.PlayerUtils; import com.google.common.collect.ImmutableSet; public class EntityNpcZelda extends EntityNpcMerchantBase implements INpcVillager, IQuestHandler, ISongTeacher { /** List of quests, in order, which involve interacting with Zelda */ private static final ImmutableSet<Class<? extends IQuest>> QUEST_LIST = new ImmutableSet.Builder<Class<? extends IQuest>>() .add(QuestZeldaTalk.class) .add(QuestPendants.class) .add(QuestZeldasLetter.class) .add(QuestMasterSword.class) .add(QuestLightArrows.class) .build(); private static final MerchantRecipe lightArrows = new MerchantRecipe(new ItemStack(Items.emerald, 8), null, new ItemStack(ZSSItems.arrowLight), 0, 99); /** Set when Zelda first converting from a villager and used to end the song */ private int conversionTime; /** Ocarina-owner's UUID, used only for returning the ocarina immediately after converting */ private UUID ocarinaOwnerId; /** Position at which the song is playing, if any */ private BlockPos songPos; public EntityNpcZelda(World world) { super(world); } @Override protected String getNameTagOnSpawn() { return "Princess Zelda"; } @Override protected String getLivingSound() { return null; // TODO Sounds.PRINCESS_HAGGLE; } @Override protected String getHurtSound() { return null; // TODO Sounds.PRINCESS_HIT; } @Override protected String getDeathSound() { return null; // TODO Sounds.PRINCESS_DEATH; } @Override public MerchantRecipeList getRecipes(EntityPlayer player) { MerchantRecipeList list = super.getRecipes(player); if (list != null) { MerchantRecipeHelper.removeTrade(list, lightArrows, false, true); if (ZSSQuests.get(player).hasCompleted(QuestLightArrows.class)) { list.add(0, new MerchantRecipe(lightArrows.getItemToBuy(), lightArrows.getSecondItemToBuy(), lightArrows.getItemToSell(), 1, 99)); } } return list; } @Override protected void populateTradingList() { if (trades == null) { trades = new MerchantRecipeList(); } updateTradingList(); } @Override protected void updateTradingList() { // TODO add new trades from the vanilla lists, if possible if (trades != null && trades.isEmpty()) { trades.add(new MerchantRecipe(new ItemStack(Items.emerald, 2), new ItemStack(Items.potato, 3))); } } @Override public void onQuestBegun(IQuest quest, EntityPlayer player) { if (quest instanceof QuestZeldaTalk) { onConverted(player); // begins Zelda's 'transformation' process } } @Override public void onQuestChanged(IQuest quest, EntityPlayer player) {} @Override public void onQuestCompleted(IQuest quest, EntityPlayer player) {} @Override public boolean interact(EntityPlayer player) { if (!isEntityAlive() || player.isSneaking()) { return false; } else if (getCustomer() != null) { if (!worldObj.isRemote) { PlayerUtils.sendTranslatedChat(player, "chat.zss.npc.merchant.busy"); } return true; } else if (conversionTime > 0) { return true; // prevent any further interaction while 'converting' } ZSSQuests quests = ZSSQuests.get(player); ItemStack stack = player.getHeldItem(); if (stack != null && stack.getItem() instanceof ItemInstrument && (worldObj.isRemote || quests.hasBegun(QuestZeldaTalk.class))) { return true; // prevent Item#onItemRightClick so song GUI doesn't open, but still allow quest to begin on server } else if (worldObj.isRemote) { return false; } if (quests.get(QuestZeldaTalk.class) == null) { quests.add(new QuestZeldaTalk()); } for (Class<? extends IQuest> clazz : EntityNpcZelda.QUEST_LIST) { IQuest quest = quests.get(clazz); if (quest != null && QuestBase.checkQuestProgress(player, quest, this)) { return true; } } updateTradingList(); displayTradingGuiFor(player); return true; } @Override public Result canInteractConvert(EntityPlayer player, EntityVillager villager) { if (villager.getClass() != EntityVillager.class || villager.isChild()) { return Result.DEFAULT; } ZSSQuests quests = ZSSQuests.get(player); QuestZeldaTalk quest = (QuestZeldaTalk) quests.get(QuestZeldaTalk.class); if (quest == null) { // can be null if someone else converted the villager quest = new QuestZeldaTalk(); quests.add(quest); } // Even though the held item is checked in #begin, it needs to be checked here as well ItemStack stack = player.getHeldItem(); if (stack != null && stack.getItem() instanceof ItemInstrument && ((ItemInstrument) stack.getItem()).getInstrument(stack) == ItemInstrument.Instrument.OCARINA_FAIRY) { // allow if player already gave ocarina to Zelda, whether quest completed yet or not if (player.worldObj.isRemote) { ZSSPlayerSongs.get(player).preventSongGui = true; // prevent song GUI from opening when converting the Princess return Result.DENY; } else if (quest.hasBegun(player)) { conversionTime = -1; // skips quest intro, since player already went through it return Result.ALLOW; } else if (quest.begin(player)) { return Result.ALLOW; } } // If quest hasn't been completed, try to give a hint to the player if (!quest.isComplete(player)) { IChatComponent hint = quest.getHint(player); if (hint != null) { player.addChatMessage(hint); return Result.DENY; } } return Result.DEFAULT; } @Override public Result canLeftClickConvert(EntityPlayer player, EntityVillager villager) { return Result.DEFAULT; } @Override public void onConverted(EntityPlayer player) { if (conversionTime < 0) { conversionTime = 0; // auto-conversion of villager by player that has already done so } else { setCustomer(player); this.ocarinaOwnerId = player.getUniqueID(); this.conversionTime = ZeldaSongs.songTime.getMinDuration() / 2; this.songPos = new BlockPos(this); PacketDispatcher.sendToAllAround(new PlayRecordPacket(ZeldaSongs.songTime.getSoundString(), songPos), this, 64.0D); } } @Override public void onLivingUpdate() { super.onLivingUpdate(); if (conversionTime > 0) { --conversionTime; if (conversionTime == 0) { if (songPos != null) { PacketDispatcher.sendToAllAround(new PlayRecordPacket(null, songPos), this, 64.0D); songPos = null; } EntityPlayer player = getCustomer(); if (player == null) { player = worldObj.getClosestPlayerToEntity(this, 16); } if (!worldObj.isRemote && player != null) { boolean isOwner = (ocarinaOwnerId != null && player.getUniqueID().compareTo(ocarinaOwnerId) == 0); if (player.getDistanceSqToEntity(this) < 64) { if (isOwner) { IQuest quest = ZSSQuests.get(player).get(QuestZeldaTalk.class); if (quest != null && quest.canComplete(player)) { quest.complete(player, true); // flag for completion message PacketDispatcher.sendTo(new SyncQuestPacket(quest), (EntityPlayerMP) player); ocarinaOwnerId = null; } } else { PlayerUtils.sendTranslatedChat(player, "chat.zss.npc.zelda.ocarina.wrong_player"); } } else if (isOwner) { PlayerUtils.sendTranslatedChat(player, "chat.zss.npc.zelda.ocarina.too_far"); } // else don't bother saying anything } setCustomer(null); } } } @Override public TeachingResult getTeachingResult(ItemStack stack, EntityPlayer player) { if (!isEntityAlive() || player.isSneaking()) { return null; } String deny = null; ZSSQuests quests = ZSSQuests.get(player); if (!quests.hasCompleted(QuestZeldaTalk.class)) { return null; // continue to Entity#interact } else if (!quests.hasBegun(QuestMasterSword.class)) { deny = "chat.zss.npc.zelda.song.not_ready"; } else if (!quests.hasCompleted(QuestMasterSword.class)) { deny = "chat.zss.npc.zelda.master_sword.hint." + rand.nextInt(3); } else if (((ItemInstrument) stack.getItem()).getInstrument(stack).getPower() < 5) { deny = "chat.zss.npc.zelda.song.weak"; } if (deny != null) { if (!player.worldObj.isRemote) { PlayerUtils.sendTranslatedChat(player, deny); } // To prevent song GUI from being opened after displaying chat message, return null // or don't cancel so Entity#interact can be called and return true from there return null; // new ISongTeacher.TeachingResult(null, false, true); } return new ISongTeacher.TeachingResult(ZeldaSongs.songTime, true, true); // skip Entity#interact } @Override public void writeEntityToNBT(NBTTagCompound compound) { super.writeEntityToNBT(compound); compound.setInteger("conversionTime", conversionTime); if (ocarinaOwnerId != null) { compound.setString("OcarinaOwnerId", ocarinaOwnerId.toString()); } } @Override public void readEntityFromNBT(NBTTagCompound compound) { super.readEntityFromNBT(compound); conversionTime = compound.getInteger("conversionTime"); if (compound.hasKey("OcarinaOwnerId", Constants.NBT.TAG_STRING)) { ocarinaOwnerId = UUID.fromString(compound.getString("OcarinaOwnerId")); } } }