Skip to content

Commit 8affaa8

Browse files
authored
Feature/gh 22/persistent bars (#31)
* WIP: Add persistent healthbars for name/scoreboard * Cleanup * Update CHANGELOG
1 parent 0104d67 commit 8affaa8

File tree

7 files changed

+71
-31
lines changed

7 files changed

+71
-31
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## [Unreleased]
44

5+
### Added
6+
- GH-22: Always on healthbars for NAME or SCOREBOARD via special-case `always` duration (@tajobe)
7+
58
## [0.4.0] - 2025-02-17
69
### Changed
710
- MC 1.21, Kotlin 2.1, Gradle 8.12 (@tajobe)

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-all.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

src/main/kotlin/org/simplemc/simplehealthbars2/DamageListener.kt

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import org.bukkit.event.EventPriority
88
import org.bukkit.event.Listener
99
import org.bukkit.event.entity.EntityDamageByEntityEvent
1010
import org.bukkit.event.entity.EntityDeathEvent
11+
import org.bukkit.event.entity.EntitySpawnEvent
12+
import org.bukkit.event.player.PlayerJoinEvent
13+
import org.bukkit.event.player.PlayerQuitEvent
1114
import org.bukkit.plugin.Plugin
15+
import org.simplemc.simplehealthbars2.healthbar.Healthbar
1216
import org.simplemc.simplehealthbars2.healthbar.MobHealthbar
1317
import org.simplemc.simplehealthbars2.healthbar.PlayerHealthbar
1418
import java.util.UUID
@@ -18,13 +22,34 @@ class DamageListener(
1822
private val playerHealthbars: Map<String?, PlayerHealthbar?>,
1923
private val mobHealthbars: Map<String?, MobHealthbar?>,
2024
) : Listener, AutoCloseable {
21-
private data class RemoveHealthbarTask(val taskId: Int, val task: () -> Unit)
25+
private data class RemoveHealthbarTask(val taskId: Int?, val removeAction: () -> Unit)
2226

2327
private val scheduler = Bukkit.getScheduler()
2428
private val removeHealthbarTasks: MutableMap<UUID, RemoveHealthbarTask?> = mutableMapOf()
2529

30+
// <editor-fold desc="Set always on healthbars on spawn/join">
2631
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
27-
fun onEntityDamageEvent(event: EntityDamageByEntityEvent) {
32+
fun onEntitySpawn(event: EntitySpawnEvent) {
33+
val entity = event.entity as? LivingEntity
34+
entity?.healthbar?.let { healthbar ->
35+
if (healthbar.durationTicks == null) {
36+
healthbar(null, entity, 0.0)
37+
}
38+
}
39+
}
40+
41+
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
42+
fun onPlayerJoin(event: PlayerJoinEvent) {
43+
event.player.healthbar?.let { healthbar ->
44+
if (healthbar.durationTicks == null) {
45+
healthbar(null, event.player, 0.0)
46+
}
47+
}
48+
}
49+
// </editor-fold>
50+
51+
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
52+
fun onEntityDamageByEntityEvent(event: EntityDamageByEntityEvent) {
2853
val target = event.entity as? LivingEntity ?: return
2954
val source = event.damager as? LivingEntity
3055

@@ -33,42 +58,46 @@ class DamageListener(
3358
source?.let { healthbar(target, it, 0.0) }
3459
}
3560

61+
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
62+
fun onPlayerQuit(event: PlayerQuitEvent) = clearEntityHealthbar(event.player)
63+
3664
/**
3765
* Remove the healthbar from dying entities immediately
3866
*/
3967
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
40-
fun onEntityDeathEvent(event: EntityDeathEvent) {
41-
removeHealthbarTasks[event.entity.uniqueId]?.let {
42-
scheduler.cancelTask(it.taskId)
43-
it.task()
44-
}
45-
}
68+
fun onEntityDeathEvent(event: EntityDeathEvent) = clearEntityHealthbar(event.entity)
4669

4770
private fun healthbar(source: LivingEntity?, target: LivingEntity, damage: Double) {
4871
// cancel scheduled healthbar removal and run it now to prepare for new (updated) healthbar
49-
removeHealthbarTasks[target.uniqueId]?.let {
50-
scheduler.cancelTask(it.taskId)
51-
it.task()
72+
clearEntityHealthbar(target)
73+
74+
// update healthbar and schedule its removal task if necessary
75+
val healthbar = target.healthbar
76+
healthbar?.updateHealth(source, target, damage)?.let { removeAction ->
77+
val taskId = healthbar.durationTicks?.let { ticks ->
78+
scheduler.scheduleSyncDelayedTask(plugin, removeAction, ticks)
79+
}
80+
removeHealthbarTasks[target.uniqueId] = RemoveHealthbarTask(taskId, removeAction)
5281
}
82+
}
5383

54-
val healthbar = when (target) {
55-
is Player -> playerHealthbars[target.world.name] ?: playerHealthbars[null]
56-
else -> mobHealthbars[target.world.name] ?: mobHealthbars[null]
84+
private val LivingEntity.healthbar: Healthbar?
85+
get(): Healthbar? = when (this) {
86+
is Player -> playerHealthbars[world.name] ?: playerHealthbars[null]
87+
else -> mobHealthbars[world.name] ?: mobHealthbars[null]
5788
}
5889

59-
// update healthbar and schedule its removal task if available
60-
healthbar?.updateHealth(source, target, damage)?.let {
61-
val taskId = scheduler.scheduleSyncDelayedTask(plugin, it, healthbar.durationTicks)
62-
removeHealthbarTasks[target.uniqueId] = RemoveHealthbarTask(taskId, it)
90+
private fun clearEntityHealthbar(entity: LivingEntity) {
91+
removeHealthbarTasks.remove(entity.uniqueId)?.let {
92+
it.taskId?.let { taskId -> scheduler.cancelTask(taskId) }
93+
it.removeAction()
6394
}
6495
}
6596

6697
/**
6798
* Remember to remove all healthbars on close
6899
*/
69100
override fun close() {
70-
removeHealthbarTasks
71-
.mapNotNull { (_, removeTask) -> removeTask?.task }
72-
.forEach { it() }
101+
removeHealthbarTasks.forEach { (_, removeTask) -> removeTask?.removeAction() }
73102
}
74103
}

src/main/kotlin/org/simplemc/simplehealthbars2/SimpleHealthbars2.kt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ class SimpleHealthbars2 : JavaPlugin() {
6262
Loaded Healthbar configs:
6363
Player bars:
6464
${barsConfigToString(playerHealthbars)}
65-
65+
6666
Mob bars:
6767
${barsConfigToString(mobHealthbars)}
68-
68+
6969
""".trimIndent()
7070
}
7171

@@ -87,7 +87,7 @@ class SimpleHealthbars2 : JavaPlugin() {
8787
ScoreboardHealthbar.Config(
8888
useMainScoreboard = config.getBoolean("useMainScoreboard", false),
8989
style = Healthbar.Style.valueOf(checkNotNull(config.getString("style", "ABSOLUTE"))),
90-
duration = Duration.ofSeconds(config.getLong("duration", 5)),
90+
duration = loadBarDuration(config),
9191
),
9292
)
9393
Healthbar.Type.NONE -> null
@@ -96,12 +96,20 @@ class SimpleHealthbars2 : JavaPlugin() {
9696

9797
private fun loadStringBar(config: ConfigurationSection) = StringHealthbar.Config(
9898
style = Healthbar.Style.valueOf(checkNotNull(config.getString("style", "BAR"))),
99-
duration = Duration.ofSeconds(config.getLong("duration", 5)),
99+
duration = loadBarDuration(config),
100100
length = config.getInt("length", 20),
101101
char = config.getInt("char", 0x25ae).toChar(),
102102
showMobNames = config.getBoolean("showMobNames", true),
103103
)
104104

105+
private fun loadBarDuration(config: ConfigurationSection): Duration? = config.getString("duration").let {
106+
if (it == "always") {
107+
null
108+
} else {
109+
Duration.ofSeconds(it?.toLongOrNull() ?: 5L)
110+
}
111+
}
112+
105113
override fun onDisable() {
106114
listener.close()
107115
Bukkit.getScoreboardManager()?.mainScoreboard?.getObjective(OBJECTIVE_NAME)?.unregister()

src/main/kotlin/org/simplemc/simplehealthbars2/healthbar/Healthbar.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ interface Healthbar {
99

1010
interface Config {
1111
val style: Style
12-
val duration: Duration
12+
val duration: Duration?
1313
}
1414

1515
val config: Config
16-
val durationTicks: Long
17-
get() = config.duration.seconds * 20
16+
val durationTicks: Long?
17+
get() = config.duration?.seconds?.times(20)
1818

1919
/**
2020
* Update target's healthbar with latest health

src/main/kotlin/org/simplemc/simplehealthbars2/healthbar/ScoreboardHealthbar.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class ScoreboardHealthbar(override val config: Config) : PlayerHealthbar {
2121
data class Config(
2222
val useMainScoreboard: Boolean = false,
2323
override val style: Healthbar.Style = Healthbar.Style.ABSOLUTE,
24-
override val duration: Duration = Duration.ofSeconds(5),
24+
override val duration: Duration? = Duration.ofSeconds(5),
2525
) : Healthbar.Config
2626

2727
private val objective: Objective

src/main/kotlin/org/simplemc/simplehealthbars2/healthbar/StringHealthbar.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import kotlin.math.ceil
1010
abstract class StringHealthbar(final override val config: Config) : Healthbar {
1111
data class Config(
1212
override val style: Healthbar.Style = Healthbar.Style.BAR,
13-
override val duration: Duration = Duration.ofSeconds(5),
13+
override val duration: Duration? = Duration.ofSeconds(5),
1414
val length: Int = 20,
1515
val char: Char = 0x25ae.toChar(),
1616
val showMobNames: Boolean = true,

0 commit comments

Comments
 (0)