/** Copyright (C) <2017> <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.world.gen.feature; import java.util.List; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.BlockPos; import net.minecraft.world.World; import net.minecraft.world.gen.structure.StructureBoundingBox; import net.minecraftforge.common.util.Constants; import zeldaswordskills.ZSSAchievements; import zeldaswordskills.api.item.IFairyUpgrade; import zeldaswordskills.block.tileentity.TileEntityDungeonCore; import zeldaswordskills.entity.passive.EntityFairy; import zeldaswordskills.ref.Config; import zeldaswordskills.util.WorldUtils; public class FairySpawner { /** The dungeon core to which the spawner belongs */ protected final TileEntityDungeonCore core; /** The fairy pool's bounding box */ protected final StructureBoundingBox box; /** Max fairies this spawner can spawn before it must refresh */ protected int maxFairies = 0; /** Number of fairies already spawned; this gets reset at a specific time each night */ protected int fairiesSpawned = 0; /** Minimum date before next spawn reset */ protected long nextResetDate = 0; /** Scheduled update timer used for processing nearby dropped items */ protected int itemUpdate = -1; /** Player who initiated the next scheduled item update */ protected String playerName = ""; /** Number of rupees (emeralds) donated to the fairy */ protected int rupees = 0; public FairySpawner(TileEntityDungeonCore core) { this.core = core; this.box = core.getDungeonBoundingBox(); if (box == null) { throw new IllegalArgumentException("Dungeon Core bounding box can not be null!"); } } /** * Sets the max number of fairies that can be spawned before needing to replenish */ public FairySpawner setMaxFairies(int maxFairies) { this.maxFairies = maxFairies; return this; } /** * Returns true if the amount of rupees was present and consumed */ public boolean consumeRupees(int amount) { if (amount > rupees) { return false; } else { rupees -= amount; return true; } } /** * Schedules this spawner to check for dropped items in the near future */ public void scheduleItemUpdate(EntityPlayer player) { if (itemUpdate < 0) { itemUpdate = 2; } if (playerName.equals("")) { playerName = player.getName(); } } /** * Call when the supporting tile entity is removed to release fairy pool contents */ public void onBlockBroken() { onBlockBroken(core.getWorld(), core.getPos().getX(), core.getPos().getY(), core.getPos().getZ()); } /** * Arguments provide cleaner-looking version of onBlockBroken() */ private void onBlockBroken(World world, int x, int y, int z) { while (rupees > 0) { int k = (rupees > 64 ? 64 : rupees); rupees -= k; world.playSoundEffect(x + 0.5D, y + 0.5D, z + 0.5D, "random.orb", 1.0F, 1.0F); WorldUtils.spawnItemWithRandom(world, new ItemStack(Items.emerald, k), x, y, z); } } /** * Call every tick to allow the fairy spawner to update */ public void onUpdate() { updateSpawner(core.getWorld(), core.getPos().getX(), core.getPos().getY(), core.getPos().getZ()); updateItems(core.getWorld(), core.getPos().getX(), core.getPos().getY(), core.getPos().getZ()); } /** * Updates fairy spawning and checks if spawn count should be reset * Args: world and x, y, z coordinates of tile entity */ private void updateSpawner(World world, int x, int y, int z) { if (fairiesSpawned < maxFairies && world.rand.nextFloat() < (world.isDaytime() ? 0.01F : 0.2F)) { int centerY = box.getCenter().getY(); int nearby = world.getEntitiesWithinAABB(EntityFairy.class, new AxisAlignedBB( x, centerY, z, x + 1, centerY + 1, z + 1). expand(box.getXSize() / 2, box.getYSize() / 2, box.getZSize() / 2)).size(); if (nearby < 4) { EntityFairy fairy = new EntityFairy(world); fairy.setFairyHome(new BlockPos(x, y + 2, z)); world.spawnEntityInWorld(fairy); if (++fairiesSpawned == maxFairies) { nextResetDate = world.getTotalWorldTime() + 24000 * (world.rand.nextInt(Config.getDaysToRespawn()) + 1); } } } if (fairiesSpawned == maxFairies && !world.isDaytime() && world.getTotalWorldTime() > nextResetDate) { fairiesSpawned = 0; } } /** * Updates item timer and checks for nearby items if scheduled * Args: world and x, y, z coordinates of tile entity */ private void updateItems(World world, int x, int y, int z) { if (itemUpdate > 0) { --itemUpdate; } else if (itemUpdate == 0) { EntityPlayer player = world.getPlayerEntityByName(playerName); if (player != null) { int centerY = box.getCenter().getY(); List<EntityItem> list = world.getEntitiesWithinAABB(EntityItem.class, new AxisAlignedBB( x, centerY, z, x + 1, centerY + 1, z + 1). expand(box.getXSize() / 2, box.getYSize() / 2, box.getZSize() / 2)); for (EntityItem item : list) { if (!item.isEntityAlive()) { continue; } ItemStack stack = item.getEntityItem(); if (stack.getItem() == Items.emerald) { player.triggerAchievement(ZSSAchievements.fairyEmerald); world.playSoundEffect(x + 0.5D, y + 1, z + 0.5D, "random.orb", 1.0F, 1.0F); rupees += stack.stackSize; item.setDead(); } else if (stack.getItem() instanceof IFairyUpgrade && ((IFairyUpgrade) stack.getItem()).hasFairyUpgrade(stack)) { ((IFairyUpgrade) stack.getItem()).handleFairyUpgrade(item, player, core); } } } itemUpdate = -1; playerName = ""; } } /** * Writes fairy spawner data to NBT */ public void writeToNBT(NBTTagCompound compound) { NBTTagCompound data = new NBTTagCompound(); data.setInteger("maxFairies", maxFairies); data.setInteger("spawned", fairiesSpawned); data.setLong("nextResetDate", nextResetDate); data.setInteger("itemUpdate", itemUpdate); data.setInteger("rupees", rupees); data.setString("playerName", playerName); compound.setTag("FairySpawner", data); } /** * Reads fairy data data from NBT */ public void readFromNBT(NBTTagCompound compound) { NBTTagCompound data = compound.getCompoundTag("FairySpawner"); maxFairies = data.getInteger("maxFairies"); fairiesSpawned = data.getInteger("spawned"); // fixes class cast exception from changing types: nextResetDate = (data.getTag("nextResetDate").getId() == Constants.NBT.TAG_LONG ? data.getLong("nextResetDate") : 0); itemUpdate = data.getInteger("itemUpdate"); rupees = data.getInteger("rupees"); playerName = data.getString("playerName"); } }