package openblocks.common;
import com.google.common.base.Preconditions;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import openblocks.Config;
import openblocks.api.IElevatorBlock;
import openblocks.api.IElevatorBlock.PlayerRotation;
import openblocks.events.ElevatorActionEvent;
import openmods.movement.PlayerMovementEvent;
import openmods.utils.EnchantmentUtils;
public class ElevatorActionHandler {
private static class SearchResult {
public final int level;
public final PlayerRotation rotation;
public SearchResult(int level, PlayerRotation rotation) {
this.level = level;
this.rotation = rotation;
}
}
private static boolean canTeleportPlayer(World world, int x, int y, int z) {
Block block = world.getBlock(x, y, z);
if (block == null || block.isAir(world, x, y, z)) return true;
if (!Config.irregularBlocksArePassable) return false;
final AxisAlignedBB aabb = block.getCollisionBoundingBoxFromPool(world, x, y, z);
return aabb == null || aabb.getAverageEdgeLength() < 0.7;
}
private static boolean canTeleportPlayer(EntityPlayer entity, World world, int x, int y, int z) {
final AxisAlignedBB aabb = entity.boundingBox;
double height = Math.abs(aabb.maxY - aabb.minY);
int blockHeight = Math.max(1, MathHelper.ceiling_double_int(height));
for (int dy = 0; dy < blockHeight; dy++)
if (!canTeleportPlayer(world, x, y + dy, z)) return false;
return true;
}
private static SearchResult findLevel(EntityPlayer player, World world, int x, int y, int z, ForgeDirection direction) {
Preconditions.checkArgument(direction == ForgeDirection.UP
|| direction == ForgeDirection.DOWN, "Must be either up or down... for now");
final IElevatorBlock thisElevatorBlock = (IElevatorBlock)world.getBlock(x, y, z);
final int thisColor = thisElevatorBlock.getColor(world, x, y, z);
int blocksInTheWay = 0;
final int delta = direction.offsetY;
for (int i = 0; i < Config.elevatorTravelDistance; i++) {
y += delta;
if (!world.blockExists(x, y, z)) break;
if (world.isAirBlock(x, y, z)) continue;
Block block = world.getBlock(x, y, z);
if (block instanceof IElevatorBlock) {
final IElevatorBlock otherElevatorBlock = (IElevatorBlock)block;
final int otherColor = otherElevatorBlock.getColor(world, x, y, z);
if (otherColor == thisColor && canTeleportPlayer(player, world, x, y + 1, z)) {
final PlayerRotation rotation = otherElevatorBlock.getRotation(world, x, y, z);
return new SearchResult(y, rotation);
}
}
if (!Config.elevatorIgnoreBlocks) {
ElevatorBlockRules.Action action = ElevatorBlockRules.instance.getActionForBlock(block);
switch (action) {
case ABORT:
return null;
case IGNORE:
continue;
case INCREMENT:
default:
break;
}
if (++blocksInTheWay > Config.elevatorMaxBlockPassCount) break;
}
}
return null;
}
private static void activate(EntityPlayer player, World world, int x, int y, int z, ForgeDirection dir) {
SearchResult result = findLevel(player, world, x, y, z, dir);
if (result != null) {
boolean doTeleport = checkXpCost(player, result);
if (doTeleport) {
if (result.rotation != PlayerRotation.NONE) player.rotationYaw = getYaw(result.rotation);
if (Config.elevatorCenter) player.setPositionAndUpdate(x + 0.5, result.level + 1.1, z + 0.5);
else player.setPositionAndUpdate(player.posX, result.level + 1.1, player.posZ);
world.playSoundAtEntity(player, "openblocks:elevator.activate", 1, 1);
}
}
}
private static float getYaw(PlayerRotation rotation) {
switch (rotation) {
case EAST:
return 90;
case NORTH:
return 0;
case SOUTH:
return 180;
case WEST:
return -90;
default:
return 0;
}
}
protected static boolean checkXpCost(EntityPlayer player, SearchResult result) {
int distance = (int)Math.abs(player.posY - result.level);
if (Config.elevatorXpDrainRatio == 0 || player.capabilities.isCreativeMode) return true;
int playerXP = EnchantmentUtils.getPlayerXP(player);
int neededXP = MathHelper.ceiling_double_int(Config.elevatorXpDrainRatio * distance);
if (playerXP >= neededXP) {
EnchantmentUtils.addPlayerXP(player, -neededXP);
return true;
}
return false;
}
@SubscribeEvent
public void onElevatorEvent(ElevatorActionEvent evt) {
final World world = evt.getWorld();
final int x = evt.xCoord;
final int y = evt.yCoord;
final int z = evt.zCoord;
if (!(world.getBlock(x, y, z) instanceof IElevatorBlock)) return;
if (evt.sender != null) {
if (evt.sender.ridingEntity != null) return;
switch (evt.type) {
case JUMP:
activate(evt.sender, world, x, y, z, ForgeDirection.UP);
break;
case SNEAK:
activate(evt.sender, world, x, y, z, ForgeDirection.DOWN);
break;
}
}
}
@SubscribeEvent
@SideOnly(Side.CLIENT)
public void onPlayerMovement(PlayerMovementEvent evt) {
final EntityPlayer player = evt.entityPlayer;
if (player == null) return;
final World world = player.worldObj;
if (world == null) return;
final int x = MathHelper.floor_double(player.posX);
final int y = MathHelper.floor_double(player.boundingBox.minY) - 1;
final int z = MathHelper.floor_double(player.posZ);
Block block = world.getBlock(x, y, z);
if (block instanceof IElevatorBlock) new ElevatorActionEvent(world.provider.dimensionId, x, y, z, evt.type).sendToServer();
}
}