package net.deliciousreya.minecraftportal.model 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 net.deliciousreya.minecraftportal.proto.PortalSaveDataProtos import org.bukkit.Material import org.bukkit.Material.* import org.bukkit.Particle import org.bukkit.Sound import org.bukkit.block.BlockFace import org.bukkit.block.data.Directional import org.bukkit.block.data.Openable import org.bukkit.block.data.type.Door 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): PortalFrame? { 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 != null) { return scanResult } } } block.type in state.doorBlocks -> for (direction in PortalFrame.EntranceDirection.values()) { for (vector in direction.doorOffsets) { val scanResult = checkPortalFrameAt( block.location.subtract(vector), direction, state ) if (scanResult != null) { return scanResult } } } block.type in state.mineralBlocks -> for (direction in PortalFrame.EntranceDirection.values()) { val scanResult = checkPortalFrameAt( block.location.subtract(direction.mineralOffset), direction, state ) if (scanResult != null) { return scanResult } } } return null } /** 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): PortalFrame? { val mineral = (location + direction.mineralOffset).block if (mineral.type !in state.mineralBlocks) { return null } for (vector in direction.glassOffsets) { val block = (location + vector).block if (block.type !in state.glassBlocks) { return null } } for (vector in direction.doorOffsets) { val block = (location + vector).block val door = block.blockData if (block.type !in state.doorBlocks || (door is Directional && door.facing != direction.doorDirection)) { return null } } return PortalFrame(location, direction) } fun PortalSaveDataProtos.Portal.Direction.toDirection(): PortalFrame.EntranceDirection { for (direction in PortalFrame.EntranceDirection.values()) { if (direction.protoEnum == this) { return direction } } throw IllegalStateException("Bad enum value: $this") } /** Information about a portal frame. */ data class PortalFrame(val lowerLeftFrontCorner: Location, val direction: EntranceDirection) { enum class State(val glassBlocks: ImmutableSet, val doorBlocks: 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 = ImmutableSet.Builder().addAll(glassBlocks).addAll(doorBlocks).addAll(mineralBlocks).build() } /** The direction along which a portal frame extends. */ enum class EntranceDirection(toRight: Vector, toBack: Vector, val doorDirection: BlockFace, val protoEnum: PortalSaveDataProtos.Portal.Direction) { NORTH(Vector(1, 0, 0), Vector(0, 0, 1), BlockFace.SOUTH, PortalSaveDataProtos.Portal.Direction.NORTH), SOUTH(Vector(1, 0, 0), Vector(0, 0, -1), BlockFace.NORTH, PortalSaveDataProtos.Portal.Direction.SOUTH), EAST(Vector(0, 0, 1), Vector(-1, 0, 0), BlockFace.WEST, PortalSaveDataProtos.Portal.Direction.EAST), WEST(Vector(0, 0, 1), Vector(1, 0, 0), BlockFace.EAST, PortalSaveDataProtos.Portal.Direction.WEST); val glassOffsets: ImmutableList = ImmutableList.of( // check corners first: // bottom left, front ZERO, // top right, back UP * 3 + toBack + toRight * 2, // bottom right, front toRight * 2, // top right, front UP * 3 + toRight * 2, // top left, front UP * 3, // bottom left, back toBack, // top left, back UP * 3 + toBack, // bottom right, back toBack + toRight * 2, // then do front walls // left front wall UP, UP * 2, // top front wall UP * 3 + toRight, // right front wall UP + toRight * 2, UP * 2 + toRight * 2, // now do the rest of the back // left back wall UP + toBack, UP * 2 + toBack, // center back wall toBack + toRight, UP + toBack + toRight, UP * 2 + toBack + toRight, UP * 3 + toBack + toRight, // right back wall UP + toBack + toRight * 2, UP * 2 + toBack + toRight * 2 ) val mineralOffset: Vector = toRight val doorOffsets: ImmutableList = ImmutableList.of( Vector(0, 1, 0) + toRight, Vector(0, 2, 0) + toRight ) fun toProto():PortalSaveDataProtos.Portal.Direction { return this.protoEnum } } val portalCenter = lowerLeftFrontCorner + direction.doorOffsets[0] fun isStandingInPortal(location: Location): Boolean { return direction.doorOffsets.any { offset -> location.block.location - offset == lowerLeftFrontCorner } } val mineral = (lowerLeftFrontCorner + direction.mineralOffset).block.type fun open() { val block = portalCenter.block val data = block.blockData if (data is Openable) { data.isOpen = true block.blockData = data } } fun close() { val block = portalCenter.block val data = block.blockData if (data is Openable) { data.isOpen = false block.blockData = data } } fun activate() { val mineral = (lowerLeftFrontCorner + direction.mineralOffset).block for (offset in direction.glassOffsets) { (lowerLeftFrontCorner + offset).block.type = MINERAL_TYPES.getOrDefault(mineral.type, BROWN_STAINED_GLASS) } val midCenter = portalCenter + MID_BLOCK portalCenter.world?.playSound(midCenter, Sound.BLOCK_BEACON_ACTIVATE, 20f, 1f) portalCenter.world?.spawnParticle(Particle.SPELL, midCenter, 75) open() } fun deactivate() { for (offset in direction.glassOffsets) { (lowerLeftFrontCorner + offset).block.type = GLASS } val midCenter = portalCenter + MID_BLOCK portalCenter.world?.playSound(midCenter, Sound.BLOCK_BEACON_DEACTIVATE, 20f, 1f) portalCenter.world?.spawnParticle(Particle.SMOKE_NORMAL, midCenter, 75) } }