1
0
Fork 0
The portal creating plugin for Minecraft.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

221 lines
8.2 KiB

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<Material, Material> = ImmutableMap.Builder<Material, Material>()
.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<Material> = 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<Material>, val doorBlocks: ImmutableSet<Material>, val mineralBlocks: ImmutableSet<Material>) {
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<Material> = ImmutableSet.Builder<Material>().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<Vector> = 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<Vector> = 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)
}
}