package net.deliciousreya.minecraftportal import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableSet import org.bukkit.Location import org.bukkit.block.Block import org.bukkit.util.Vector import net.deliciousreya.minecraftportal.extensions.* import org.bukkit.Material import org.bukkit.Material.* import org.bukkit.block.BlockFace val MINERAL_TYPES: ImmutableMap = ImmutableMap.Builder() .put(COAL_BLOCK, BLACK_STAINED_GLASS) .put(REDSTONE_BLOCK, RED_STAINED_GLASS) .put(LAPIS_BLOCK, BLUE_STAINED_GLASS) .put(GOLD_BLOCK, YELLOW_STAINED_GLASS) .put(DIAMOND_BLOCK, CYAN_STAINED_GLASS) .put(EMERALD_BLOCK, GREEN_STAINED_GLASS) .put(IRON_BLOCK, GRAY_STAINED_GLASS) .put(QUARTZ_BLOCK, WHITE_STAINED_GLASS) .build() val DOOR_TYPES: ImmutableSet = ImmutableSet.of(ACACIA_DOOR, BIRCH_DOOR, DARK_OAK_DOOR, IRON_DOOR, JUNGLE_DOOR, OAK_DOOR, SPRUCE_DOOR) fun findPortalFrameConnectedTo(block:Block, state:PortalFrame.State): PortalScanResults { var bestScan:PortalScanResults? = null when { block.type in state.glassBlocks -> for (direction in PortalFrame.EntranceDirection.values()) { for (vector in direction.glassOffsets) { val scanResult = checkPortalFrameAt(block.location.subtract(vector), direction, state) if (scanResult.frame != null) { return scanResult } else if (bestScan == null || scanResult > bestScan) { bestScan = scanResult } } } block.type in state.airBlocks -> for (direction in PortalFrame.EntranceDirection.values()) { for (vector in direction.airOffsets) { val scanResult = checkPortalFrameAt(block.location.subtract(vector), direction, state) if (scanResult.frame != null) { return scanResult } else if (bestScan == null || scanResult > bestScan) { bestScan = scanResult } } } block.type in state.mineralBlocks -> for (direction in PortalFrame.EntranceDirection.values()) { val scanResult = checkPortalFrameAt(block.location.subtract(direction.mineralOffset), direction, state) if (scanResult.frame != null) { return scanResult } else if (bestScan == null || scanResult > bestScan) { bestScan = scanResult } } } return bestScan!! } /** Detects whether there is a portal frame in the given state at the given location, extending in the given direction. */ fun checkPortalFrameAt(location:Location, direction:PortalFrame.EntranceDirection, state:PortalFrame.State): PortalScanResults { val matches = ImmutableList.Builder() val nonmatches = ImmutableList.Builder() for (vector in direction.glassOffsets) { val block = (location + vector).block (if (block.type in state.glassBlocks) matches else nonmatches).add(block) } for (vector in direction.airOffsets) { val block = (location + vector).block (if (block.type in state.airBlocks) matches else nonmatches).add(block) } val block = (location + direction.mineralOffset).block (if (block.type in state.mineralBlocks) matches else nonmatches).add(block) val nonmatchesBuilt = nonmatches.build() return PortalScanResults(if (nonmatchesBuilt.isEmpty()) {PortalFrame(location, direction)} else {null}, matches.build(), nonmatchesBuilt) } data class PortalScanResults(val frame: PortalFrame?, val matchingBlocks:List, val nonMatchingBlocks:List) : Comparable { override fun compareTo(other:PortalScanResults): Int { return matchingBlocks.size - other.matchingBlocks.size } } /** Information about a portal frame. */ data class PortalFrame(val lowerLeftCorner: Location, val direction: EntranceDirection) { val UP = Vector(0, 1, 0) enum class State(val glassBlocks: ImmutableSet, val airBlocks: ImmutableSet, val mineralBlocks: ImmutableSet) { ACTIVE(ImmutableSet.copyOf(MINERAL_TYPES.values), DOOR_TYPES, ImmutableSet.copyOf(MINERAL_TYPES.keys)), INACTIVE(ImmutableSet.of(GLASS), DOOR_TYPES, ImmutableSet.copyOf(MINERAL_TYPES.keys)); val allValidBlocks = ImmutableSet.Builder().addAll(glassBlocks).addAll(airBlocks).addAll(mineralBlocks).build() } /** The direction along which a portal frame extends. */ enum class EntranceDirection(toRight: Vector, toBack: Vector, doorDirection: BlockFace) { NORTH(Vector(1, 0, 0), Vector(0, 0, 1), BlockFace.NORTH), SOUTH(Vector(1, 0, 0), Vector(0, 0, -1), BlockFace.SOUTH), EAST(Vector(0, 0, 1), Vector(-1, 0, 0), BlockFace.EAST), WEST(Vector(0, 0, 1), Vector(1, 0, 0), BlockFace.WEST); val glassOffsets: ImmutableList = ImmutableList.of( Vector(0, 0, 0), Vector(0, 1, 0), Vector(0, 2, 0), Vector(0, 3, 0), Vector(0, 3, 0) + toRight, toRight * 2, Vector(0, 1, 0) + toRight * 2, Vector(0, 2, 0) + toRight * 2, Vector(0, 3, 0) + toRight * 2, Vector(0, 0, 0) + toBack, Vector(0, 1, 0) + toBack, Vector(0, 2, 0) + toBack, Vector(0, 3, 0) + toBack, Vector(0, 0, 0) + toBack + toRight, Vector(0, 1, 0) + toBack + toRight, Vector(0, 2, 0) + toBack + toRight, Vector(0, 3, 0) + toBack + toRight, Vector(0, 0, 0) + toBack + toRight * 2, Vector(0, 1, 0) + toBack + toRight * 2, Vector(0, 2, 0) + toBack + toRight * 2, Vector(0, 3, 0) + toBack + toRight * 2 ) val mineralOffset: Vector = toRight val airOffsets: ImmutableList = ImmutableList.of( Vector(0, 1, 0) + toRight, Vector(0, 2, 0) + toRight ) } val portalCenter = lowerLeftCorner + direction.airOffsets[0] fun isStandingInPortal(location: Location): Boolean { return direction.airOffsets.any { offset -> location.block.location - offset == lowerLeftCorner } } fun color() { val mineral = (lowerLeftCorner + direction.mineralOffset).block for (offset in direction.glassOffsets) { (lowerLeftCorner + offset).block.type = MINERAL_TYPES.getOrDefault(mineral.type, BROWN_STAINED_GLASS) } } fun uncolor() { for (offset in direction.glassOffsets) { (lowerLeftCorner + offset).block.type = GLASS } } }