1
0
Fork 0

Faster portal detection

main
Marissa Staib 6 years ago
parent 52b937f7d2
commit 289db444d0
  1. 1
      TODO
  2. 53
      src/main/kotlin/net/deliciousreya/minecraftportal/MinecraftPortalPlugin.kt
  3. 129
      src/main/kotlin/net/deliciousreya/minecraftportal/PortalFrame.kt
  4. 3
      src/main/kotlin/net/deliciousreya/minecraftportal/extensions/Vector.kt

@ -7,3 +7,4 @@
* teleport everyone inside the portal, not just the person who closed the door * teleport everyone inside the portal, not just the person who closed the door
* interrupt teleportation if portal is destroyed mid-teleportation * interrupt teleportation if portal is destroyed mid-teleportation
* save any effects that would be overwritten by teleportation (including to disk!), and put them back when the player opens the door or arrives * save any effects that would be overwritten by teleportation (including to disk!), and put them back when the player opens the door or arrives
* add portal particles and ambient sounds to the teleporter

@ -31,11 +31,11 @@ class MinecraftPortalPlugin() : JavaPlugin(), Listener
fun onBlockPlaced(e: BlockPlaceEvent) { fun onBlockPlaced(e: BlockPlaceEvent) {
if (e.block.type in PortalFrame.State.INACTIVE.allValidBlocks) { if (e.block.type in PortalFrame.State.INACTIVE.allValidBlocks) {
val portalScanResults = findPortalFrameConnectedTo(e.block, PortalFrame.State.INACTIVE) val portalScanResults = findPortalFrameConnectedTo(e.block, PortalFrame.State.INACTIVE)
if (portalScanResults.frame != null) { if (portalScanResults != null) {
logger.info("found portal frame, creating portal") logger.info("found portal frame, creating portal")
portalScanResults.frame.color() portalScanResults.color()
e.block.world.playSound(portalScanResults.frame.portalCenter, Sound.BLOCK_BEACON_ACTIVATE, 20f, 1f) e.block.world.playSound(portalScanResults.portalCenter, Sound.BLOCK_BEACON_ACTIVATE, 20f, 1f)
e.block.world.spawnParticle(Particle.SPELL, portalScanResults.frame.portalCenter, 40) e.block.world.spawnParticle(Particle.SPELL, portalScanResults.portalCenter, 40)
} else { } else {
logger.info("no portal frame found matching placed block, ignoring") logger.info("no portal frame found matching placed block, ignoring")
} }
@ -68,11 +68,6 @@ class MinecraftPortalPlugin() : JavaPlugin(), Listener
onDestroyedBlock(e.block) onDestroyedBlock(e.block)
} }
@EventHandler
fun onSpongeAbsorb(e: SpongeAbsorbEvent) {
e.blocks.forEach { blockstate -> onDestroyedBlock(blockstate.location.block) }
}
@EventHandler @EventHandler
fun onBlockBreak(e:BlockBreakEvent) { fun onBlockBreak(e:BlockBreakEvent) {
onDestroyedBlock(e.block) onDestroyedBlock(e.block)
@ -83,11 +78,6 @@ class MinecraftPortalPlugin() : JavaPlugin(), Listener
onDestroyedBlock(e.block) onDestroyedBlock(e.block)
} }
@EventHandler
fun onPlayerFillBucket(e: PlayerBucketFillEvent) {
onDestroyedBlock(e.blockClicked)
}
@EventHandler @EventHandler
fun onPlayerInteract(e: PlayerInteractEvent) { fun onPlayerInteract(e: PlayerInteractEvent) {
if (e.clickedBlock == null || e.action != Action.RIGHT_CLICK_BLOCK) { if (e.clickedBlock == null || e.action != Action.RIGHT_CLICK_BLOCK) {
@ -97,42 +87,39 @@ class MinecraftPortalPlugin() : JavaPlugin(), Listener
if(block.type !in DOOR_TYPES) { if(block.type !in DOOR_TYPES) {
return return
} }
val portalScanResults = findPortalFrameConnectedTo(block, PortalFrame.State.ACTIVE) val portalScanResults = findPortalFrameConnectedTo(block, PortalFrame.State.ACTIVE) ?: return
if (portalScanResults.frame == null) {
return
}
val door = block.blockData as Door val door = block.blockData as Door
if (!portalScanResults.isStandingInPortal(e.player.location)) {
if (!portalScanResults.frame.isStandingInPortal(e.player.location)) {
e.isCancelled = true e.isCancelled = true
return return
} }
if (door.isOpen) { if (!door.isOpen) {
// door is about to be closed, so drug the player
e.player.playSound(e.player.location, Sound.BLOCK_PORTAL_TRIGGER, 20f, 1f)
e.player.addPotionEffect(PotionEffect(PotionEffectType.CONFUSION, 200, 10, false, false, false))
e.player.addPotionEffect(PotionEffect(PotionEffectType.BLINDNESS, 200, 1, false, false, false))
e.player.addPotionEffect(PotionEffect(PotionEffectType.INVISIBILITY, 200, 1, false, false, false))
} else {
// door is about to be opened, so undrug the player // door is about to be opened, so undrug the player
e.player.stopSound(Sound.BLOCK_PORTAL_TRIGGER) e.player.stopSound(Sound.BLOCK_PORTAL_TRIGGER)
e.player.removePotionEffect(PotionEffectType.BLINDNESS) e.player.removePotionEffect(PotionEffectType.BLINDNESS)
e.player.removePotionEffect(PotionEffectType.CONFUSION) e.player.removePotionEffect(PotionEffectType.CONFUSION)
e.player.removePotionEffect(PotionEffectType.INVISIBILITY) e.player.removePotionEffect(PotionEffectType.INVISIBILITY)
} else {
// door is about to be closed, so drug the player
e.player.playSound(e.player.location, Sound.BLOCK_PORTAL_TRIGGER, 20f, 1f)
e.player.addPotionEffect(PotionEffect(PotionEffectType.CONFUSION, 200, 1, false, false, false))
e.player.addPotionEffect(PotionEffect(PotionEffectType.BLINDNESS, 200, 1, false, false, false))
e.player.addPotionEffect(PotionEffect(PotionEffectType.INVISIBILITY, 200, 1, false, false, false))
} }
} }
fun onDestroyedBlock(block: Block) { fun onDestroyedBlock(block: Block) {
if (block.type in PortalFrame.State.ACTIVE.allValidBlocks) { if (block.type !in PortalFrame.State.ACTIVE.allValidBlocks) {
return
}
val portalScanResults = findPortalFrameConnectedTo(block, PortalFrame.State.ACTIVE) val portalScanResults = findPortalFrameConnectedTo(block, PortalFrame.State.ACTIVE)
if (portalScanResults.frame != null) { if (portalScanResults != null) {
logger.info("found portal frame matching destroyed block, deactivating") logger.info("found portal frame matching destroyed block, deactivating")
portalScanResults.frame.uncolor() portalScanResults.uncolor()
block.world.playSound(portalScanResults.frame.portalCenter, Sound.BLOCK_BEACON_DEACTIVATE, 20f, 1f) block.world.playSound(portalScanResults.portalCenter, Sound.BLOCK_BEACON_DEACTIVATE, 20f, 1f)
block.world.spawnParticle(Particle.SMOKE_NORMAL, portalScanResults.frame.portalCenter, 40) block.world.spawnParticle(Particle.SMOKE_NORMAL, portalScanResults.portalCenter, 40)
} else { } else {
logger.info("no portal frame found matching destroyed block, ignoring") logger.info("no portal frame found matching destroyed block, ignoring")
} }
} }
}
} }

@ -10,6 +10,7 @@ import net.deliciousreya.minecraftportal.extensions.*
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.Material.* import org.bukkit.Material.*
import org.bukkit.block.BlockFace import org.bukkit.block.BlockFace
import org.bukkit.material.Directional
val MINERAL_TYPES: ImmutableMap<Material, Material> = ImmutableMap.Builder<Material, Material>() val MINERAL_TYPES: ImmutableMap<Material, Material> = ImmutableMap.Builder<Material, Material>()
.put(COAL_BLOCK, BLACK_STAINED_GLASS) .put(COAL_BLOCK, BLACK_STAINED_GLASS)
@ -24,129 +25,137 @@ val MINERAL_TYPES: ImmutableMap<Material, Material> = ImmutableMap.Builder<Mater
val DOOR_TYPES: ImmutableSet<Material> = ImmutableSet.of(ACACIA_DOOR, BIRCH_DOOR, DARK_OAK_DOOR, IRON_DOOR, JUNGLE_DOOR, OAK_DOOR, SPRUCE_DOOR) 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): PortalScanResults { fun findPortalFrameConnectedTo(block:Block, state:PortalFrame.State): PortalFrame? {
var bestScan:PortalScanResults? = null
when { when {
block.type in state.glassBlocks -> for (direction in PortalFrame.EntranceDirection.values()) { block.type in state.glassBlocks -> for (direction in PortalFrame.EntranceDirection.values()) {
for (vector in direction.glassOffsets) { for (vector in direction.glassOffsets) {
val scanResult = checkPortalFrameAt(block.location.subtract(vector), direction, state) val scanResult = checkPortalFrameAt(block.location.subtract(vector), direction, state)
if (scanResult.frame != null) { if (scanResult != null) {
return scanResult return scanResult
} else if (bestScan == null || scanResult > bestScan) {
bestScan = scanResult
} }
} }
} }
block.type in state.airBlocks -> for (direction in PortalFrame.EntranceDirection.values()) { block.type in state.doorBlocks -> for (direction in PortalFrame.EntranceDirection.values()) {
for (vector in direction.airOffsets) { for (vector in direction.doorOffsets) {
val scanResult = checkPortalFrameAt(block.location.subtract(vector), direction, state) val scanResult = checkPortalFrameAt(block.location.subtract(vector), direction, state)
if (scanResult.frame != null) { if (scanResult != null) {
return scanResult return scanResult
} else if (bestScan == null || scanResult > bestScan) {
bestScan = scanResult
} }
} }
} }
block.type in state.mineralBlocks -> for (direction in PortalFrame.EntranceDirection.values()) { block.type in state.mineralBlocks -> for (direction in PortalFrame.EntranceDirection.values()) {
val scanResult = checkPortalFrameAt(block.location.subtract(direction.mineralOffset), direction, state) val scanResult = checkPortalFrameAt(block.location.subtract(direction.mineralOffset), direction, state)
if (scanResult.frame != null) { if (scanResult != null) {
return scanResult return scanResult
} else if (bestScan == null || scanResult > bestScan) {
bestScan = scanResult
} }
} }
} }
return bestScan!! return null
} }
/** Detects whether there is a portal frame in the given state at the given location, extending in the given direction. */ /** 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 { fun checkPortalFrameAt(location:Location, direction:PortalFrame.EntranceDirection, state:PortalFrame.State): PortalFrame? {
val matches = ImmutableList.Builder<Block>() val mineral = (location + direction.mineralOffset).block
val nonmatches = ImmutableList.Builder<Block>() if (mineral.type !in state.mineralBlocks) {
return null
}
for (vector in direction.glassOffsets) { for (vector in direction.glassOffsets) {
val block = (location + vector).block val block = (location + vector).block
(if (block.type in state.glassBlocks) matches else nonmatches).add(block) if (block.type !in state.glassBlocks) {
return null
}
} }
for (vector in direction.airOffsets) { for (vector in direction.doorOffsets) {
val block = (location + vector).block val block = (location + vector).block
(if (block.type in state.airBlocks) matches else nonmatches).add(block) if (block.type !in state.doorBlocks || (block.blockData is Directional && (block.blockData as Directional).facing != direction.doorDirection)) {
return null
} }
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<Block>, val nonMatchingBlocks:List<Block>) : Comparable<PortalScanResults> {
override fun compareTo(other:PortalScanResults): Int {
return matchingBlocks.size - other.matchingBlocks.size
} }
return PortalFrame(location, direction)
} }
/** Information about a portal frame. */ /** Information about a portal frame. */
data class PortalFrame(val lowerLeftCorner: Location, val direction: EntranceDirection) { data class PortalFrame(val lowerLeftFrontCorner: Location, val direction: EntranceDirection) {
val UP = Vector(0, 1, 0) enum class State(val glassBlocks: ImmutableSet<Material>, val doorBlocks: ImmutableSet<Material>, val mineralBlocks: ImmutableSet<Material>) {
enum class State(val glassBlocks: ImmutableSet<Material>, val airBlocks: ImmutableSet<Material>, val mineralBlocks: ImmutableSet<Material>) {
ACTIVE(ImmutableSet.copyOf(MINERAL_TYPES.values), DOOR_TYPES, ImmutableSet.copyOf(MINERAL_TYPES.keys)), ACTIVE(ImmutableSet.copyOf(MINERAL_TYPES.values), DOOR_TYPES, ImmutableSet.copyOf(MINERAL_TYPES.keys)),
INACTIVE(ImmutableSet.of(GLASS), DOOR_TYPES, ImmutableSet.copyOf(MINERAL_TYPES.keys)); INACTIVE(ImmutableSet.of(GLASS), DOOR_TYPES, ImmutableSet.copyOf(MINERAL_TYPES.keys));
val allValidBlocks = ImmutableSet.Builder<Material>().addAll(glassBlocks).addAll(airBlocks).addAll(mineralBlocks).build() val allValidBlocks = ImmutableSet.Builder<Material>().addAll(glassBlocks).addAll(doorBlocks).addAll(mineralBlocks).build()
} }
/** The direction along which a portal frame extends. */ /** The direction along which a portal frame extends. */
enum class EntranceDirection(toRight: Vector, toBack: Vector, doorDirection: BlockFace) { enum class EntranceDirection(toRight: Vector, toBack: Vector, val doorDirection: BlockFace) {
NORTH(Vector(1, 0, 0), Vector(0, 0, 1), BlockFace.NORTH), NORTH(Vector(1, 0, 0), Vector(0, 0, 1), BlockFace.NORTH),
SOUTH(Vector(1, 0, 0), Vector(0, 0, -1), BlockFace.SOUTH), SOUTH(Vector(1, 0, 0), Vector(0, 0, -1), BlockFace.SOUTH),
EAST(Vector(0, 0, 1), Vector(-1, 0, 0), BlockFace.EAST), EAST(Vector(0, 0, 1), Vector(-1, 0, 0), BlockFace.EAST),
WEST(Vector(0, 0, 1), Vector(1, 0, 0), BlockFace.WEST); WEST(Vector(0, 0, 1), Vector(1, 0, 0), BlockFace.WEST);
val glassOffsets: ImmutableList<Vector> = ImmutableList.of( val glassOffsets: ImmutableList<Vector> = ImmutableList.of(
Vector(0, 0, 0), // check corners first:
Vector(0, 1, 0), // bottom left, front
Vector(0, 2, 0), ZERO,
Vector(0, 3, 0), // top right, back
Vector(0, 3, 0) + toRight, UP * 3 + toBack + toRight * 2,
// bottom right, front
toRight * 2, toRight * 2,
Vector(0, 1, 0) + toRight * 2, // top right, front
Vector(0, 2, 0) + toRight * 2, UP * 3 + toRight * 2,
Vector(0, 3, 0) + toRight * 2, // top left, front
Vector(0, 0, 0) + toBack, UP * 3,
Vector(0, 1, 0) + toBack, // bottom left, back
Vector(0, 2, 0) + toBack, toBack,
Vector(0, 3, 0) + toBack, // top left, back
Vector(0, 0, 0) + toBack + toRight, UP * 3 + toBack,
Vector(0, 1, 0) + toBack + toRight, // bottom right, back
Vector(0, 2, 0) + toBack + toRight, toBack + toRight * 2,
Vector(0, 3, 0) + toBack + toRight,
Vector(0, 0, 0) + toBack + toRight * 2, // then do front walls
Vector(0, 1, 0) + toBack + toRight * 2, // left front wall
Vector(0, 2, 0) + toBack + toRight * 2, UP,
Vector(0, 3, 0) + toBack + toRight * 2 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 mineralOffset: Vector = toRight
val airOffsets: ImmutableList<Vector> = ImmutableList.of( val doorOffsets: ImmutableList<Vector> = ImmutableList.of(
Vector(0, 1, 0) + toRight, Vector(0, 1, 0) + toRight,
Vector(0, 2, 0) + toRight Vector(0, 2, 0) + toRight
) )
} }
val portalCenter = lowerLeftCorner + direction.airOffsets[0] val portalCenter = lowerLeftFrontCorner + direction.doorOffsets[0]
fun isStandingInPortal(location: Location): Boolean { fun isStandingInPortal(location: Location): Boolean {
return direction.airOffsets.any { offset -> location.block.location - offset == lowerLeftCorner } return direction.doorOffsets.any { offset -> location.block.location - offset == lowerLeftFrontCorner }
} }
fun color() { fun color() {
val mineral = (lowerLeftCorner + direction.mineralOffset).block val mineral = (lowerLeftFrontCorner + direction.mineralOffset).block
for (offset in direction.glassOffsets) { for (offset in direction.glassOffsets) {
(lowerLeftCorner + offset).block.type = MINERAL_TYPES.getOrDefault(mineral.type, BROWN_STAINED_GLASS) (lowerLeftFrontCorner + offset).block.type = MINERAL_TYPES.getOrDefault(mineral.type, BROWN_STAINED_GLASS)
} }
} }
fun uncolor() { fun uncolor() {
for (offset in direction.glassOffsets) { for (offset in direction.glassOffsets) {
(lowerLeftCorner + offset).block.type = GLASS (lowerLeftFrontCorner + offset).block.type = GLASS
} }
} }
} }

@ -2,6 +2,9 @@ package net.deliciousreya.minecraftportal.extensions
import org.bukkit.util.Vector import org.bukkit.util.Vector
val ZERO = Vector(0, 0, 0)
val UP = Vector(0, 1, 0)
operator fun Vector.plus(v:Vector):Vector { operator fun Vector.plus(v:Vector):Vector {
return this.clone().add(v) return this.clone().add(v)
} }

Loading…
Cancel
Save