/**
* 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 @ [Jun 9, 2014, 8:51:55 PM (GMT)]
*/
package vazkii.botania.common.block.tile;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.common.MinecraftForge;
import vazkii.botania.api.BotaniaAPI;
import vazkii.botania.api.lexicon.ILexicon;
import vazkii.botania.api.lexicon.multiblock.Multiblock;
import vazkii.botania.api.lexicon.multiblock.MultiblockSet;
import vazkii.botania.api.recipe.ElvenPortalUpdateEvent;
import vazkii.botania.api.recipe.IElvenItem;
import vazkii.botania.api.recipe.RecipeElvenTrade;
import vazkii.botania.api.state.BotaniaStateProps;
import vazkii.botania.api.state.enums.AlfPortalState;
import vazkii.botania.api.state.enums.LivingWoodVariant;
import vazkii.botania.api.state.enums.PylonVariant;
import vazkii.botania.common.Botania;
import vazkii.botania.common.block.ModBlocks;
import vazkii.botania.common.block.tile.mana.TilePool;
import vazkii.botania.common.core.handler.ConfigHandler;
import vazkii.botania.common.item.ItemLexicon;
import vazkii.botania.common.lexicon.LexiconData;
public class TileAlfPortal extends TileMod {
private static final BlockPos[] LIVINGWOOD_POSITIONS = {
new BlockPos(-1, 0, 0), new BlockPos(1, 0, 0), new BlockPos(-2, 1, 0),
new BlockPos(2, 1, 0), new BlockPos(-2, 3, 0), new BlockPos(2, 3, 0),
new BlockPos(-1, 4, 0), new BlockPos(1, 4, 0)
};
private static final BlockPos[] GLIMMERING_LIVINGWOOD_POSITIONS = {
new BlockPos(-2, 2, 0), new BlockPos(2, 2, 0), new BlockPos(0, 4, 0)
};
private static final BlockPos[] AIR_POSITIONS = {
new BlockPos(-1, 1, 0), new BlockPos(0, 1, 0), new BlockPos(1, 1, 0),
new BlockPos(-1, 2, 0), new BlockPos(0, 2, 0), new BlockPos(1, 2, 0),
new BlockPos(-1, 3, 0), new BlockPos(0, 3, 0), new BlockPos(1, 3, 0)
};
private static final String TAG_TICKS_OPEN = "ticksOpen";
private static final String TAG_TICKS_SINCE_LAST_ITEM = "ticksSinceLastItem";
private static final String TAG_STACK_COUNT = "stackCount";
private static final String TAG_STACK = "portalStack";
private static final String TAG_PORTAL_FLAG = "_elvenPortal";
private final List<ItemStack> stacksIn = new ArrayList<>();
public int ticksOpen = 0;
private int ticksSinceLastItem = 0;
private boolean closeNow = false;
private static final Function<BlockPos, BlockPos> CONVERTER_X_Z = input -> new BlockPos(input.getZ(), input.getY(), input.getX());
private static final Function<double[], double[]> CONVERTER_X_Z_FP = input -> new double[] { input[2], input[1], input[0] };
private static final Function<BlockPos, BlockPos> CONVERTER_Z_SWAP = input -> new BlockPos(input.getX(), input.getY(), -input.getZ());
public static MultiblockSet makeMultiblockSet() {
Multiblock mb = new Multiblock();
for(BlockPos l : LIVINGWOOD_POSITIONS)
mb.addComponent(l.up(), ModBlocks.livingwood.getDefaultState());
for(BlockPos g : GLIMMERING_LIVINGWOOD_POSITIONS)
mb.addComponent(g.up(), ModBlocks.livingwood.getDefaultState().withProperty(BotaniaStateProps.LIVINGWOOD_VARIANT, LivingWoodVariant.GLIMMERING));
// for(BlockPos p : PYLON_POSITIONS)
// mb.addComponent(new BlockPos(-p.getX(), p.getY() + 1, -p.getZ()), ModBlocks.pylon.getDefaultState().withProperty(BotaniaStateProps.PYLON_VARIANT, PylonVariant.NATURA));
// for(BlockPos p : POOL_POSITIONS)
// mb.addComponent(new StateInsensitiveComponent(new BlockPos(-p.getX(), p.getY() + 1, -p.getZ()), ModBlocks.pool));
mb.addComponent(new BlockPos(0, 1, 0), ModBlocks.alfPortal.getDefaultState());
mb.setRenderOffset(new BlockPos(0, -1, 0));
return mb.makeSet();
}
@Override
public void update() {
IBlockState iBlockState = world.getBlockState(getPos());
if(iBlockState.getValue(BotaniaStateProps.ALFPORTAL_STATE) == AlfPortalState.OFF) {
ticksOpen = 0;
return;
}
AlfPortalState state = iBlockState.getValue(BotaniaStateProps.ALFPORTAL_STATE);
AlfPortalState newState = getValidState();
ticksOpen++;
AxisAlignedBB aabb = getPortalAABB();
boolean open = ticksOpen > 60;
ElvenPortalUpdateEvent event = new ElvenPortalUpdateEvent(this, aabb, open, stacksIn);
MinecraftForge.EVENT_BUS.post(event);
if(ticksOpen > 60) {
ticksSinceLastItem++;
if(ConfigHandler.elfPortalParticlesEnabled)
blockParticle(state);
List<EntityItem> items = world.getEntitiesWithinAABB(EntityItem.class, aabb);
if(!world.isRemote)
for(EntityItem item : items) {
if(item.isDead)
continue;
ItemStack stack = item.getEntityItem();
boolean consume;
if (item.getEntityData().hasKey(TAG_PORTAL_FLAG)) {
consume = false;
} else if (stack.getItem() instanceof ItemLexicon) {
consume = true;
} else if ((!(stack.getItem() instanceof IElvenItem) || !((IElvenItem) stack.getItem()).isElvenItem(stack))) {
consume = true;
} else {
consume = false;
}
if (consume) {
item.setDead();
addItem(stack);
ticksSinceLastItem = 0;
}
}
if(ticksSinceLastItem >= 4) {
if(!world.isRemote)
resolveRecipes();
}
}
if(closeNow) {
world.setBlockState(getPos(), ModBlocks.alfPortal.getDefaultState(), 1 | 2);
for(int i = 0; i < 36; i++)
blockParticle(state);
closeNow = false;
} else if(newState != state) {
if(newState == AlfPortalState.OFF)
for(int i = 0; i < 36; i++)
blockParticle(state);
world.setBlockState(getPos(), world.getBlockState(getPos()).withProperty(BotaniaStateProps.ALFPORTAL_STATE, newState), 1 | 2);
}
}
private void blockParticle(AlfPortalState state) {
int i = world.rand.nextInt(AIR_POSITIONS.length);
double[] pos = new double[] {
AIR_POSITIONS[i].getX() + 0.5F, AIR_POSITIONS[i].getY() + 0.5F, AIR_POSITIONS[i].getZ() + 0.5F
};
if(state == AlfPortalState.ON_X)
pos = CONVERTER_X_Z_FP.apply(pos);
float motionMul = 0.2F;
Botania.proxy.wispFX(getPos().getX() + pos[0], getPos().getY() + pos[1], getPos().getZ() + pos[2], (float) (Math.random() * 0.25F), (float) (Math.random() * 0.5F + 0.5F), (float) (Math.random() * 0.25F), (float) (Math.random() * 0.15F + 0.1F), (float) (Math.random() - 0.5F) * motionMul, (float) (Math.random() - 0.5F) * motionMul, (float) (Math.random() - 0.5F) * motionMul);
}
public boolean onWanded() {
AlfPortalState state = world.getBlockState(getPos()).getValue(BotaniaStateProps.ALFPORTAL_STATE);
if(state == AlfPortalState.OFF) {
AlfPortalState newState = getValidState();
if(newState != AlfPortalState.OFF) {
world.setBlockState(getPos(), world.getBlockState(getPos()).withProperty(BotaniaStateProps.ALFPORTAL_STATE, newState), 1 | 2);
return true;
}
}
return false;
}
private AxisAlignedBB getPortalAABB() {
AxisAlignedBB aabb = new AxisAlignedBB(pos.add(-1, 1, 0), pos.add(2, 4, 1));
if(world.getBlockState(getPos()).getValue(BotaniaStateProps.ALFPORTAL_STATE) == AlfPortalState.ON_X)
aabb = new AxisAlignedBB(pos.add(0, 1, -1), pos.add(1, 4, 2));
return aabb;
}
private void addItem(ItemStack stack) {
int size = stack.getCount();
stack.setCount(1);
for(int i = 0; i < size; i++)
stacksIn.add(stack.copy());
}
private void resolveRecipes() {
int i = 0;
for(ItemStack stack : stacksIn) {
if(!stack.isEmpty() && stack.getItem() instanceof ILexicon) {
ILexicon lexicon = (ILexicon) stack.getItem();
if (!lexicon.isKnowledgeUnlocked(stack, BotaniaAPI.elvenKnowledge)) {
lexicon.unlockKnowledge(stack, BotaniaAPI.elvenKnowledge);
ItemLexicon.setForcedPage(stack, LexiconData.elvenMessage.getUnlocalizedName());
spawnItem(stack);
stacksIn.remove(i);
return;
}
}
i++;
}
for(RecipeElvenTrade recipe : BotaniaAPI.elvenTradeRecipes) {
if(recipe.matches(stacksIn, false)) {
if(consumeMana(null, 500, false)) {
recipe.matches(stacksIn, true);
for(ItemStack output : recipe.getOutputs())
spawnItem(output.copy());
}
break;
}
}
}
private void spawnItem(ItemStack stack) {
EntityItem item = new EntityItem(world, pos.getX() + 0.5, pos.getY() + 1.5, pos.getZ() + 0.5, stack);
item.getEntityData().setBoolean(TAG_PORTAL_FLAG, true);
world.spawnEntity(item);
ticksSinceLastItem = 0;
}
@Nonnull
@Override
public NBTTagCompound writeToNBT(NBTTagCompound cmp) {
NBTTagCompound ret = super.writeToNBT(cmp);
cmp.setInteger(TAG_STACK_COUNT, stacksIn.size());
int i = 0;
for(ItemStack stack : stacksIn) {
NBTTagCompound stackcmp = stack.writeToNBT(new NBTTagCompound());
cmp.setTag(TAG_STACK + i, stackcmp);
i++;
}
return ret;
}
@Override
public void readFromNBT(NBTTagCompound cmp) {
super.readFromNBT(cmp);
int count = cmp.getInteger(TAG_STACK_COUNT);
stacksIn.clear();
for(int i = 0; i < count; i++) {
NBTTagCompound stackcmp = cmp.getCompoundTag(TAG_STACK + i);
ItemStack stack = new ItemStack(stackcmp);
stacksIn.add(stack);
}
}
@Override
public void writePacketNBT(NBTTagCompound cmp) {
cmp.setInteger(TAG_TICKS_OPEN, ticksOpen);
cmp.setInteger(TAG_TICKS_SINCE_LAST_ITEM, ticksSinceLastItem);
}
@Override
public void readPacketNBT(NBTTagCompound cmp) {
ticksOpen = cmp.getInteger(TAG_TICKS_OPEN);
ticksSinceLastItem = cmp.getInteger(TAG_TICKS_SINCE_LAST_ITEM);
}
private AlfPortalState getValidState() {
if(checkConverter(null))
return AlfPortalState.ON_Z;
if(checkConverter(CONVERTER_X_Z))
return AlfPortalState.ON_X;
return AlfPortalState.OFF;
}
private boolean checkConverter(Function<BlockPos, BlockPos> baseConverter) {
return checkMultipleConverters(baseConverter) || checkMultipleConverters(CONVERTER_Z_SWAP, baseConverter);
}
@SafeVarargs
private final boolean checkMultipleConverters(Function<BlockPos, BlockPos>... converters) {
if(!check2DArray(AIR_POSITIONS, Blocks.AIR.getDefaultState(), true, converters))
return false;
if(!check2DArray(LIVINGWOOD_POSITIONS, ModBlocks.livingwood.getDefaultState().withProperty(BotaniaStateProps.LIVINGWOOD_VARIANT, LivingWoodVariant.DEFAULT), false, converters))
return false;
if(!check2DArray(GLIMMERING_LIVINGWOOD_POSITIONS, ModBlocks.livingwood.getDefaultState().withProperty(BotaniaStateProps.LIVINGWOOD_VARIANT, LivingWoodVariant.GLIMMERING), false, converters))
return false;
// if(!check2DArray(PYLON_POSITIONS, ModBlocks.pylon.getDefaultState().withProperty(BotaniaStateProps.PYLON_VARIANT, PylonVariant.NATURA), false, converters))
// return false;
// if(!check2DArray(POOL_POSITIONS, ModBlocks.pool.getDefaultState(), true, converters))
// return false;
lightPylons();
return true;
}
public List<BlockPos> locatePylons() {
List<BlockPos> list = new ArrayList();
int range = 5;
IBlockState pylonState = ModBlocks.pylon.getDefaultState().withProperty(BotaniaStateProps.PYLON_VARIANT, PylonVariant.NATURA);
IBlockState poolState = ModBlocks.pool.getDefaultState();
for(int i = -range; i < range + 1; i++)
for(int j = -range; j < range + 1; j++)
for(int k = -range; k < range + 1; k++) {
BlockPos pos = new BlockPos(i, j, k);
if(checkPosition(pos, pylonState, false) && checkPosition(pos.down(), poolState, true))
list.add(pos);
}
return list;
}
public void lightPylons() {
if(ticksOpen < 50)
return;
List<BlockPos> pylons = locatePylons();
for(BlockPos pos : pylons) {
TileEntity tile = world.getTileEntity(getPos().add(pos));
if(tile instanceof TilePylon) {
TilePylon pylon = (TilePylon) tile;
pylon.activated = true;
pylon.centerPos = getPos();
}
}
if(ticksOpen == 50)
consumeMana(pylons, 200000, true);
}
public boolean consumeMana(@Nullable List<BlockPos> pylons, int totalCost, boolean close) {
List<TilePool> consumePools = new ArrayList();
int consumed = 0;
if(pylons == null)
pylons = locatePylons();
if(pylons.size() < 2) {
closeNow = true;
return false;
}
int costPer = Math.max(1, totalCost / pylons.size());
int expectedConsumption = costPer * pylons.size();
for(BlockPos pos : pylons) {
TileEntity tile = world.getTileEntity(getPos().add(pos));
if(tile instanceof TilePylon) {
TilePylon pylon = (TilePylon) tile;
pylon.activated = true;
pylon.centerPos = getPos();
}
tile = world.getTileEntity(getPos().add(pos).down());
if(tile instanceof TilePool) {
TilePool pool = (TilePool) tile;
if(pool.getCurrentMana() < costPer) {
closeNow = closeNow || close;
return false;
} else if(!world.isRemote) {
consumePools.add(pool);
consumed += costPer;
}
}
}
if(consumed >= expectedConsumption) {
for(TilePool pool : consumePools)
pool.recieveMana(-costPer);
return true;
}
return false;
}
@SafeVarargs
private final boolean check2DArray(BlockPos[] positions, IBlockState state, boolean onlyCheckBlock, Function<BlockPos, BlockPos>... converters) {
for(BlockPos pos : positions) {
for(Function<BlockPos, BlockPos> f : converters)
if(f != null)
pos = f.apply(pos);
if(!checkPosition(pos, state, onlyCheckBlock))
return false;
}
return true;
}
private boolean checkPosition(BlockPos pos, IBlockState state, boolean onlyCheckBlock) {
BlockPos pos_ = getPos().add(pos);
IBlockState stateat = world.getBlockState(pos_);
Block blockat = stateat.getBlock();
if(state.getBlock() == Blocks.AIR ? blockat.isAir(stateat, world, pos_) : blockat == state.getBlock())
return onlyCheckBlock || stateat == state;
return false;
}
@Nonnull
@Override
public AxisAlignedBB getRenderBoundingBox() {
return INFINITE_EXTENT_AABB;
}
}