Skip to content

Commit 0c48aa6

Browse files
authored
Merge pull request #423 from BentoBoxWorld/420_boss_bar
420 boss bar
2 parents 68107d6 + 5de015a commit 0c48aa6

File tree

26 files changed

+1682
-732
lines changed

26 files changed

+1682
-732
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
<!-- Do not change unless you want different name for local builds. -->
6464
<build.number>-LOCAL</build.number>
6565
<!-- This allows to change between versions. -->
66-
<build.version>1.18.3</build.version>
66+
<build.version>1.19.0</build.version>
6767
<!-- SonarCloud -->
6868
<sonar.projectKey>BentoBoxWorld_AOneBlock</sonar.projectKey>
6969
<sonar.organization>bentobox-world</sonar.organization>

src/main/java/world/bentobox/aoneblock/AOneBlock.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import org.bukkit.World;
99
import org.bukkit.World.Environment;
1010
import org.bukkit.WorldCreator;
11-
import org.bukkit.WorldType;
1211
import org.bukkit.entity.SpawnCategory;
1312
import org.bukkit.generator.ChunkGenerator;
1413
import org.eclipse.jdt.annotation.NonNull;
@@ -24,6 +23,7 @@
2423
import world.bentobox.aoneblock.listeners.InfoListener;
2524
import world.bentobox.aoneblock.listeners.ItemsAdderListener;
2625
import world.bentobox.aoneblock.listeners.JoinLeaveListener;
26+
import world.bentobox.aoneblock.listeners.BossBarListener;
2727
import world.bentobox.aoneblock.listeners.NoBlockHandler;
2828
import world.bentobox.aoneblock.listeners.StartSafetyListener;
2929
import world.bentobox.aoneblock.oneblocks.OneBlockCustomBlockCreator;
@@ -66,6 +66,9 @@ public class AOneBlock extends GameModeAddon {
6666
.listener(new StartSafetyListener(this))
6767
.defaultSetting(false)
6868
.build();
69+
private BossBarListener bossBar = new BossBarListener(this);
70+
public final Flag BOSSBAR = new Flag.Builder("BOSSBAR", Material.DRAGON_HEAD).mode(Mode.BASIC)
71+
.type(Type.SETTING).listener(bossBar).defaultSetting(true).build();
6972

7073
@Override
7174
public void onLoad() {
@@ -89,6 +92,8 @@ public void onLoad() {
8992
// Register flag with BentoBox
9093
// Register protection flag with BentoBox
9194
getPlugin().getFlagsManager().registerFlag(this, START_SAFETY);
95+
// Bossbar
96+
getPlugin().getFlagsManager().registerFlag(this, this.BOSSBAR);
9297
}
9398
}
9499

@@ -120,6 +125,7 @@ public void onEnable() {
120125
registerListener(new BlockProtect(this));
121126
registerListener(new JoinLeaveListener(this));
122127
registerListener(new InfoListener(this));
128+
registerListener(bossBar);
123129
// Register placeholders
124130
phManager = new AOneBlockPlaceholders(this, getPlugin().getPlaceholdersManager());
125131

@@ -335,4 +341,11 @@ public void setSettings(Settings settings) {
335341
this.settings = settings;
336342
}
337343

344+
/**
345+
* @return the bossBar
346+
*/
347+
public BossBarListener getBossBar() {
348+
return bossBar;
349+
}
350+
338351
}

src/main/java/world/bentobox/aoneblock/Settings.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ public class Settings implements WorldSettings {
7272
@ConfigEntry(path = "aoneblock.command.set-count-command", since = "1.10.0")
7373
private String setCountCommand = "setCount";
7474

75+
@ConfigComment("The command label that toggers the progress bar.")
76+
@ConfigComment("By default it is 'bossbar'.")
77+
@ConfigEntry(path = "aoneblock.command.bossbar-command", since = "1.19.0")
78+
private String bossBarCommand = "bossbar";
79+
7580
@ConfigComment("How long a player must wait until they can use the setCount command again. In minutes.")
7681
@ConfigComment("This is the command that is run from the phases panel.")
7782
@ConfigEntry(path = "aoneblock.command.set-count-cooldown", since = "1.13.0")
@@ -2236,4 +2241,15 @@ public void setConcurrentIslands(int concurrentIslands) {
22362241
this.concurrentIslands = concurrentIslands;
22372242
}
22382243

2244+
public String getBossBarCommand() {
2245+
return bossBarCommand;
2246+
}
2247+
2248+
/**
2249+
* @param bossBarCommand the bossBarCommand to set
2250+
*/
2251+
public void setBossBarCommand(String bossBarCommand) {
2252+
this.bossBarCommand = bossBarCommand;
2253+
}
2254+
22392255
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package world.bentobox.aoneblock.commands.island;
2+
3+
import java.util.List;
4+
5+
import world.bentobox.aoneblock.AOneBlock;
6+
import world.bentobox.bentobox.api.commands.CompositeCommand;
7+
import world.bentobox.bentobox.api.user.User;
8+
9+
public class IslandBossBarCommand extends CompositeCommand {
10+
11+
private AOneBlock addon;
12+
13+
public IslandBossBarCommand(CompositeCommand islandCommand, String label, String[] aliases)
14+
{
15+
super(islandCommand, label, aliases);
16+
}
17+
18+
@Override
19+
public void setup() {
20+
setDescription("aoneblock.commands.island.bossbar.description");
21+
setOnlyPlayer(true);
22+
// Permission
23+
setPermission("island.bossbar");
24+
addon = getAddon();
25+
}
26+
27+
@Override
28+
public boolean execute(User user, String label, List<String> args) {
29+
addon.getBossBar().toggleUser(user);
30+
getIslands().getIslandAt(user.getLocation()).ifPresent(i -> {
31+
if (!i.isAllowed(addon.BOSSBAR)) {
32+
user.sendMessage("aoneblock.bossbar.not-active");
33+
}
34+
});
35+
return true;
36+
}
37+
}

src/main/java/world/bentobox/aoneblock/commands/island/PlayerCommand.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,8 @@ public void setup() {
3535
new IslandRespawnBlockCommand(this,
3636
settings.getRespawnBlockCommand().split(" ")[0],
3737
settings.getRespawnBlockCommand().split(" "));
38+
// Boss bar
39+
new IslandBossBarCommand(this, settings.getBossBarCommand().split(" ")[0],
40+
settings.getBossBarCommand().split(" "));
3841
}
3942
}
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package world.bentobox.aoneblock.listeners;
2+
3+
import java.util.HashMap;
4+
import java.util.Locale;
5+
import java.util.Map;
6+
import java.util.UUID;
7+
8+
import org.bukkit.Bukkit;
9+
import org.bukkit.boss.BarColor;
10+
import org.bukkit.boss.BarStyle;
11+
import org.bukkit.boss.BossBar;
12+
import org.bukkit.entity.Player;
13+
import org.bukkit.event.EventHandler;
14+
import org.bukkit.event.EventPriority;
15+
import org.bukkit.event.Listener;
16+
import org.bukkit.event.player.PlayerJoinEvent;
17+
import org.bukkit.event.player.PlayerQuitEvent;
18+
import org.eclipse.jdt.annotation.NonNull;
19+
20+
import world.bentobox.aoneblock.AOneBlock;
21+
import world.bentobox.aoneblock.dataobjects.OneBlockIslands;
22+
import world.bentobox.aoneblock.events.MagicBlockEvent;
23+
import world.bentobox.bentobox.api.events.flags.FlagSettingChangeEvent;
24+
import world.bentobox.bentobox.api.events.island.IslandEnterEvent;
25+
import world.bentobox.bentobox.api.events.island.IslandExitEvent;
26+
import world.bentobox.bentobox.api.metadata.MetaDataValue;
27+
import world.bentobox.bentobox.api.user.User;
28+
import world.bentobox.bentobox.database.objects.Island;
29+
30+
public class BossBarListener implements Listener {
31+
32+
private static final String AONEBLOCK_BOSSBAR = "aoneblock.bossbar";
33+
34+
public BossBarListener(AOneBlock addon) {
35+
super();
36+
this.addon = addon;
37+
}
38+
39+
private AOneBlock addon;
40+
41+
// Store a boss bar for each player (using their UUID)
42+
private final Map<Island, BossBar> islandBossBars = new HashMap<>();
43+
44+
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
45+
public void onBreakBlockEvent(MagicBlockEvent e) {
46+
// Update boss bar
47+
tryToShowBossBar(e.getPlayerUUID(), e.getIsland());
48+
}
49+
50+
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
51+
public void onEnterIsland(IslandEnterEvent event) {
52+
if (addon.inWorld(event.getIsland().getWorld())) {
53+
tryToShowBossBar(event.getPlayerUUID(), event.getIsland());
54+
}
55+
}
56+
57+
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
58+
public void onFlagChange(FlagSettingChangeEvent e) {
59+
if (e.getEditedFlag() == addon.BOSSBAR) {
60+
// Show to players on island. If it isn't allowed then this will clean up the boss bar too
61+
e.getIsland().getPlayersOnIsland().stream().map(Player::getUniqueId)
62+
.forEach(uuid -> this.tryToShowBossBar(uuid, e.getIsland()));
63+
}
64+
}
65+
66+
/**
67+
* Try to show the bossbar to the player
68+
* @param uuid player's UUID
69+
* @param island island they are on
70+
*/
71+
private void tryToShowBossBar(UUID uuid, Island island) {
72+
User user = User.getInstance(uuid);
73+
74+
// Only show if enabled for island
75+
if (!island.isAllowed(addon.BOSSBAR)) {
76+
BossBar removed = islandBossBars.remove(island);
77+
if (removed != null) {
78+
// Remove all players from the boss bar
79+
removed.removeAll();
80+
}
81+
return;
82+
}
83+
// Default to showing boss bar unless it is explicitly turned off
84+
if (!user.getMetaData(AONEBLOCK_BOSSBAR).map(MetaDataValue::asBoolean).orElse(true)) {
85+
// Remove any boss bar from user if they are in the world
86+
removeBar(user, island);
87+
// Do not show a boss bar
88+
return;
89+
}
90+
// Prepare boss bar
91+
String title = user.getTranslationOrNothing("aoneblock.bossbar.title");
92+
BarColor c;
93+
try {
94+
c = BarColor.valueOf(user.getTranslation("aoneblock.bossbar.color").toUpperCase(Locale.ENGLISH));
95+
} catch (Exception e) {
96+
c = BarColor.RED;
97+
addon.logError("Bossbar color unknown. Pick from RED, WHITE, PINK, BLUE, GREEN, YELLOW, or PURPLE");
98+
}
99+
BarStyle s = BarStyle.SOLID;
100+
try {
101+
s = BarStyle.valueOf(user.getTranslation("aoneblock.bossbar.style").toUpperCase(Locale.ENGLISH));
102+
} catch (Exception e) {
103+
s = BarStyle.SOLID;
104+
addon.logError(
105+
"Bossbar style unknow. Pick from SOLID, SEGMENTED_6, SEGMENTED_10, SEGMENTED_12, SEGMENTED_20");
106+
}
107+
// Get it or make it
108+
BossBar bar = this.islandBossBars.getOrDefault(island,
109+
Bukkit.createBossBar(title, c, s));
110+
// Get the progress
111+
@NonNull
112+
OneBlockIslands obi = addon.getOneBlocksIsland(island);
113+
114+
// Set progress
115+
bar.setProgress(addon.getOneBlockManager().getPercentageDone(obi) / 100);
116+
int numBlocksToGo = addon.getOneBlockManager().getNextPhaseBlocks(obi);
117+
int phaseBlocks = addon.getOneBlockManager().getPhaseBlocks(obi);
118+
int done = phaseBlocks - numBlocksToGo;
119+
String translation = user.getTranslationOrNothing("aoneblock.bossbar.status", "[togo]",
120+
String.valueOf(numBlocksToGo), "[total]", String.valueOf(phaseBlocks), "[done]", String.valueOf(done));
121+
bar.setTitle(translation);
122+
// Add to user if they don't have it already
123+
Player player = Bukkit.getPlayer(uuid);
124+
if (!bar.getPlayers().contains(player)) {
125+
bar.addPlayer(player);
126+
}
127+
// Save the boss bar for later reference (e.g., when updating or removing)
128+
islandBossBars.put(island, bar);
129+
130+
}
131+
132+
private void removeBar(User user, Island island) {
133+
if (!addon.inWorld(island.getWorld()) || !user.isPlayer() || !user.isOnline()) {
134+
return;
135+
}
136+
BossBar bossBar = islandBossBars.get(island);
137+
if (bossBar != null) {
138+
bossBar.removePlayer(user.getPlayer());
139+
if (bossBar.getPlayers().isEmpty()) {
140+
// Clean up
141+
islandBossBars.remove(island);
142+
}
143+
}
144+
145+
}
146+
147+
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
148+
public void onExitIsland(IslandExitEvent event) {
149+
User user = User.getInstance(event.getPlayerUUID());
150+
removeBar(user, event.getIsland());
151+
}
152+
153+
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
154+
public void onJoin(PlayerJoinEvent e) {
155+
// If the player is on an island then show the bar
156+
if (!addon.inWorld(e.getPlayer().getLocation())) {
157+
return;
158+
}
159+
addon.getIslands().getIslandAt(e.getPlayer().getLocation())
160+
.ifPresent(is -> this.tryToShowBossBar(e.getPlayer().getUniqueId(), is));
161+
}
162+
163+
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
164+
public void onQuit(PlayerQuitEvent e) {
165+
// Clean up boss bars
166+
islandBossBars.values().stream().forEach(bb -> bb.removePlayer(e.getPlayer()));
167+
islandBossBars.values().removeIf(bb -> bb.getPlayers().isEmpty());
168+
}
169+
170+
/**
171+
* User-level boss bar control.
172+
* @param user user to toggle
173+
*/
174+
public void toggleUser(User user) {
175+
boolean newState = !user.getMetaData(AONEBLOCK_BOSSBAR).map(MetaDataValue::asBoolean).orElse(true);
176+
user.putMetaData(AONEBLOCK_BOSSBAR, new MetaDataValue(newState));
177+
if (newState) {
178+
// If the player is on an island then show the bar
179+
addon.getIslands().getIslandAt(user.getLocation()).filter(is -> addon.inWorld(is.getWorld()))
180+
.ifPresent(is -> this.tryToShowBossBar(user.getUniqueId(), is));
181+
user.sendMessage("aoneblock.commands.island.bossbar.status_on");
182+
} else {
183+
// Remove player from any boss bars. Adding happens automatically
184+
islandBossBars.forEach((k, v) -> v.removePlayer(user.getPlayer()));
185+
user.sendMessage("aoneblock.commands.island.bossbar.status_off");
186+
}
187+
}
188+
189+
}

src/main/resources/addon.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ permissions:
5050
aoneblock.island:
5151
description: Allow use of '/ob' command - the main island command
5252
default: TRUE
53+
aoneblock.island.bossbar:
54+
description: Allow use of '/ob bossbar' command - toggle the bossbar
55+
default: FALSE
5356
aoneblock.island.home:
5457
description: Allow use of '/ob go' command - teleport you to your island
5558
default: TRUE

0 commit comments

Comments
 (0)