/**
* This class was created by <Vazkii>. It's distributed as
* part of the Botania Mod. Get the Source Code in github:
* https://github.com/Vazkii/Botania
*
* Botania is Open Source and distributed under the
* Botania License: http://botaniamod.net/license.php
*
* File Created @ [Aug 21, 2014, 5:43:44 PM (GMT)]
*/
package vazkii.botania.common.entity;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import javax.annotation.Nonnull;
import baubles.api.BaublesApi;
import gnu.trove.map.hash.TObjectIntHashMap;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.items.IItemHandler;
import vazkii.botania.api.mana.IManaItem;
import vazkii.botania.api.mana.IManaPool;
import vazkii.botania.api.mana.spark.ISparkAttachable;
import vazkii.botania.api.mana.spark.ISparkEntity;
import vazkii.botania.api.mana.spark.SparkHelper;
import vazkii.botania.api.mana.spark.SparkUpgradeType;
import vazkii.botania.common.item.ModItems;
import vazkii.botania.common.network.PacketBotaniaEffect;
import vazkii.botania.common.network.PacketHandler;
public class EntitySpark extends Entity implements ISparkEntity {
private static final int TRANSFER_RATE = 1000;
private static final String TAG_UPGRADE = "upgrade";
private static final String TAG_INVIS = "invis";
private static final DataParameter<Integer> UPGRADE = EntityDataManager.createKey(EntitySpark.class, DataSerializers.VARINT);
private final Set<ISparkEntity> transfers = Collections.newSetFromMap(new WeakHashMap<>());
private int removeTransferants = 2;
public EntitySpark(World world) {
super(world);
isImmuneToFire = true;
}
@Override
protected void entityInit() {
setSize(0.1F, 0.5F);
dataManager.register(UPGRADE, 0);
}
@Nonnull
@Override
public ItemStack getPickedResult(RayTraceResult target) {
return new ItemStack(ModItems.spark);
}
@Override
public void onUpdate() {
super.onUpdate();
if(world.isRemote)
return;
ISparkAttachable tile = getAttachedTile();
if(tile == null) {
dropAndKill();
return;
}
SparkUpgradeType upgrade = getUpgrade();
List<ISparkEntity> allSparks = null;
if(upgrade == SparkUpgradeType.DOMINANT || upgrade == SparkUpgradeType.RECESSIVE)
allSparks = SparkHelper.getSparksAround(world, posX, posY, posZ);
Collection<ISparkEntity> transfers = getTransfers();
switch(upgrade) {
case DISPERSIVE : {
List<EntityPlayer> players = SparkHelper.getEntitiesAround(EntityPlayer.class, world, posX, posY, posZ);
Map<EntityPlayer, TObjectIntHashMap<ItemStack>> receivingPlayers = new HashMap<>();
ItemStack input = new ItemStack(ModItems.spark);
for(EntityPlayer player : players) {
List<ItemStack> stacks = new ArrayList<>();
stacks.addAll(player.inventory.mainInventory);
stacks.addAll(player.inventory.armorInventory);
IItemHandler baubles = BaublesApi.getBaublesHandler(player);
for (int i = 0; i < baubles.getSlots(); i++)
stacks.add(baubles.getStackInSlot(i));
for(ItemStack stack : stacks) {
if(stack.isEmpty() || !(stack.getItem() instanceof IManaItem))
continue;
IManaItem manaItem = (IManaItem) stack.getItem();
if(manaItem.canReceiveManaFromItem(stack, input)) {
TObjectIntHashMap<ItemStack> receivingStacks;
boolean add = false;
if(!receivingPlayers.containsKey(player)) {
add = true;
receivingStacks = new TObjectIntHashMap<>();
} else receivingStacks = receivingPlayers.get(player);
int recv = Math.min(getAttachedTile().getCurrentMana(), Math.min(TRANSFER_RATE, manaItem.getMaxMana(stack) - manaItem.getMana(stack)));
if(recv > 0) {
receivingStacks.put(stack, recv);
if(add)
receivingPlayers.put(player, receivingStacks);
}
}
}
}
if(!receivingPlayers.isEmpty()) {
List<EntityPlayer> keys = new ArrayList<>(receivingPlayers.keySet());
Collections.shuffle(keys);
EntityPlayer player = keys.iterator().next();
TObjectIntHashMap<ItemStack> items = receivingPlayers.get(player);
ItemStack stack = items.keySet().iterator().next();
int cost = items.get(stack);
int manaToPut = Math.min(getAttachedTile().getCurrentMana(), cost);
((IManaItem) stack.getItem()).addMana(stack, manaToPut);
getAttachedTile().recieveMana(-manaToPut);
particlesTowards(player);
}
break;
}
case DOMINANT : {
List<ISparkEntity> validSparks = new ArrayList<>();
for(ISparkEntity spark : allSparks) {
if(spark == this)
continue;
SparkUpgradeType upgrade_ = spark.getUpgrade();
if(upgrade_ == SparkUpgradeType.NONE && spark.getAttachedTile() instanceof IManaPool)
validSparks.add(spark);
}
if(validSparks.size() > 0)
validSparks.get(world.rand.nextInt(validSparks.size())).registerTransfer(this);
break;
}
case RECESSIVE : {
for(ISparkEntity spark : allSparks) {
if(spark == this)
continue;
SparkUpgradeType upgrade_ = spark.getUpgrade();
if(upgrade_ != SparkUpgradeType.DOMINANT && upgrade_ != SparkUpgradeType.RECESSIVE && upgrade_ != SparkUpgradeType.ISOLATED)
transfers.add(spark);
}
break;
}
case NONE:
default: break;
}
if(!transfers.isEmpty()) {
int manaTotal = Math.min(TRANSFER_RATE * transfers.size(), tile.getCurrentMana());
int manaForEach = manaTotal / transfers.size();
int manaSpent = 0;
if(manaForEach > transfers.size()) {
for(ISparkEntity spark : transfers) {
if(spark.getAttachedTile() == null || spark.getAttachedTile().isFull() || spark.areIncomingTransfersDone()) {
manaTotal -= manaForEach;
continue;
}
ISparkAttachable attached = spark.getAttachedTile();
int spend = Math.min(attached.getAvailableSpaceForMana(), manaForEach);
attached.recieveMana(spend);
manaSpent += spend;
particlesTowards((Entity) spark);
}
tile.recieveMana(-manaSpent);
}
}
if(removeTransferants > 0)
removeTransferants--;
filterTransfers();
}
private void particlesTowards(Entity e) {
PacketHandler.sendToNearby(world, this,
new PacketBotaniaEffect(PacketBotaniaEffect.EffectType.SPARK_MANA_FLOW, posX, posY, posZ,
getEntityId(), e.getEntityId()));
}
public static void particleBeam(EntityPlayer player, Entity e1, Entity e2) {
if(e1 != null && e2 != null && !e1.world.isRemote) {
PacketHandler.sendTo((EntityPlayerMP) player,
new PacketBotaniaEffect(PacketBotaniaEffect.EffectType.SPARK_NET_INDICATOR, e1.posX, e1.posY, e1.posZ,
e1.getEntityId(), e2.getEntityId()));
}
}
private void dropAndKill() {
SparkUpgradeType upgrade = getUpgrade();
entityDropItem(new ItemStack(ModItems.spark), 0F);
if(upgrade != SparkUpgradeType.NONE)
entityDropItem(new ItemStack(ModItems.sparkUpgrade, 1, upgrade.ordinal() - 1), 0F);
setDead();
}
@Override
public boolean canBeCollidedWith() {
return true;
}
@Override
public boolean processInitialInteract(EntityPlayer player, EnumHand hand) {
ItemStack stack = player.getHeldItem(hand);
if(!stack.isEmpty()) {
if(world.isRemote) {
boolean valid = stack.getItem() == ModItems.twigWand || stack.getItem() == ModItems.sparkUpgrade
|| stack.getItem() == ModItems.phantomInk;
if(valid)
player.swingArm(hand);
return valid;
}
SparkUpgradeType upgrade = getUpgrade();
if(stack.getItem() == ModItems.twigWand) {
if(player.isSneaking()) {
if(upgrade != SparkUpgradeType.NONE) {
entityDropItem(new ItemStack(ModItems.sparkUpgrade, 1, upgrade.ordinal() - 1), 0F);
setUpgrade(SparkUpgradeType.NONE);
transfers.clear();
removeTransferants = 2;
} else dropAndKill();
return true;
} else {
for(ISparkEntity spark : SparkHelper.getSparksAround(world, posX, posY, posZ))
particleBeam(player, this, (Entity) spark);
return true;
}
} else if(stack.getItem() == ModItems.sparkUpgrade && upgrade == SparkUpgradeType.NONE) {
int newUpgrade = stack.getItemDamage() + 1;
setUpgrade(SparkUpgradeType.values()[newUpgrade]);
stack.shrink(1);
return true;
} else if (stack.getItem() == ModItems.phantomInk) {
setInvisible(true);
return true;
}
}
return false;
}
@Override
protected void readEntityFromNBT(@Nonnull NBTTagCompound cmp) {
setUpgrade(SparkUpgradeType.values()[cmp.getInteger(TAG_UPGRADE)]);
setInvisible(cmp.getInteger(TAG_INVIS) == 1);
}
@Override
protected void writeEntityToNBT(@Nonnull NBTTagCompound cmp) {
cmp.setInteger(TAG_UPGRADE, getUpgrade().ordinal());
cmp.setInteger(TAG_INVIS, isInvisible() ? 1 : 0);
}
@Override
public ISparkAttachable getAttachedTile() {
int x = MathHelper.floor(posX);
int y = MathHelper.floor(posY) - 1;
int z = MathHelper.floor(posZ);
TileEntity tile = world.getTileEntity(new BlockPos(x, y, z));
if(tile != null && tile instanceof ISparkAttachable)
return (ISparkAttachable) tile;
return null;
}
private void filterTransfers() {
Iterator<ISparkEntity> iter = transfers.iterator();
while (iter.hasNext()) {
ISparkEntity spark = iter.next();
SparkUpgradeType upgr = getUpgrade();
SparkUpgradeType supgr = spark.getUpgrade();
ISparkAttachable atile = spark.getAttachedTile();
if(!(spark != this
&& !spark.areIncomingTransfersDone()
&& atile != null && !atile.isFull()
&& (upgr == SparkUpgradeType.NONE && supgr == SparkUpgradeType.DOMINANT || upgr == SparkUpgradeType.RECESSIVE && (supgr == SparkUpgradeType.NONE || supgr == SparkUpgradeType.DISPERSIVE) || !(atile instanceof IManaPool))))
iter.remove();
}
}
@Override
public Collection<ISparkEntity> getTransfers() {
filterTransfers();
return transfers;
}
private boolean hasTransfer(ISparkEntity entity) {
return transfers.contains(entity);
}
@Override
public void registerTransfer(ISparkEntity entity) {
if(hasTransfer(entity))
return;
transfers.add(entity);
}
@Override
public SparkUpgradeType getUpgrade() {
return SparkUpgradeType.values()[dataManager.get(UPGRADE)];
}
@Override
public void setUpgrade(SparkUpgradeType upgrade) {
dataManager.set(UPGRADE, upgrade.ordinal());
}
@Override
public boolean areIncomingTransfersDone() {
ISparkAttachable tile = getAttachedTile();
if(tile instanceof IManaPool)
return removeTransferants > 0;
return tile != null && tile.areIncomingTranfersDone();
}
}