Oath System for Minecraft Plugin
Oath System for Minecraft Plugin
oathsmpv2
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link].*
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link].*
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
companion object {
private const val PDC_OATH_KEY = "oath_id"
private const val PDC_CRYSTAL = "is_oath_crystal"
// temp blocks placed for abilities -> revert later: playerUUID -> list of
(Location, originalMaterial)
private val tempPlacedBlocks: MutableMap<UUID, MutableList<Pair<Location,
Material>>> = HashMap()
[Link](this, this)
[Link]("giveoath")?.setExecutor(GiveOathCommand(this))
createRecipes()
[Link]("Oathsmpv2 enabled.")
}
// -------------------------
// Recipes / Items
// -------------------------
private fun createRecipes() {
val crystal = makeOathCrystal()
val key = NamespacedKey(this, "oath_crystal_recipe")
val recipe = ShapedRecipe(key, crystal)
[Link](" R ", "RNR", " R ")
[Link]('R', Material.NETHER_STAR)
[Link]('N', Material.NETHERITE_INGOT)
[Link](recipe)
}
// -------------------------
// Inventory guards
// -------------------------
@EventHandler
fun onInventoryClick(ev: InventoryClickEvent) {
val cursor = [Link]
val current = [Link]
if (isOath(cursor) || isOath(current)) {
val invType = [Link]
if (invType != [Link]) {
[Link] = true
([Link] as? Player)?.sendMessage("${[Link]}Oaths
cannot be placed in containers.")
}
}
}
// -------------------------
// Interaction: crystal & oath use
// -------------------------
@EventHandler
fun onPlayerInteract(ev: PlayerInteractEvent) {
val player = [Link]
if ([Link] != [Link]) return
val item = [Link]
if (isCrystal(item) && ([Link] == Action.RIGHT_CLICK_AIR || [Link] ==
Action.RIGHT_CLICK_BLOCK)) {
[Link] = true
if ([Link] > 1) [Link] = [Link] - 1 else
[Link](null)
startCrystalRoll(player)
return
}
if (isOath(item) && ([Link] == Action.RIGHT_CLICK_AIR || [Link] ==
Action.RIGHT_CLICK_BLOCK)) {
[Link] = true
val id = [Link]?.persistentDataContainer?.get(keyOath,
[Link]) ?: return
val oath = [Link](id) ?: return
val isSecondary = [Link]
handleOathUse(player, oath, isSecondary)
}
}
// -------------------------
// Death / Respawn persistence
// -------------------------
@EventHandler
fun onPlayerDeath(ev: PlayerDeathEvent) {
val player = [Link]
val items = [Link]()
val oathItems = [Link] { isOath(it) }
if ([Link]()) {
[Link](oathItems)
toRestoreOnRespawn[[Link]] = [Link]()
}
}
@EventHandler
fun onPlayerRespawn(ev: PlayerRespawnEvent) {
val pid = [Link]
val item = [Link](pid) ?: return
object : BukkitRunnable() {
override fun run() {
[Link](item)
[Link]("${[Link]}Your Oath has been
returned.")
}
}.runTaskLater(this, 2L)
}
// first join
@EventHandler
fun onPlayerJoin(ev: PlayerJoinEvent) {
val player = [Link]
[Link]().runTaskLater(this, Runnable {
if (!playerHasOath(player)) {
giveRandomOath(player)
[Link]("${[Link]}You were given a random Oath
on first join.")
}
}, 20L)
}
// -------------------------
// Crystal roll (explosive cinematic)
// -------------------------
fun startCrystalRoll(player: Player) {
val durationTicks = 80 // 4s
val names = [Link]().map { [Link] }
[Link](PotionEffect([Link],
durationTicks / 2, 1))
object : BukkitRunnable() {
var t = 0
override fun run() {
if (![Link] || [Link]) { cancel(); return }
val loc = [Link]().add(0.0, 1.2, 0.0)
t++
}
}.runTaskTimer(this, 0L, 2L)
}
// -------------------------
// Per-power cooldown gating
// -------------------------
fun handleOathUse(player: Player, oath: Oath, isSecondary: Boolean) {
val now = [Link]()
val key = "${[Link]}:${if (isSecondary) "sec" else "prim"}"
val playerMap = [Link]([Link])
{ mutableMapOf() }
val cooldownEnd = playerMap[key] ?: 0L
val remaining = cooldownEnd - now
if (remaining > 0) {
val seconds = ((remaining + 999) / 1000)
[Link]("${[Link]}$key Cooldown: ${seconds}s")
return
}
if (isSecondary) performSecondary(player, oath) else performPrimary(player,
oath)
val cdMillis = (if (isSecondary) [Link] else
[Link]) * 1000L
playerMap[key] = now + cdMillis
val hudName = "${[Link]} ${if (isSecondary) "Secondary" else
"Primary"}"
startCooldownHUD(player, hudName, if (isSecondary) [Link]
else [Link])
}
// -------------------------
// True damage helper (applies true damage amounts)
// -------------------------
private fun applyTrueDamage(target: LivingEntity, amount: Double, attacker:
Player? = null) {
val newHealth = [Link] - amount
if (newHealth > 0.0) {
try { [Link] = newHealth } catch (_: Exception) {}
} else {
if (attacker != null) {
try { [Link](1000.0, attacker) } catch (_: Exception)
{ [Link]() }
} else {
[Link]()
}
}
}
// -------------------------
// Implementations for each Oath's abilities
// -------------------------
private fun performPrimary(player: Player, oath: Oath) {
when (oath) {
[Link] -> primaryEmbers(player)
[Link] -> primaryFrostCryoLance(player)
[Link] -> primaryStormsStatic(player)
[Link] -> primaryEchoSonicPulse(player)
[Link] -> primaryVerdantThornLash(player)
[Link] -> primaryIronBash(player)
[Link] -> primaryRadianceBeam(player)
[Link] -> primaryAstraStellarPull(player)
[Link] -> primaryBloodCrimsonJab(player)
[Link] -> primarySoulSpiritSwipe(player)
[Link] -> primaryVoidSingularityPulse(player)
[Link] -> primaryZephyrWindSlash(player)
[Link] -> primaryTimeTemporalBlink(player)
}
}
// -------------------------
// 1) EMBERS (kept similar)
// -------------------------
private fun primaryEmbers(player: Player) {
val dir = [Link]()
val maxDist = 6
for (i in 1..maxDist) {
val pos =
[Link]().add([Link]().multiply([Link]()))
[Link]([Link], [Link](0.0, 1.0, 0.0), 4, 0.2,
0.2, 0.2, 0.01)
val ents = [Link](1.2, 1.2, 1.2)
for (e in ents) {
if (e is LivingEntity && e !== player) {
applyTrueDamage(e, PRIMARY_TRUE_DAMAGE, player)
[Link] = 2 * 20
spawnSpin(center = [Link](0.0, 0.6, 0.0), points = 10,
radius = 0.6)
}
}
}
spawnSpin(center = [Link](0.0, 1.0, 0.0), points = 18, radius
= 1.2)
}
// -------------------------
// 2) FROST
// -------------------------
private fun primaryFrostCryoLance(player: Player) {
val eye = [Link]
val dir = [Link]()
val length = 18.0
val points = 28
val start = [Link]().add([Link]().multiply(1.0))
for (i in 0..points) {
val step = [Link]().add([Link]().multiply([Link]() *
(length / points)))
[Link]([Link], step, 1, 0.05, 0.05, 0.05,
0.01)
if (i % 5 == 0) [Link](Particle.BLOCK_CRUMBLE, step,
2, [Link]())
val nearby = [Link](1.2, 1.2, 1.2)
for (e in nearby) {
if (e is LivingEntity && e !== player) {
applyTrueDamage(e, PRIMARY_TRUE_DAMAGE, player)
[Link](PotionEffect([Link], 20 *
2, 1))
placeTemporaryBlock(player, [Link]().add(0.0, -1.0,
0.0), Material.POWDER_SNOW, durationTicks = 60)
spawnSpin(center = [Link](0.0, 0.6, 0.0), points = 10,
radius = 0.5)
}
}
}
}
// -------------------------
// 3) STORMS
// -------------------------
private fun primaryStormsStatic(player: Player) {
val eye = [Link]
val dir = [Link]()
val length = 12.0
val points = 18
for (i in 0..points) {
val step = [Link]().add([Link]().multiply([Link]() *
(length / points)))
[Link](Particle.SONIC_BOOM, step, 0)
if (i % 4 == 0) [Link]([Link], step, 2)
val near = [Link](1.0, 1.0, 1.0)
for (e in near) if (e is LivingEntity && e !== player) {
applyTrueDamage(e, PRIMARY_TRUE_DAMAGE, player)
spawnSpin(center = [Link](0.0, 0.6, 0.0), points = 10,
radius = 0.6)
}
}
}
// -------------------------
// 4) ECHO (Warden-themed) - updated secondary to 4 shockwaves + glow stacking
// -------------------------
private fun primaryEchoSonicPulse(player: Player) {
val eye = [Link]
val dir = [Link]()
val length = 14.0
val steps = 20
val start = [Link]().add([Link]().multiply(1.0))
for (i in 0..steps) {
val step = [Link]().add([Link]().multiply([Link]() *
(length / steps)))
[Link](Particle.SONIC_BOOM, step, 0)
[Link](Particle.SCULK_SOUL, step, 0)
val near = [Link](1.2, 1.2, 1.2)
for (e in near) if (e is LivingEntity && e !== player) {
applyTrueDamage(e, PRIMARY_TRUE_DAMAGE, player)
[Link](PotionEffect([Link], 20 * 1,
0))
spawnSpin(center = [Link](0.0, 0.6, 0.0), points = 10,
radius = 0.6)
}
}
}
// -------------------------
// 5) VERDANT
// -------------------------
private fun primaryVerdantThornLash(player: Player) {
val eye = [Link]
val dir = [Link]()
val length = 10.0
val points = 16
for (i in 0..points) {
val step = [Link]().add([Link]().multiply([Link]() *
(length / points)))
[Link](Particle.HAPPY_VILLAGER, step, 1)
if (i % 4 == 0) [Link](Particle.BLOCK_MARKER, step,
1, Material.OAK_LEAVES.createBlockData())
val near = [Link](1.2, 1.2, 1.2)
for (e in near) if (e is LivingEntity && e !== player) {
[Link] =
[Link]().subtract([Link]()).normalize().multiply(0.6
)
applyTrueDamage(e, PRIMARY_TRUE_DAMAGE, player)
[Link](PotionEffect([Link], 20 * 2, 0))
spawnSpin(center = [Link](0.0, 0.6, 0.0), points = 10,
radius = 0.5)
}
}
}
// -------------------------
// 6) IRON
// -------------------------
private fun primaryIronBash(player: Player) {
val center = [Link]
val list = [Link](6.0, 4.5, 6.0)
for (e in list) if (e is LivingEntity && e !== player) {
[Link](player, 1.6f, ([Link].x - [Link].x).toFloat(),
([Link].z - [Link].z).toFloat())
applyTrueDamage(e, PRIMARY_TRUE_DAMAGE, player)
spawnPathParticles(center, [Link], 8)
}
val dust = DustOptions([Link](160,160,160),
[Link](160,160,160), 1f)
[Link](Particle.DUST_COLOR_TRANSITION, [Link](0.0,
0.5, 0.0), 0, 0.0, 0.0, 0.0, 0.0, dust)
}
// -------------------------
// 7) RADIANCE
// -------------------------
private fun primaryRadianceBeam(player: Player) {
val eye = [Link]
val dir = [Link]()
val length = 12.0
for (i in 0..24) {
val step = [Link]().add([Link]().multiply([Link]() *
(length / 24)))
[Link](Particle.DUST_COLOR_TRANSITION, step, 0,
0.0,0.0,0.0,0.0, DustOptions([Link], [Link], 1f))
if (i % 5 == 0) [Link](Particle.END_ROD, step, 0)
val near = [Link](0.8, 0.8, 0.8)
for (e in near) if (e is LivingEntity && e !== player) {
applyTrueDamage(e, PRIMARY_TRUE_DAMAGE, player)
[Link](PotionEffect([Link], 20*1,
0))
}
}
}
// -------------------------
// 8) ASTRA
// -------------------------
private fun primaryAstraStellarPull(player: Player) {
val list = [Link](12.0, 6.0, 12.0)
for (e in list) if (e is LivingEntity && e !== player) {
val dirToPlayer =
[Link]().subtract([Link]()).normalize()
val pullStrength = 2.4
[Link] = [Link](pullStrength).setY(0.9)
spawnChain(a = [Link](0.0, 0.6, 0.0), b =
[Link](0.0, 1.2, 0.0), points = 18, center = [Link])
applyTrueDamage(e, PRIMARY_TRUE_DAMAGE, player)
}
spawnRing(center = [Link](0.0, 1.0, 0.0), points = 22, radius
= 2.2)
}
// -------------------------
// 9) BLOOD
// -------------------------
private fun primaryBloodCrimsonJab(player: Player) {
val eye = [Link]
val dir = [Link]()
val points = 14
for (i in 0..points) {
val step = [Link]().add([Link]().multiply([Link]() *
(10.0/points)))
[Link](Particle.DUST_COLOR_TRANSITION, step, 0, 0.0,
0.0, 0.0, 0.0, DustOptions([Link](140,10,10), [Link](180,40,40), 1f))
val near = [Link](1.0,1.0,1.0)
for (e in near) if (e is LivingEntity && e !== player) {
applyTrueDamage(e, PRIMARY_TRUE_DAMAGE, player)
try { [Link] =
min([Link](Attribute.MAX_HEALTH)!!.value, [Link] + 0.5) } catch
(_: Exception) {}
spawnSpin(center = [Link](0.0,0.6,0.0), points = 10, radius
= 0.6)
}
}
}
// -------------------------
// 10) SOUL
// -------------------------
private fun primarySoulSpiritSwipe(player: Player) {
val eye = [Link]
val dir = [Link]()
val len = 8.0
for (i in 0..16) {
val step = [Link]().add([Link]().multiply([Link]() *
(len/16)))
[Link](Particle.SOUL_FIRE_FLAME, step, 1)
val near = [Link](1.0,1.0,1.0)
for (e in near) if (e is LivingEntity && e !== player) {
applyTrueDamage(e, PRIMARY_TRUE_DAMAGE, player)
spawnSpin(center = [Link](0.0,0.6,0.0), points = 10, radius
= 0.6)
}
}
}
// -------------------------
// 11) VOID
// -------------------------
private fun primaryVoidSingularityPulse(player: Player) {
val center = [Link]
val orb = [Link]().add([Link]().multiply(3.0))
val points = 14
for (i in 0..points) {
val angle = i * (2*[Link] / points)
val x = [Link](angle) * 0.6
val z = [Link](angle) * 0.6
val loc = [Link]().add(x, 0.2, z)
[Link]([Link], loc, 1)
val nearby = [Link](1.2,1.2,1.2)
for (e in nearby) if (e is LivingEntity && e !== player) {
[Link] =
[Link]([Link]().subtract([Link]()).normalize
().multiply(0.4)).setY(0.3)
applyTrueDamage(e, PRIMARY_TRUE_DAMAGE, player)
}
}
spawnRing(center = orb, points = 16, radius = 1.0)
}
// -------------------------
// 12) ZEPHYR
// -------------------------
private fun primaryZephyrWindSlash(player: Player) {
val eye = [Link]
val dir = [Link]()
val len = 14.0
val points = 18
for (i in 0..points) {
val step =
[Link]().add([Link]().multiply([Link]()*(len/points)))
[Link]([Link], step, 1)
val near = [Link](0.9,0.9,0.9)
for (e in near) if (e is LivingEntity && e !== player) {
applyTrueDamage(e, PRIMARY_TRUE_DAMAGE, player)
[Link] = [Link]([Link]().multiply(0.6))
}
}
}
// -------------------------
// 13) TIME
// -------------------------
private fun primaryTimeTemporalBlink(player: Player) {
val back =
[Link]().add([Link]().multiply(-5.0))
spawnRing(center = [Link], points = 12, radius = 1.2)
[Link](back)
spawnRing(center = [Link], points = 14, radius = 1.4)
}
// -------------------------
// Helpers: particles & temporary blocks
// -------------------------
private fun placeTemporaryBlock(player: Player, loc: Location, mat: Material,
durationTicks: Int = 40) {
try {
val world = [Link] ?: return
val b = [Link](loc)
val original = [Link]
if (original == mat) return
[Link] = mat
val list = [Link]([Link])
{ mutableListOf() }
[Link]([Link] to original)
object : BukkitRunnable() {
override fun run() {
try { if ([Link] == mat) [Link] = original } catch (_:
Exception) {}
}
}.runTaskLater(this, [Link]())
} catch (_: Exception) {}
}
// -------------------------
// HUD helper
// -------------------------
private fun startCooldownHUD(player: Player, name: String, seconds: Int) {
[Link]([Link]) { mutableMapOf() }[name] =
seconds
object : BukkitRunnable() {
override fun run() {
val map = hudCooldowns[[Link]] ?: run { cancel(); return }
val cd = map[name] ?: run { cancel(); return }
if (cd <= 0) {
[Link]("${[Link]}$name ${[Link]}
[READY]")
[Link](name)
cancel(); return
}
val totalBlocks = 10
val filled = (([Link]() / [Link](1)) *
totalBlocks).toInt().coerceIn(0, totalBlocks)
val filledStr = "█".repeat(filled)
val emptyStr = "█".repeat(totalBlocks - filled)
val bar = "${[Link]}$filledStr${ChatColor.DARK_GRAY}
$emptyStr${[Link]} ${cd}s"
[Link]("${[Link]}$name: $bar")
map[name] = cd - 1
}
}.runTaskTimer(this, 0L, 20L)
}
// -------------------------
// Oath enum (primary=14s, secondary=30s)
// -------------------------
enum class Oath(val id: String, val displayName: String, val description:
List<String>, val primaryCooldown: Int, val secondaryCooldown: Int) {
EMBERS("embers","Embers", listOf("Flame Dash (Primary)","Inferno Burst
(Secondary)"), 14, 30),
FROST("frost","Frost", listOf("Cryo Lance (Primary)","Glacial Prison
(Secondary)"), 14, 30),
STORMS("storms","Storms", listOf("Static Strike (Primary)","Tempest Field
(Secondary)"), 14, 30),
ECHO("echo","Echo", listOf("Sonic Pulse (Primary)","Resonance Field
(Secondary)"), 14, 30),
VERDANT("verdant","Verdant", listOf("Thorn Lash (Primary)","Bloom Surge
(Secondary)"), 14, 30),
IRON("iron","Iron", listOf("Iron Bash (Primary)","Fortress Mode (Secondary)"),
14, 30),
RADIANCE("radiance","Radiance", listOf("Light Beam (Primary)","Solar Pulse
(Secondary)"), 14, 30),
ASTRA("astra","Astra", listOf("Stellar Pull (Primary)","Nebula Slam
(Secondary)"), 14, 30),
BLOOD("blood","Blood", listOf("Crimson Jab (Primary)","Vital Drain
(Secondary)"), 14, 30),
SOUL("soul","Soul", listOf("Spirit Swipe (Primary)","Ethereal Howl
(Secondary)"), 14, 30),
VOID("void","Void", listOf("Singularity Pulse (Primary)","Black Hole
(Secondary)"), 14, 30),
ZEPHYR("zephyr","Zephyr", listOf("Wind Slash (Primary)","Gale Launch
(Secondary)"), 14, 30),
TIME("time","Time", listOf("Temporal Blink (Primary)","Chrono Rift
(Secondary)"), 14, 30);
companion object {
fun fromId(id: String): Oath? = values().find { [Link](id, ignoreCase
= true) || [Link](id, ignoreCase = true) }
}
}
// -------------------------
// /giveoath command
// -------------------------
class GiveOathCommand(private val plugin: Oathsmpv2) : CommandExecutor {
override fun onCommand(sender: CommandSender, command: Command, label: String,
args: Array<out String>): Boolean {
if (sender !is Player) {
[Link]("This command can only be used in-game.")
return true
}
val player = sender
if ([Link]()) {
[Link](player); return true
}
val name = [Link](" ")
val oath = [Link](name)
if (oath != null) [Link](player, oath, replace = true)
else [Link]("${[Link]}Unknown Oath '$name'. Available: $
{[Link]().joinToString(", ") { [Link] }}")
return true
}
}