Add support for boss health bars.
Three different types of health bars are implemented behind a basic Strategy Pattern. A new per-arena setting, boss-health-bar, can be used to configure which one of the three types (if any) of health bar should be used for bosses in the given arena: - `boss-bar` creates a boss bar at the top of the screen as if the players were fighting an ender dragon or a wither. - `title` uses the Chapters/Titles API in Bukkit to display the health of the boss as a "subtitle" whenever it takes damage. - `name` sets the entitiy's health along with an optional custom name above the entity's head.
This commit is contained in:
@@ -25,6 +25,7 @@ import com.garbagemule.MobArena.util.ClassChests;
|
|||||||
import com.garbagemule.MobArena.util.inventory.InventoryManager;
|
import com.garbagemule.MobArena.util.inventory.InventoryManager;
|
||||||
import com.garbagemule.MobArena.util.timer.AutoStartTimer;
|
import com.garbagemule.MobArena.util.timer.AutoStartTimer;
|
||||||
import com.garbagemule.MobArena.util.timer.StartDelayTimer;
|
import com.garbagemule.MobArena.util.timer.StartDelayTimer;
|
||||||
|
import com.garbagemule.MobArena.waves.MABoss;
|
||||||
import com.garbagemule.MobArena.waves.SheepBouncer;
|
import com.garbagemule.MobArena.waves.SheepBouncer;
|
||||||
import com.garbagemule.MobArena.waves.WaveManager;
|
import com.garbagemule.MobArena.waves.WaveManager;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@@ -1118,6 +1119,14 @@ public class ArenaImpl implements Arena
|
|||||||
|
|
||||||
private void clearPlayer(Player p)
|
private void clearPlayer(Player p)
|
||||||
{
|
{
|
||||||
|
// Remove from boss health bar
|
||||||
|
monsterManager.getBossMonsters().forEach(entity -> {
|
||||||
|
MABoss boss = monsterManager.getBoss(entity);
|
||||||
|
if (boss != null) {
|
||||||
|
boss.getHealthBar().removePlayer(p);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Remove pets.
|
// Remove pets.
|
||||||
monsterManager.removePets(p);
|
monsterManager.removePets(p);
|
||||||
|
|
||||||
|
|||||||
@@ -635,6 +635,7 @@ public class ArenaListener
|
|||||||
event.getDrops().addAll(drops);
|
event.getDrops().addAll(drops);
|
||||||
}
|
}
|
||||||
boss.setDead(true);
|
boss.setDead(true);
|
||||||
|
boss.getHealthBar().setProgress(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ItemStack> loot = monsters.getLoot(damagee);
|
List<ItemStack> loot = monsters.getLoot(damagee);
|
||||||
@@ -685,6 +686,10 @@ public class ArenaListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MABoss boss = (damagee instanceof LivingEntity)
|
||||||
|
? monsters.getBoss((LivingEntity) damagee)
|
||||||
|
: null;
|
||||||
|
|
||||||
// Pet wolf
|
// Pet wolf
|
||||||
if (damagee instanceof Wolf && arena.hasPet(damagee)) {
|
if (damagee instanceof Wolf && arena.hasPet(damagee)) {
|
||||||
onPetDamage(event, (Wolf) damagee, damager);
|
onPetDamage(event, (Wolf) damagee, damager);
|
||||||
@@ -701,6 +706,10 @@ public class ArenaListener
|
|||||||
else if (damagee instanceof Snowman && event.getCause() == DamageCause.MELTING) {
|
else if (damagee instanceof Snowman && event.getCause() == DamageCause.MELTING) {
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
}
|
}
|
||||||
|
// Boss monster
|
||||||
|
else if (boss != null) {
|
||||||
|
onBossDamage(event, boss, damager);
|
||||||
|
}
|
||||||
// Regular monster
|
// Regular monster
|
||||||
else if (monsters.getMonsters().contains(damagee)) {
|
else if (monsters.getMonsters().contains(damagee)) {
|
||||||
onMonsterDamage(event, damagee, damager);
|
onMonsterDamage(event, damagee, damager);
|
||||||
@@ -740,6 +749,16 @@ public class ArenaListener
|
|||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onBossDamage(EntityDamageEvent event, MABoss boss, Entity damager) {
|
||||||
|
onMonsterDamage(event, boss.getEntity(), damager);
|
||||||
|
if (event.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double progress = boss.getHealth() / boss.getMaxHealth();
|
||||||
|
boss.getHealthBar().setProgress(progress);
|
||||||
|
}
|
||||||
|
|
||||||
private void onMonsterDamage(EntityDamageEvent event, Entity monster, Entity damager) {
|
private void onMonsterDamage(EntityDamageEvent event, Entity monster, Entity damager) {
|
||||||
if (damager instanceof Player) {
|
if (damager instanceof Player) {
|
||||||
Player p = (Player) damager;
|
Player p = (Player) damager;
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package com.garbagemule.MobArena;
|
|||||||
import com.garbagemule.MobArena.events.ArenaCompleteEvent;
|
import com.garbagemule.MobArena.events.ArenaCompleteEvent;
|
||||||
import com.garbagemule.MobArena.events.NewWaveEvent;
|
import com.garbagemule.MobArena.events.NewWaveEvent;
|
||||||
import com.garbagemule.MobArena.framework.Arena;
|
import com.garbagemule.MobArena.framework.Arena;
|
||||||
|
import com.garbagemule.MobArena.healthbar.CreatesHealthBar;
|
||||||
|
import com.garbagemule.MobArena.healthbar.HealthBar;
|
||||||
import com.garbagemule.MobArena.region.ArenaRegion;
|
import com.garbagemule.MobArena.region.ArenaRegion;
|
||||||
import com.garbagemule.MobArena.things.Thing;
|
import com.garbagemule.MobArena.things.Thing;
|
||||||
import com.garbagemule.MobArena.waves.MABoss;
|
import com.garbagemule.MobArena.waves.MABoss;
|
||||||
@@ -31,6 +33,7 @@ public class MASpawnThread implements Runnable
|
|||||||
private RewardManager rewardManager;
|
private RewardManager rewardManager;
|
||||||
private WaveManager waveManager;
|
private WaveManager waveManager;
|
||||||
private MonsterManager monsterManager;
|
private MonsterManager monsterManager;
|
||||||
|
private CreatesHealthBar createsHealthBar;
|
||||||
|
|
||||||
private int playerCount, monsterLimit;
|
private int playerCount, monsterLimit;
|
||||||
private boolean waveClear, bossClear, preBossClear, wavesAsLevel;
|
private boolean waveClear, bossClear, preBossClear, wavesAsLevel;
|
||||||
@@ -48,6 +51,7 @@ public class MASpawnThread implements Runnable
|
|||||||
this.rewardManager = arena.getRewardManager();
|
this.rewardManager = arena.getRewardManager();
|
||||||
this.waveManager = arena.getWaveManager();
|
this.waveManager = arena.getWaveManager();
|
||||||
this.monsterManager = arena.getMonsterManager();
|
this.monsterManager = arena.getMonsterManager();
|
||||||
|
this.createsHealthBar = new CreatesHealthBar(arena.getSettings().getString("boss-health-bar", "none"));
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
@@ -195,6 +199,10 @@ public class MASpawnThread implements Runnable
|
|||||||
BossWave bw = (BossWave) w;
|
BossWave bw = (BossWave) w;
|
||||||
double maxHealth = bw.getMaxHealth(playerCount);
|
double maxHealth = bw.getMaxHealth(playerCount);
|
||||||
MABoss boss = monsterManager.addBoss(e, maxHealth);
|
MABoss boss = monsterManager.addBoss(e, maxHealth);
|
||||||
|
HealthBar healthbar = createsHealthBar.create(e, bw.getBossName());
|
||||||
|
arena.getPlayersInArena().forEach(healthbar::addPlayer);
|
||||||
|
healthbar.setProgress(1);
|
||||||
|
boss.setHealthBar(healthbar);
|
||||||
boss.setReward(bw.getReward());
|
boss.setReward(bw.getReward());
|
||||||
boss.setDrops(bw.getDrops());
|
boss.setDrops(bw.getDrops());
|
||||||
bw.addMABoss(boss);
|
bw.addMABoss(boss);
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.garbagemule.MobArena.healthbar;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.boss.BarColor;
|
||||||
|
import org.bukkit.boss.BarFlag;
|
||||||
|
import org.bukkit.boss.BarStyle;
|
||||||
|
import org.bukkit.boss.BossBar;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
class BossHealthBar implements HealthBar {
|
||||||
|
|
||||||
|
private static final double LOW_HEALTH = 0.25;
|
||||||
|
|
||||||
|
private final BossBar bar;
|
||||||
|
|
||||||
|
BossHealthBar(String title) {
|
||||||
|
bar = Bukkit.createBossBar(
|
||||||
|
title.isEmpty() ? "Boss" : title,
|
||||||
|
BarColor.GREEN,
|
||||||
|
BarStyle.SOLID,
|
||||||
|
BarFlag.PLAY_BOSS_MUSIC,
|
||||||
|
BarFlag.DARKEN_SKY
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setProgress(double progress) {
|
||||||
|
if (progress <= LOW_HEALTH && bar.getColor() != BarColor.RED) {
|
||||||
|
bar.setColor(BarColor.RED);
|
||||||
|
}
|
||||||
|
bar.setProgress(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addPlayer(Player player) {
|
||||||
|
bar.addPlayer(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removePlayer(Player player) {
|
||||||
|
bar.removePlayer(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAll() {
|
||||||
|
bar.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.garbagemule.MobArena.healthbar;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
|
||||||
|
public class CreatesHealthBar {
|
||||||
|
|
||||||
|
private final String type;
|
||||||
|
private final CreatesHealthString createsHealthString;
|
||||||
|
|
||||||
|
public CreatesHealthBar(String type) {
|
||||||
|
this.type = type;
|
||||||
|
this.createsHealthString = new CreatesHealthString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HealthBar create(Entity entity, String title) {
|
||||||
|
String name = (title != null) ? title : "";
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "boss-bar":
|
||||||
|
return new BossHealthBar(name);
|
||||||
|
case "title":
|
||||||
|
return new TitleHealthBar(name, createsHealthString);
|
||||||
|
case "name":
|
||||||
|
return new NameHealthBar(entity, name, createsHealthString);
|
||||||
|
default:
|
||||||
|
return new NullHealthBar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.garbagemule.MobArena.healthbar;
|
||||||
|
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
class CreatesHealthString {
|
||||||
|
|
||||||
|
private static final int TOTAL_BARS = 20;
|
||||||
|
private static final int LOW_BARS = TOTAL_BARS / 4;
|
||||||
|
|
||||||
|
String create(double progress) {
|
||||||
|
int bars = (int) (progress * TOTAL_BARS);
|
||||||
|
|
||||||
|
String current = IntStream.range(0, bars)
|
||||||
|
.mapToObj(i -> "|")
|
||||||
|
.collect(Collectors.joining());
|
||||||
|
|
||||||
|
String lost = IntStream.range(bars, TOTAL_BARS)
|
||||||
|
.mapToObj(i -> "|")
|
||||||
|
.collect(Collectors.joining());
|
||||||
|
|
||||||
|
ChatColor color = (bars <= LOW_BARS) ? ChatColor.RED : ChatColor.GREEN;
|
||||||
|
|
||||||
|
return color + current + ChatColor.GRAY + lost;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.garbagemule.MobArena.healthbar;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public interface HealthBar {
|
||||||
|
|
||||||
|
void setProgress(double progress);
|
||||||
|
|
||||||
|
void addPlayer(Player player);
|
||||||
|
|
||||||
|
void removePlayer(Player player);
|
||||||
|
|
||||||
|
void removeAll();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.garbagemule.MobArena.healthbar;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
class NameHealthBar implements HealthBar {
|
||||||
|
|
||||||
|
private final Entity entity;
|
||||||
|
private final String title;
|
||||||
|
private final CreatesHealthString createsHealthString;
|
||||||
|
|
||||||
|
NameHealthBar(Entity entity, String title, CreatesHealthString createsHealthString) {
|
||||||
|
this.entity = entity;
|
||||||
|
this.title = title;
|
||||||
|
this.createsHealthString = createsHealthString;
|
||||||
|
|
||||||
|
entity.setCustomNameVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setProgress(double progress) {
|
||||||
|
String health = createsHealthString.create(progress);
|
||||||
|
String name = title.isEmpty() ? health : title + " " + health;
|
||||||
|
|
||||||
|
entity.setCustomName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addPlayer(Player player) {
|
||||||
|
// OK BOSS
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removePlayer(Player player) {
|
||||||
|
// OK BOSS
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAll() {
|
||||||
|
// OK BOSS
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.garbagemule.MobArena.healthbar;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
class NullHealthBar implements HealthBar {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setProgress(double progress) {
|
||||||
|
// OK BOSS
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addPlayer(Player player) {
|
||||||
|
// OK BOSS
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removePlayer(Player player) {
|
||||||
|
// OK BOSS
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAll() {
|
||||||
|
// OK BOSS
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.garbagemule.MobArena.healthbar;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
class TitleHealthBar implements HealthBar {
|
||||||
|
|
||||||
|
private static final int FADE_IN_TICKS = 5;
|
||||||
|
private static final int STAY_TICKS = 40;
|
||||||
|
private static final int FADE_OUT_TICKS = 10;
|
||||||
|
|
||||||
|
private final String title;
|
||||||
|
private final CreatesHealthString createsHealthString;
|
||||||
|
private final Set<Player> players;
|
||||||
|
|
||||||
|
TitleHealthBar(String title, CreatesHealthString createsHealthString) {
|
||||||
|
this.title = title;
|
||||||
|
this.createsHealthString = createsHealthString;
|
||||||
|
this.players = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setProgress(double progress) {
|
||||||
|
String health = createsHealthString.create(progress);
|
||||||
|
String message = title.isEmpty() ? health : title + " " + health;
|
||||||
|
|
||||||
|
players.forEach(player -> player.sendTitle(
|
||||||
|
"",
|
||||||
|
message,
|
||||||
|
FADE_IN_TICKS,
|
||||||
|
STAY_TICKS,
|
||||||
|
FADE_OUT_TICKS
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addPlayer(Player player) {
|
||||||
|
players.add(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removePlayer(Player player) {
|
||||||
|
players.remove(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAll() {
|
||||||
|
players.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.garbagemule.MobArena.waves;
|
package com.garbagemule.MobArena.waves;
|
||||||
|
|
||||||
|
import com.garbagemule.MobArena.healthbar.HealthBar;
|
||||||
import com.garbagemule.MobArena.things.Thing;
|
import com.garbagemule.MobArena.things.Thing;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.LivingEntity;
|
import org.bukkit.entity.LivingEntity;
|
||||||
@@ -13,6 +14,7 @@ public class MABoss
|
|||||||
private boolean dead;
|
private boolean dead;
|
||||||
private Thing reward;
|
private Thing reward;
|
||||||
private List<ItemStack> drops;
|
private List<ItemStack> drops;
|
||||||
|
private HealthBar healthbar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an MABoss from the given entity with the given max health.
|
* Create an MABoss from the given entity with the given max health.
|
||||||
@@ -73,6 +75,7 @@ public class MABoss
|
|||||||
*/
|
*/
|
||||||
public void setDead(boolean dead) {
|
public void setDead(boolean dead) {
|
||||||
this.dead = dead;
|
this.dead = dead;
|
||||||
|
healthbar.removeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReward(Thing reward) {
|
public void setReward(Thing reward) {
|
||||||
@@ -90,4 +93,12 @@ public class MABoss
|
|||||||
public List<ItemStack> getDrops() {
|
public List<ItemStack> getDrops() {
|
||||||
return drops;
|
return drops;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HealthBar getHealthBar() {
|
||||||
|
return healthbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHealthBar(HealthBar healthbar) {
|
||||||
|
this.healthbar = healthbar;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ auto-start-timer: 0
|
|||||||
start-delay-timer: 0
|
start-delay-timer: 0
|
||||||
auto-ready: false
|
auto-ready: false
|
||||||
use-class-chests: false
|
use-class-chests: false
|
||||||
|
boss-health-bar: boss-bar
|
||||||
display-waves-as-level: false
|
display-waves-as-level: false
|
||||||
display-timer-as-level: false
|
display-timer-as-level: false
|
||||||
use-scoreboards: true
|
use-scoreboards: true
|
||||||
|
|||||||
Reference in New Issue
Block a user