1
0
Fork 0

Portals are super pretty and functional!

main
Marissa Staib 6 years ago
parent 2ae528fc98
commit 8dbd61c1ef
  1. 6
      TODO
  2. 42
      src/main/kotlin/net/deliciousreya/minecraftportal/MinecraftPortalPlugin.kt
  3. 4
      src/main/kotlin/net/deliciousreya/minecraftportal/extensions/Vector.kt
  4. 21
      src/main/kotlin/net/deliciousreya/minecraftportal/model/Portal.kt
  5. 68
      src/main/kotlin/net/deliciousreya/minecraftportal/model/PortalDataStore.kt
  6. 56
      src/main/kotlin/net/deliciousreya/minecraftportal/model/PortalFrame.kt
  7. 35
      src/main/kotlin/net/deliciousreya/minecraftportal/model/PortalType.kt
  8. 118
      src/main/kotlin/net/deliciousreya/minecraftportal/model/Teleportation.kt

@ -1,7 +1,3 @@
* teleport from one to the other a set time after entering a teleportation chamber and closing the door
* automatically open the door after effects wear off
* teleport everyone inside the portal, not just the person who closed the door
* interrupt teleportation if portal is destroyed mid-teleportation, or if a new portal is constructed during teleportation, changing the destination
* cancel teleportation if someone moves out of the teleporter during teleportation (maybe prevent them from starting?) * cancel teleportation if someone moves out of the teleporter during teleportation (maybe prevent them from starting?)
* 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 when portals are paired * fix facing and location when teleporting in different directions

@ -7,18 +7,19 @@ import net.deliciousreya.minecraftportal.model.DOOR_TYPES
import net.deliciousreya.minecraftportal.model.PortalDataStore import net.deliciousreya.minecraftportal.model.PortalDataStore
import net.deliciousreya.minecraftportal.model.PortalFrame import net.deliciousreya.minecraftportal.model.PortalFrame
import net.deliciousreya.minecraftportal.model.findPortalFrameConnectedTo import net.deliciousreya.minecraftportal.model.findPortalFrameConnectedTo
import org.bukkit.Particle
import org.bukkit.Sound import org.bukkit.Sound
import org.bukkit.block.Block import org.bukkit.block.Block
import org.bukkit.block.data.type.Door import org.bukkit.block.data.type.Door
import org.bukkit.entity.Entity
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.event.block.* import org.bukkit.event.block.*
import org.bukkit.event.entity.EntityChangeBlockEvent import org.bukkit.event.entity.EntityChangeBlockEvent
import org.bukkit.event.entity.EntityExplodeEvent import org.bukkit.event.entity.EntityExplodeEvent
import org.bukkit.event.player.PlayerInteractEvent import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.event.player.PlayerMoveEvent
import org.bukkit.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType import org.bukkit.potion.PotionEffectType
class MinecraftPortalPlugin() : JavaPlugin(), Listener class MinecraftPortalPlugin() : JavaPlugin(), Listener
@ -28,6 +29,8 @@ class MinecraftPortalPlugin() : JavaPlugin(), Listener
override fun onEnable() { override fun onEnable() {
super.onEnable() super.onEnable()
portals.loadFromAndAutoSaveTo(server, dataFolder.resolve("portal_data.binproto"), dataFolder.resolve("portal_data.binproto.bak")) portals.loadFromAndAutoSaveTo(server, dataFolder.resolve("portal_data.binproto"), dataFolder.resolve("portal_data.binproto.bak"))
portals.validateWorldOnStartup(logger)
portals.launchEffectsOnStartup(this)
server.pluginManager.registerEvents(this, this) server.pluginManager.registerEvents(this, this)
} }
@ -51,6 +54,7 @@ class MinecraftPortalPlugin() : JavaPlugin(), Listener
if (otherPortal != null) { if (otherPortal != null) {
newPortal.open() newPortal.open()
otherPortal.open() otherPortal.open()
portals.startEffectsFor(newPortal, this)
} else { } else {
newPortal.ejectEntities() newPortal.ejectEntities()
newPortal.close() newPortal.close()
@ -113,26 +117,24 @@ class MinecraftPortalPlugin() : JavaPlugin(), Listener
return return
} }
if (!door.isOpen) { if (!door.isOpen) {
// door is about to be opened, so undrug the player portals.stopTeleporting(portal)
e.player.stopSound(Sound.BLOCK_PORTAL_TRIGGER)
val otherPortal = portals.getOtherPortal(frame = portal) ?: return
otherPortal.open()
/* e.player.removePotionEffect(PotionEffectType.BLINDNESS)
e.player.removePotionEffect(PotionEffectType.CONFUSION)
e.player.removePotionEffect(PotionEffectType.INVISIBILITY) */
} else { } else {
// door is about to be closed, so drug the player portals.startTeleporting(portal, this)
val otherPortal = portals.getOtherPortal(frame = portal) ?: return }
otherPortal.close() }
val relativeLocation = portal.getRelativeLocationFromAbsoluteLocation(location)
val destination = otherPortal.getAbsoluteLocationFromRelativeLocation(relativeLocation) fun onPlayerMove(e: PlayerMoveEvent) {
relativeLocation.world?.playSound(e.player.location, Sound.BLOCK_PORTAL_TRIGGER, 20f, 1f) val player = e.player
destination.world?.playSound(destination, Sound.BLOCK_PORTAL_TRAVEL, 20f, 1f) val oldLocation = e.from
e.player.teleport(destination) val portal = portals.isLocationInPortalChamber(oldLocation) ?: return
/* e.player.addPotionEffect(PotionEffect(PotionEffectType.CONFUSION, 200, 1, false, false, false)) val newLocation = e.to ?: return
e.player.addPotionEffect(PotionEffect(PotionEffectType.BLINDNESS, 200, 1, false, false, false)) if (oldLocation.world != newLocation.world || oldLocation.toVector() !in portal.portalInsideBoundingBox) {
e.player.addPotionEffect(PotionEffect(PotionEffectType.INVISIBILITY, 200, 1, false, false, false)) */ cancelTeleportFor(player)
}
} }
private fun cancelTeleportFor(entity: Entity) {
} }
fun onDestroyedBlock(block: Block) { fun onDestroyedBlock(block: Block) {

@ -34,3 +34,7 @@ operator fun Vector.times(v:Vector):Vector {
operator fun Vector.div(v:Vector):Vector { operator fun Vector.div(v:Vector):Vector {
return this.clone().divide(v) return this.clone().divide(v)
} }
operator fun Vector.unaryMinus(): Vector {
return this * -1
}

@ -7,16 +7,35 @@ import net.deliciousreya.minecraftportal.extensions.toProto
import net.deliciousreya.minecraftportal.proto.PortalSaveDataProtos import net.deliciousreya.minecraftportal.proto.PortalSaveDataProtos
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.Server import org.bukkit.Server
import org.bukkit.plugin.Plugin
import org.bukkit.scheduler.BukkitRunnable
import java.lang.IllegalArgumentException import java.lang.IllegalArgumentException
fun PortalSaveDataProtos.Portal.toPortal(server: Server, portalTypes: Map<Material, PortalType>): Portal { fun PortalSaveDataProtos.Portal.toPortal(server: Server, portalTypes: Map<Material, PortalType>): Portal {
val portalType = portalTypes[mineral.toMaterial()] ?: throw IllegalArgumentException("No portal type for $mineral") val portalType = portalTypes[mineral.toMaterial()] ?: throw IllegalArgumentException("No portal type for $mineral")
val result = Portal(PortalFrame(lowerLeftFrontCorner.toLocation(server), exitDirection.toDirection()), portalType) val result = Portal(PortalFrame(lowerLeftFrontCorner.toLocation(server), exitDirection.toDirection()), portalType)
// TODO: assert that the saved portal and mineral match the actual state of the world
return result return result
} }
class Portal(val frame: PortalFrame, val type: PortalType) { class Portal(val frame: PortalFrame, val type: PortalType) {
var portalSounds: PortalFrame.MakePortalSound? = null
var portalSparkles: PortalFrame.MakePortalSparkles? = null
fun startEffects(plugin: Plugin) {
stopEffects()
portalSounds = frame.MakePortalSound()
portalSparkles = frame.MakePortalSparkles()
portalSounds?.runTaskTimer(plugin, 0, 120)
portalSparkles?.runTaskTimer(plugin, 0, 20)
}
fun stopEffects() {
portalSounds?.cancel()
portalSparkles?.cancel()
portalSounds = null
portalSparkles = null
}
fun toProto(): PortalSaveDataProtos.Portal { fun toProto(): PortalSaveDataProtos.Portal {
return PortalSaveDataProtos.Portal.newBuilder() return PortalSaveDataProtos.Portal.newBuilder()
.setLowerLeftFrontCorner(frame.lowerLeftFrontCorner.toProto()) .setLowerLeftFrontCorner(frame.lowerLeftFrontCorner.toProto())

@ -7,11 +7,13 @@ import net.deliciousreya.minecraftportal.proto.PortalSaveDataProtos
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.Server import org.bukkit.Server
import org.bukkit.plugin.Plugin
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.lang.IllegalArgumentException import java.lang.IllegalArgumentException
import java.util.logging.Logger
class PortalDataStore { class PortalDataStore (){
var saveDataTo: File? = null var saveDataTo: File? = null
var useBackup: File? = null var useBackup: File? = null
val portalTypes: ImmutableMap<Material, PortalType> val portalTypes: ImmutableMap<Material, PortalType>
@ -87,6 +89,52 @@ class PortalDataStore {
this.useBackup = backupFile this.useBackup = backupFile
} }
fun validateWorldOnStartup(logger: Logger) {
for (portalType in this.portalTypes.values) {
val oldPortal = portalType.oldestActivePortal
val newPortal = portalType.newestActivePortal
if (newPortal != null && !validatePortal(newPortal)) {
logger.warning("Portal $newPortal was no longer valid!")
portalType.newestActivePortal = null
}
if (oldPortal != null && !validatePortal(oldPortal)) {
logger.warning("Portal $oldPortal was no longer valid!")
portalType.oldestActivePortal = portalType.newestActivePortal
portalType.newestActivePortal = null
}
}
}
fun validatePortal(portal: Portal): Boolean {
return portal.frame.mineral == portal.type.mineral && portal.frame.isActivePortal()
}
fun launchEffectsOnStartup(plugin: Plugin) {
for (portalType in this.portalTypes.values) {
startEffectsFor(portalType, plugin)
}
}
fun startEffectsFor(portal: PortalFrame, plugin: Plugin) {
startEffectsFor(getPortalType(portal), plugin)
}
fun startEffectsFor(portalType: PortalType, plugin: Plugin) {
val oldPortal = portalType.oldestActivePortal
val newPortal = portalType.newestActivePortal
if (portalType.isPaired) {
oldPortal?.startEffects(plugin)
oldPortal?.frame?.open()
newPortal?.startEffects(plugin)
newPortal?.frame?.open()
} else if (!portalType.isEmpty) {
oldPortal?.stopEffects()
oldPortal?.frame?.close()
newPortal?.stopEffects()
newPortal?.frame?.close()
}
}
fun unload() { fun unload() {
this.clear() this.clear()
this.saveDataTo = null this.saveDataTo = null
@ -112,17 +160,17 @@ class PortalDataStore {
/** Marks the new portal as active, and removes the one that needs to be deactivated, if any. */ /** Marks the new portal as active, and removes the one that needs to be deactivated, if any. */
fun activateAndReplacePortal(frame: PortalFrame): PortalFrame? { fun activateAndReplacePortal(frame: PortalFrame): PortalFrame? {
val type = getPortalType(frame) val type = getPortalType(frame)
val result = type.addOrReplacePortal(Portal(frame, type))?.frame val result = type.addOrReplacePortal(Portal(frame, type))
onAfterChanged() onAfterChanged()
return result return result?.frame
} }
/** Deactivates the given portal. Returns the other portal if it needs to be closed. */ /** Deactivates the given portal. Returns the other portal if it needs to be closed. */
fun deactivatePortal(frame: PortalFrame): PortalFrame? { fun deactivatePortal(frame: PortalFrame): PortalFrame? {
val type = getPortalType(frame) val type = getPortalType(frame)
val result = type.removePortalWithFrame(frame)?.frame val result = type.removePortalWithFrame(frame)
onAfterChanged() onAfterChanged()
return result return result?.frame
} }
fun getPortalType(frame: PortalFrame): PortalType { fun getPortalType(frame: PortalFrame): PortalType {
@ -138,6 +186,8 @@ class PortalDataStore {
/** Forgets all the data without actually affecting the world. */ /** Forgets all the data without actually affecting the world. */
fun clear() { fun clear() {
for (portalType in this.portalTypes.values) { for (portalType in this.portalTypes.values) {
portalType.oldestActivePortal?.stopEffects()
portalType.newestActivePortal?.stopEffects()
portalType.clear() portalType.clear()
} }
} }
@ -175,4 +225,12 @@ class PortalDataStore {
} }
return builder.build() return builder.build()
} }
fun startTeleporting(portal: PortalFrame, plugin: Plugin) {
getPortalType(portal).beginTeleportation(plugin)
}
fun stopTeleporting(portal: PortalFrame) {
getPortalType(portal).cancelTeleportation()
}
} }

@ -15,6 +15,8 @@ import org.bukkit.Sound
import org.bukkit.block.BlockFace import org.bukkit.block.BlockFace
import org.bukkit.block.data.Directional import org.bukkit.block.data.Directional
import org.bukkit.block.data.Openable import org.bukkit.block.data.Openable
import org.bukkit.entity.Entity
import org.bukkit.scheduler.BukkitRunnable
import org.bukkit.util.BoundingBox import org.bukkit.util.BoundingBox
val MINERAL_TYPES: ImmutableMap<Material, Material> = ImmutableMap.Builder<Material, Material>() val MINERAL_TYPES: ImmutableMap<Material, Material> = ImmutableMap.Builder<Material, Material>()
@ -174,6 +176,7 @@ data class PortalFrame(val lowerLeftFrontCorner: Location, val direction: ExitDi
} }
val portalCenter = lowerLeftFrontCorner + direction.doorOffsets[0] val portalCenter = lowerLeftFrontCorner + direction.doorOffsets[0]
val midCenter = portalCenter + MID_BLOCK
val fullPortalBoundingBox = BoundingBox.of(lowerLeftFrontCorner.block, (lowerLeftFrontCorner + UP * 3 + direction.toRight * 2 + direction.toBack).block) val fullPortalBoundingBox = BoundingBox.of(lowerLeftFrontCorner.block, (lowerLeftFrontCorner + UP * 3 + direction.toRight * 2 + direction.toBack).block)
val portalInsideBoundingBox = BoundingBox.of(portalCenter.block, (portalCenter + UP).block) val portalInsideBoundingBox = BoundingBox.of(portalCenter.block, (portalCenter + UP).block)
@ -238,8 +241,7 @@ data class PortalFrame(val lowerLeftFrontCorner: Location, val direction: ExitDi
for (offset in direction.glassOffsets) { for (offset in direction.glassOffsets) {
(lowerLeftFrontCorner + offset).block.type = MINERAL_TYPES.getOrDefault(mineral.type, BROWN_STAINED_GLASS) (lowerLeftFrontCorner + offset).block.type = MINERAL_TYPES.getOrDefault(mineral.type, BROWN_STAINED_GLASS)
} }
val midCenter = portalCenter + MID_BLOCK portalCenter.world?.playSound(midCenter, Sound.ENTITY_EVOKER_CAST_SPELL, 20f, 1f)
portalCenter.world?.playSound(midCenter, Sound.BLOCK_BEACON_ACTIVATE, 20f, 1f)
portalCenter.world?.spawnParticle(Particle.SPELL, midCenter, 75) portalCenter.world?.spawnParticle(Particle.SPELL, midCenter, 75)
open() open()
} }
@ -248,8 +250,54 @@ data class PortalFrame(val lowerLeftFrontCorner: Location, val direction: ExitDi
for (offset in direction.glassOffsets) { for (offset in direction.glassOffsets) {
(lowerLeftFrontCorner + offset).block.type = GLASS (lowerLeftFrontCorner + offset).block.type = GLASS
} }
val midCenter = portalCenter + MID_BLOCK portalCenter.world?.playSound(midCenter, Sound.ENTITY_ILLUSIONER_CAST_SPELL, 20f, 1f)
portalCenter.world?.playSound(midCenter, Sound.BLOCK_BEACON_DEACTIVATE, 20f, 1f)
portalCenter.world?.spawnParticle(Particle.SMOKE_NORMAL, midCenter, 75) portalCenter.world?.spawnParticle(Particle.SMOKE_NORMAL, midCenter, 75)
} }
fun isActivePortal(): Boolean {
return checkPortalFrameAt(lowerLeftFrontCorner, direction, State.ACTIVE) == this
}
fun playPortalsLinkedEffect() {
portalCenter.world?.playSound(midCenter, Sound.BLOCK_BEACON_ACTIVATE, 20f, 1f)
portalCenter.world?.spawnParticle(Particle.SPELL, midCenter, 30)
}
fun playPortalsUnlinkedEffect() {
portalCenter.world?.playSound(midCenter, Sound.BLOCK_BEACON_DEACTIVATE, 20f, 1f)
portalCenter.world?.spawnParticle(Particle.SMOKE_NORMAL, midCenter, 30)
}
fun playTeleporterActivatedEffect() {
portalCenter.world?.playSound(midCenter, Sound.BLOCK_PORTAL_TRIGGER, 20f, 1f)
}
fun playTeleportTravelEffect() {
portalCenter.world?.playSound(midCenter, Sound.BLOCK_PORTAL_TRAVEL, 20f, 1f)
portalCenter.world?.spawnParticle(Particle.FIREWORKS_SPARK, midCenter, 75)
}
fun getEntities(): Collection<Entity> {
return portalCenter.world?.getNearbyEntities(portalInsideBoundingBox) ?: ImmutableList.of()
}
fun playTeleporterActiveEffect() {
portalCenter.world?.spawnParticle(Particle.SPELL_MOB_AMBIENT, midCenter, 10)
}
fun playTeleportCanceledEffect() {
portalCenter.world?.playSound(midCenter, Sound.BLOCK_LAVA_EXTINGUISH, 20f, 1f)
portalCenter.world?.spawnParticle(Particle.SMOKE_NORMAL, midCenter, 30)
}
inner class MakePortalSound : BukkitRunnable() {
override fun run() {
portalCenter.world?.playSound(portalCenter + MID_BLOCK, Sound.BLOCK_PORTAL_AMBIENT, 3f, 0f)
}
}
inner class MakePortalSparkles : BukkitRunnable() {
override fun run() {
portalCenter.world?.spawnParticle(Particle.PORTAL, portalCenter + MID_BLOCK, 20)
}
}
} }

@ -3,6 +3,7 @@ package net.deliciousreya.minecraftportal.model
import net.deliciousreya.minecraftportal.proto.PortalSaveDataProtos import net.deliciousreya.minecraftportal.proto.PortalSaveDataProtos
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.plugin.Plugin
import java.lang.IllegalArgumentException import java.lang.IllegalArgumentException
import java.lang.IllegalStateException import java.lang.IllegalStateException
@ -39,6 +40,12 @@ class PortalType(val mineral: Material) {
} }
else -> { else -> {
val replacedPortal = newestActivePortal val replacedPortal = newestActivePortal
replacedPortal?.stopEffects()
replacedPortal?.frame?.playPortalsUnlinkedEffect()
if (replacedPortal != null) {
cancelTeleportation()
}
portal.frame.playPortalsLinkedEffect()
this.newestActivePortal = portal this.newestActivePortal = portal
replacedPortal replacedPortal
} }
@ -69,6 +76,19 @@ class PortalType(val mineral: Material) {
this.newestActivePortal = newerPortal this.newestActivePortal = newerPortal
} }
private var teleportation: Teleportation? = null
fun beginTeleportation(plugin: Plugin) {
val teleportation = Teleportation(this, plugin)
teleportation.start()
this.teleportation = teleportation
}
fun cancelTeleportation() {
teleportation?.cancelTeleportation()
teleportation = null
}
fun clear() { fun clear() {
oldestActivePortal = null oldestActivePortal = null
newestActivePortal = null newestActivePortal = null
@ -83,11 +103,26 @@ class PortalType(val mineral: Material) {
* returns it. Otherwise (if that frame wasn't for either portal or if there are none left) returns null. * returns it. Otherwise (if that frame wasn't for either portal or if there are none left) returns null.
*/ */
fun removePortalWithFrame(frame: PortalFrame): Portal? { fun removePortalWithFrame(frame: PortalFrame): Portal? {
val isPaired = this.isPaired
if (oldestActivePortal?.frame == frame) { if (oldestActivePortal?.frame == frame) {
oldestActivePortal?.stopEffects()
newestActivePortal?.stopEffects()
if (isPaired) {
oldestActivePortal?.frame?.playPortalsUnlinkedEffect()
newestActivePortal?.frame?.playPortalsUnlinkedEffect()
}
cancelTeleportation()
oldestActivePortal = newestActivePortal oldestActivePortal = newestActivePortal
} else if (newestActivePortal?.frame != frame) { } else if (newestActivePortal?.frame != frame) {
return null return null
} }
oldestActivePortal?.stopEffects()
newestActivePortal?.stopEffects()
cancelTeleportation()
if (isPaired) {
oldestActivePortal?.frame?.playPortalsUnlinkedEffect()
newestActivePortal?.frame?.playPortalsUnlinkedEffect()
}
newestActivePortal = null newestActivePortal = null
return oldestActivePortal return oldestActivePortal
} }

@ -0,0 +1,118 @@
package net.deliciousreya.minecraftportal.model
import net.deliciousreya.minecraftportal.proto.PortalSaveDataProtos
import org.bukkit.Location
import org.bukkit.entity.Entity
import org.bukkit.entity.LivingEntity
import org.bukkit.plugin.Plugin
import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType
import org.bukkit.scheduler.BukkitRunnable
class Teleportation(val portalType: PortalType, val plugin: Plugin) {
val leftEntities = LinkedHashSet<Entity>()
val rightEntities = LinkedHashSet<Entity>()
val allEntities = LinkedHashSet<Entity>()
var currentTask: BukkitRunnable? = null
var sparklesTask: DoSparkles? = null
val leftPortal = portalType.newestActivePortal!!
val rightPortal = portalType.oldestActivePortal!!
fun cancelFor(entity: Entity) {
leftEntities.remove(entity)
rightEntities.remove(entity)
allEntities.remove(entity)
if (allEntities.isEmpty()) {
cancelTeleportation()
}
}
fun cancelEffectsOn(entity: Entity) {
if (entity is LivingEntity) {
entity.removePotionEffect(PotionEffectType.BLINDNESS)
entity.removePotionEffect(PotionEffectType.CONFUSION)
}
}
fun cancelTeleportation() {
leftPortal.frame.open()
rightPortal.frame.open()
leftPortal.frame.playTeleportCanceledEffect()
rightPortal.frame.playTeleportCanceledEffect()
cancelEffectsOnAll()
leftEntities.clear()
rightEntities.clear()
allEntities.clear()
currentTask?.cancel()
currentTask = null
sparklesTask?.cancel()
sparklesTask = null
}
fun cancelEffectsOnAll() {
for (entity in allEntities) {
cancelEffectsOn(entity)
}
}
fun start() {
leftEntities.addAll(leftPortal.frame.getEntities())
rightEntities.addAll(rightPortal.frame.getEntities())
allEntities.addAll(leftEntities)
allEntities.addAll(rightEntities)
for (entity in allEntities) {
if (entity is LivingEntity) {
entity.addPotionEffect(PotionEffect(PotionEffectType.CONFUSION, 200, 1, false, false, false))
entity.addPotionEffect(PotionEffect(PotionEffectType.BLINDNESS, 200, 1, false, false, false))
}
}
leftPortal.frame.close()
rightPortal.frame.close()
leftPortal.frame.playTeleporterActivatedEffect()
rightPortal.frame.playTeleporterActivatedEffect()
val task = DoTeleport()
task.runTaskLater(plugin, 100)
currentTask = task
val sparkles = DoSparkles()
sparkles.runTaskTimer(plugin, 10, 15)
sparklesTask = sparkles
}
inner class DoSparkles : BukkitRunnable() {
override fun run() {
leftPortal.frame.playTeleporterActiveEffect()
rightPortal.frame.playTeleporterActiveEffect()
}
}
inner class DoTeleport : BukkitRunnable() {
override fun run() {
for(leftEntity in leftEntities) {
val relativeLocation = leftPortal.frame.getRelativeLocationFromAbsoluteLocation(leftEntity.location)
val destination = rightPortal.frame.getAbsoluteLocationFromRelativeLocation(relativeLocation)
leftEntity.teleport(destination)
}
for(rightEntity in rightEntities) {
val relativeLocation = rightPortal.frame.getRelativeLocationFromAbsoluteLocation(rightEntity.location)
val destination = leftPortal.frame.getAbsoluteLocationFromRelativeLocation(relativeLocation)
rightEntity.teleport(destination)
}
leftPortal.frame.playTeleportTravelEffect()
rightPortal.frame.playTeleportTravelEffect()
val task = OpenDoors()
task.runTaskLater(plugin, 100)
currentTask = task
}
}
inner class OpenDoors: BukkitRunnable() {
override fun run() {
leftPortal.frame.open()
rightPortal.frame.open()
cancelEffectsOnAll()
currentTask = null
sparklesTask?.cancel()
sparklesTask = null
}
}
}
Loading…
Cancel
Save