/** * 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 @ [Jul 15, 2015, 8:32:04 PM (GMT)] */ package vazkii.botania.common.block.tile; import java.awt.Color; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.item.EntityEnderPearl; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; 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.DamageSource; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import vazkii.botania.api.sound.BotaniaSoundEvents; import vazkii.botania.api.state.BotaniaStateProps; import vazkii.botania.api.state.enums.LuminizerVariant; import vazkii.botania.api.wand.IWandBindable; import vazkii.botania.common.Botania; import vazkii.botania.common.achievement.ModAchievements; import vazkii.botania.common.block.ModBlocks; import vazkii.botania.common.core.helper.Vector3; public class TileLightRelay extends TileMod implements IWandBindable { private static final int MAX_DIST = 20; private static final String TAG_BIND_X = "bindX"; private static final String TAG_BIND_Y = "bindY"; private static final String TAG_BIND_Z = "bindZ"; BlockPos bindPos = new BlockPos(0, -1, 0); int ticksElapsed = 0; public void mountEntity(Entity e) { BlockPos nextDest = getNextDestination(); if(e.isRiding() || world.isRemote || nextDest == null || !isValidBinding()) return; EntityPlayerMover mover = new EntityPlayerMover(world, pos, nextDest); world.spawnEntity(mover); e.startRiding(mover); if(!(e instanceof EntityItem)) { mover.playSound(BotaniaSoundEvents.lightRelay, 0.2F, (float) Math.random() * 0.3F + 0.7F); if(e instanceof EntityPlayer) ((EntityPlayer) e).addStat(ModAchievements.luminizerRide, 1); } } @Override public void update() { ticksElapsed++; BlockPos nextDest = getNextDestination(); if(nextDest != null && nextDest.getY() > -1 && isValidBinding()) { if(world.isRemote) { Vector3 vec = getMovementVector(); if(vec != null) { double dist = 0.1; int size = (int) (vec.mag() / dist); int count = 10; int start = ticksElapsed % size; Vector3 vecMag = vec.normalize().multiply(dist); Vector3 vecTip = vecMag.multiply(start).add(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5); double radPer = Math.PI / 16.0; float mul = 0.5F; float mulPer = 0.4F; float maxMul = 2; for(int i = start; i < start + count; i++) { mul = Math.min(maxMul, mul + mulPer); double rad = radPer * (i + ticksElapsed * 0.4); Vector3 vecRot = vecMag.crossProduct(Vector3.ONE).multiply(mul).rotate(rad, vecMag).add(vecTip); Botania.proxy.wispFX(vecRot.x, vecRot.y, vecRot.z, 0.4F, 0.4F, 1F, 0.1F, (float) -vecMag.x, (float) -vecMag.y, (float) -vecMag.z, 1F); vecTip = vecTip.add(vecMag); } } } else { BlockPos endpoint = getEndpoint(); if(endpoint != null) { AxisAlignedBB aabb = ModBlocks.lightRelay.getBoundingBox(world.getBlockState(pos), world, pos).offset(pos); float range = 0.5F; List<EntityEnderPearl> enderPearls = world.getEntitiesWithinAABB(EntityEnderPearl.class, aabb.expandXyz(range)); for(EntityEnderPearl pearl : enderPearls) { pearl.setPositionAndUpdate( endpoint.getX() + pearl.posX - pos.getX(), endpoint.getY() + pearl.posY - pos.getY(), endpoint.getZ() + pearl.posZ - pos.getZ() ); } } } } } private boolean isValidBinding() { BlockPos nextDest = getNextDestination(); if(nextDest == null) return false; Block block = world.getBlockState(nextDest).getBlock(); return block == ModBlocks.lightRelay; } private BlockPos getEndpoint() { List<TileLightRelay> pointsPassed = new ArrayList<>(); TileLightRelay relay = this; BlockPos lastCoords = null; // Doing while(true) gives an unreachable code error boolean run = true; while(run) { if(pointsPassed.contains(relay)) return null; // Circular path pointsPassed.add(relay); BlockPos coords = relay.getNextDestination(); if(coords == null) return lastCoords; TileEntity tile = world.getTileEntity(coords); if(tile != null && tile instanceof TileLightRelay) relay = (TileLightRelay) tile; else return lastCoords; lastCoords = coords; } return null; } public Vector3 getMovementVector() { BlockPos dest = getNextDestination(); if(dest == null) return null; return new Vector3(dest.getX() - pos.getX(), dest.getY() - pos.getY(), dest.getZ() - pos.getZ()); } @Override public BlockPos getBinding() { return bindPos; } public BlockPos getNextDestination() { IBlockState state = world.getBlockState(pos); if(state.getValue(BotaniaStateProps.LUMINIZER_VARIANT) == LuminizerVariant.TOGGLE && state.getValue(BotaniaStateProps.POWERED)) return null; else if(state.getValue(BotaniaStateProps.LUMINIZER_VARIANT) == LuminizerVariant.FORK) { BlockPos torchPos = null; for(int i = -2; i < 3; i++) { BlockPos testPos = pos.add(0, i, 0); IBlockState testState = world.getBlockState(testPos); if(testState.getBlock() == ModBlocks.animatedTorch) { torchPos = testPos; break; } } if(torchPos != null) { TileAnimatedTorch torch = (TileAnimatedTorch) world.getTileEntity(torchPos); EnumFacing side = TileAnimatedTorch.SIDES[torch.side].getOpposite(); for(int i = 1; i < MAX_DIST; i++) { BlockPos testPos = pos.offset(side, i); IBlockState testState = world.getBlockState(testPos); if(testState.getBlock() == ModBlocks.lightRelay) return testPos; } } } return getBinding(); } @Override public boolean canSelect(EntityPlayer player, ItemStack wand, BlockPos pos, EnumFacing side) { return true; } @Override public boolean bindTo(EntityPlayer player, ItemStack wand, BlockPos pos, EnumFacing side) { if(player.world.getBlockState(pos).getBlock() != ModBlocks.lightRelay || pos.distanceSq(getPos()) > MAX_DIST * MAX_DIST) return false; bindPos = pos; return true; } @Override public void readPacketNBT(NBTTagCompound cmp) { bindPos = new BlockPos( cmp.getInteger(TAG_BIND_X), cmp.getInteger(TAG_BIND_Y), cmp.getInteger(TAG_BIND_Z) ); } @Override public void writePacketNBT(NBTTagCompound cmp) { cmp.setInteger(TAG_BIND_X, bindPos.getX()); cmp.setInteger(TAG_BIND_Y, bindPos.getY()); cmp.setInteger(TAG_BIND_Z, bindPos.getZ()); } public static class EntityPlayerMover extends Entity { private static final String TAG_EXIT_X = "exitX"; private static final String TAG_EXIT_Y = "exitY"; private static final String TAG_EXIT_Z = "exitZ"; private static final DataParameter<BlockPos> EXIT_POS = EntityDataManager.createKey(EntityPlayerMover.class, DataSerializers.BLOCK_POS); public EntityPlayerMover(World world) { super(world); } public EntityPlayerMover(World world, BlockPos pos, BlockPos exitPos) { this(world); setPosition(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5); setExit(exitPos); } @Override protected void entityInit() { setSize(0F, 0F); noClip = true; dataManager.register(EXIT_POS, BlockPos.ORIGIN); } @Override public void onUpdate() { super.onUpdate(); if(getPassengers().isEmpty() && !world.isRemote) { setDead(); return; } boolean isItem = getRidingEntity() instanceof EntityItem; if(!isItem && ticksExisted % 30 == 0) playSound(BotaniaSoundEvents.lightRelay, 0.05F, (float) Math.random() * 0.3F + 0.7F); BlockPos pos = new BlockPos(this); BlockPos exitPos = getExitPos(); if(pos.equals(exitPos)) { TileEntity tile = world.getTileEntity(pos); if(tile != null && tile instanceof TileLightRelay) { if(world.getBlockState(pos).getValue(BotaniaStateProps.LUMINIZER_VARIANT) == LuminizerVariant.DETECTOR) { world.setBlockState(pos, world.getBlockState(pos).withProperty(BotaniaStateProps.POWERED, true), 1 | 2); world.scheduleUpdate(pos, tile.getBlockType(), tile.getBlockType().tickRate(world)); } TileLightRelay relay = (TileLightRelay) tile; BlockPos bind = relay.getNextDestination(); if(bind != null && relay.isValidBinding()) { setExit(bind); return; } } for(Entity e : getPassengers()) { e.dismountRidingEntity(); if(e instanceof EntityPlayerMP) ((EntityPlayerMP) e).connection.setPlayerLocation(posX, posY, posZ, e.rotationYaw, e.rotationPitch); else e.setPosition(posX, posY, posZ); } setDead(); } else { Vector3 thisVec = Vector3.fromEntity(this); Vector3 motVec = thisVec.negate().add(exitPos.getX() + 0.5, exitPos.getY() + 0.5, exitPos.getZ() + 0.5).normalize().multiply(0.5); Color color; int count = 4; for(int i = 0; i < count; i++) { color = Color.getHSBColor(ticksExisted / 36F + 1F / count * i, 1F, 1F); double rad = Math.PI * 2.0 / count * i + ticksExisted / Math.PI; double cos = Math.cos(rad); double sin = Math.sin(rad); double s = 0.4; Botania.proxy.sparkleFX(posX + cos * s, posY - 0.5, posZ + sin * s, color.getRed() / 255F, color.getGreen() / 255F, color.getBlue() / 255F, 1.2F, 10); } posX += motVec.x; posY += motVec.y; posZ += motVec.z; } } @Override public boolean shouldRiderSit() { return false; } @Override public boolean attackEntityFrom(@Nonnull DamageSource source, float damage) { return false; } @Override protected void readEntityFromNBT(@Nonnull NBTTagCompound cmp) { setExit(new BlockPos(cmp.getInteger(TAG_EXIT_X), cmp.getInteger(TAG_EXIT_Y), cmp.getInteger(TAG_EXIT_Z))); } @Override protected void writeEntityToNBT(@Nonnull NBTTagCompound cmp) { BlockPos exit = getExitPos(); cmp.setInteger(TAG_EXIT_X, exit.getX()); cmp.setInteger(TAG_EXIT_Y, exit.getY()); cmp.setInteger(TAG_EXIT_Z, exit.getZ()); } public BlockPos getExitPos() { return dataManager.get(EXIT_POS); } public void setExit(BlockPos pos) { dataManager.set(EXIT_POS, pos); } } }