Skip to content
Merged
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number>
<!-- This allows to change between versions. -->
<build.version>2.19.3</build.version>
<build.version>2.20.0</build.version>
<sonar.projectKey>BentoBoxWorld_Level</sonar.projectKey>
<sonar.organization>bentobox-world</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
Expand Down
80 changes: 47 additions & 33 deletions src/main/java/world/bentobox/level/Level.java
Original file line number Diff line number Diff line change
Expand Up @@ -339,12 +339,13 @@ public long getIslandLevel(World world, @Nullable UUID targetPlayer) {
}

/**
* Sets the player's level to a value
*
* @param world - world
* @param targetPlayer - target player
* @param level - level
*/
* Sets the player's level to a value. This will only last until the player reruns the level command
*
* @param world - world
* @param targetPlayer - target player
* @param level - level
* @deprecated This is a useless method.
*/
public void setIslandLevel(World world, UUID targetPlayer, long level) {
getManager().setIslandLevel(world, targetPlayer, level);
}
Expand All @@ -355,19 +356,31 @@ public void setIslandLevel(World world, UUID targetPlayer, long level) {
* @param island - island
* @param level - initial calculated island level
*/
public void setInitialIslandLevel(@NonNull Island island, long level) {
getManager().setInitialIslandLevel(island, level);
}
/* TODO
public void setInitialIslandLevel(@NonNull Island island, long level) {
getManager().setInitialIslandLevel(island, level);
}

/**
* Get the initial island level
*
* @param island - island
* @return level or 0 by default
*/
/* TODO
public long getInitialIslandLevel(@NonNull Island island) {
return getManager().getInitialLevel(island);
}*/

/**
* Get the initial island level
*
* @param island - island
* @return level or 0 by default
*/
public long getInitialIslandLevel(@NonNull Island island) {
return getManager().getInitialLevel(island);
}
/**
* Get the initial island count
*
* @param island - island
* @return count or 0 by default
*/
public long getInitialIslandCount(@NonNull Island island) {
return getManager().getInitialCount(island);
}

/**
* Calculates a user's island
Expand All @@ -392,21 +405,22 @@ public void calculateIslandLevel(World world, @Nullable User user, @NonNull UUID
* @return LevelsData object or null if not found. Only island levels are set!
* @deprecated Do not use this anymore. Use {@link #getIslandLevel(World, UUID)}
*/
@Deprecated(since = "2.3.0", forRemoval = true)
public LevelsData getLevelsData(UUID targetPlayer) {
LevelsData ld = new LevelsData(targetPlayer);
getPlugin().getAddonsManager().getGameModeAddons().stream()
.filter(gm -> !settings.getGameModes().contains(gm.getDescription().getName())).forEach(gm -> {
if (getSettings().isZeroNewIslandLevels()) {
Island island = getIslands().getIsland(gm.getOverWorld(), targetPlayer);
if (island != null) {
ld.setInitialLevel(gm.getOverWorld(), this.getInitialIslandLevel(island));
}
}
ld.setLevel(gm.getOverWorld(), this.getIslandLevel(gm.getOverWorld(), targetPlayer));
});
return ld;
}
/*
@Deprecated(since = "2.3.0", forRemoval = true)
public LevelsData getLevelsData(UUID targetPlayer) {
LevelsData ld = new LevelsData(targetPlayer);
getPlugin().getAddonsManager().getGameModeAddons().stream()
.filter(gm -> !settings.getGameModes().contains(gm.getDescription().getName())).forEach(gm -> {
if (getSettings().isZeroNewIslandLevels()) {
Island island = getIslands().getIsland(gm.getOverWorld(), targetPlayer);
if (island != null) {
ld.setInitialLevel(gm.getOverWorld(), this.getInitialIslandLevel(island));
}
}
ld.setLevel(gm.getOverWorld(), this.getIslandLevel(gm.getOverWorld(), targetPlayer));
});
return ld;
}*/

/**
* @return the registeredGameModes
Expand Down
84 changes: 66 additions & 18 deletions src/main/java/world/bentobox/level/LevelsManager.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package world.bentobox.level;

import java.io.IOException;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.time.Instant;
import java.util.AbstractMap;
import java.util.Collections;
Expand All @@ -26,6 +28,7 @@

import world.bentobox.bentobox.database.Database;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.level.calculators.EquationEvaluator;
import world.bentobox.level.calculators.Results;
import world.bentobox.level.events.IslandLevelCalculatedEvent;
import world.bentobox.level.events.IslandPreLevelEvent;
Expand Down Expand Up @@ -130,7 +133,7 @@ private boolean fireIslandLevelCalcEvent(UUID targetPlayer, Island island, Resul
return true;
// Set the values if they were altered
results.setLevel((Long) ilce.getKeyValues().getOrDefault("level", results.getLevel()));
results.setInitialLevel((Long) ilce.getKeyValues().getOrDefault("initialLevel", results.getInitialLevel()));
results.setInitialCount((Long) ilce.getKeyValues().getOrDefault("initialCount", results.getInitialCount()));
results.setDeathHandicap((int) ilce.getKeyValues().getOrDefault("deathHandicap", results.getDeathHandicap()));
results.setPointsToNextLevel(
(Long) ilce.getKeyValues().getOrDefault("pointsToNextLevel", results.getPointsToNextLevel()));
Expand Down Expand Up @@ -168,13 +171,65 @@ public String formatLevel(@Nullable Long lvl) {
}

/**
* Get the initial level of the island. Used to zero island levels
* Get the initial count of the island. Used to zero island levels
*
* @param island - island
* @return initial level of island
* @return initial count of island
*/
public long getInitialLevel(Island island) {
return getLevelsData(island).getInitialLevel();
@SuppressWarnings("deprecation")
public long getInitialCount(Island island) {
Long initialLevel = getLevelsData(island).getInitialLevel(); // Backward compatibility check. For all new islands, this should be null.
Long initialCount = getLevelsData(island).getInitialCount();
if (initialLevel != null) {
// Initial level exists so convert it
if (initialCount == null) { // If initialCount is not null, then this is an edge case and initialCount will be used and initialLevel discarded
// Convert from level to count
initialCount = 0L;
try {
initialCount = getNumBlocks(initialLevel);
} catch (Exception e) {
addon.logError("Could not convert legacy initial level to count, so it will be set to 0. Error is: "
+ e.getLocalizedMessage());
initialCount = 0L;
}
}
// Null out the old initial level and save
getLevelsData(island).setInitialLevel(null);
// Save
this.setInitialIslandCount(island, initialCount);
}
// If initialCount doesn't exist, set it to 0L
if (initialCount == null) {
initialCount = 0L;
getLevelsData(island).setInitialCount(0L);
}
return initialCount;
}

/**
* Runs the level calculation using the current formula until the level matches the initial value, or fails.
* @param initialLevel - the old initial level
* @return block count to obtain this level now
* @throws ParseException if the formula for level calc is bugged
* @throws IOException if the number of blocks cannot be found for this level
*/
private long getNumBlocks(final long initialLevel) throws ParseException, IOException {
String calcString = addon.getSettings().getLevelCalc();
int result = -1;
long calculatedLevel = 0;
String withCost = calcString.replace("level_cost", String.valueOf(this.addon.getSettings().getLevelCost()));
long time = System.currentTimeMillis() + 10 * 1000; // 10 seconds
do {
result++;
if (System.currentTimeMillis() > time) {
throw new IOException("Timeout: Blocks cannot be found to create this initial level");
}
// Paste in the values to the formula
String withValues = withCost.replace("blocks", String.valueOf(result));
// Try and evaluate it
calculatedLevel = (long) EquationEvaluator.eval(withValues);
} while (calculatedLevel != initialLevel);
return result;
}

/**
Expand Down Expand Up @@ -422,15 +477,13 @@ public void removeEntry(World world, String uuid) {
}

/**
* Set an initial island level
* Set an initial island count
*
* @param island - the island to set. Must have a non-null world
* @param lv - initial island level
* @param island - the island to set.
* @param lv - initial island count
*/
public void setInitialIslandLevel(@NonNull Island island, long lv) {
if (island.getWorld() == null)
return;
levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new).setInitialLevel(lv);
public void setInitialIslandCount(@NonNull Island island, long lv) {
levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new).setInitialCount(lv);
handler.saveObjectAsync(levelsCache.get(island.getUniqueId()));
}

Expand All @@ -448,12 +501,7 @@ public void setIslandLevel(@NonNull World world, @NonNull UUID targetPlayer, lon
if (island != null) {
String id = island.getUniqueId();
IslandLevels il = levelsCache.computeIfAbsent(id, IslandLevels::new);
// Remove the initial level
if (addon.getSettings().isZeroNewIslandLevels()) {
il.setLevel(lv - il.getInitialLevel());
} else {
il.setLevel(lv);
}
il.setLevel(lv);
handler.saveObjectAsync(levelsCache.get(id));
// Update TopTen
addToTopTen(island, levelsCache.get(id).getLevel());
Expand Down
122 changes: 122 additions & 0 deletions src/main/java/world/bentobox/level/PlaceholderManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
import java.util.stream.Collectors;

import org.bukkit.World;
import org.bukkit.Material;
import org.eclipse.jdt.annotation.Nullable;
import org.bukkit.Bukkit;

import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.GameModeAddon;
Expand Down Expand Up @@ -97,6 +99,87 @@ protected void registerPlaceholders(GameModeAddon gm) {
// Personal rank
bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_rank_value",
u -> getRankValue(gm.getOverWorld(), u));

// Register mainhand placeholders
bpm.registerPlaceholder(addon,
gm.getDescription().getName().toLowerCase() + "_island_value_mainhand",
user -> {
if (user.getPlayer() == null || !user.getPlayer().getInventory().getItemInMainHand().getType().isBlock()) {
return "0";
}
String blockName = user.getPlayer().getInventory().getItemInMainHand().getType().getKey().getKey();
return String.valueOf(Objects.requireNonNullElse(
addon.getBlockConfig().getValue(gm.getOverWorld(), blockName),
0
));
}
);

bpm.registerPlaceholder(addon,
gm.getDescription().getName().toLowerCase() + "_island_count_mainhand",
user -> {
if (user.getPlayer() == null || !user.getPlayer().getInventory().getItemInMainHand().getType().isBlock()) {
return "0";
}
Material material = user.getPlayer().getInventory().getItemInMainHand().getType();
return getBlockCount(gm, user, material);
}
);

// Register looking at block placeholders
bpm.registerPlaceholder(addon,
gm.getDescription().getName().toLowerCase() + "_island_value_looking",
user -> {
if (user.getPlayer() == null) return "0";
var targetBlock = user.getPlayer().getTargetBlock(null, 5);
if (targetBlock != null && !targetBlock.getType().isAir()) {
String blockName = targetBlock.getType().getKey().getKey();
return String.valueOf(Objects.requireNonNullElse(
addon.getBlockConfig().getValue(gm.getOverWorld(), blockName),
0
));
}
return "0";
}
);

bpm.registerPlaceholder(addon,
gm.getDescription().getName().toLowerCase() + "_island_count_looking",
user -> {
if (user.getPlayer() == null) return "0";
var targetBlock = user.getPlayer().getTargetBlock(null, 5);
if (targetBlock != null && !targetBlock.getType().isAir()) {
return getBlockCount(gm, user, targetBlock.getType());
}
return "0";
}
);

// Register placeholders for all block materials
if (Bukkit.getServer() != null) {
// Get all materials from the block config
addon.getBlockConfig().getBlockValues().keySet().forEach(blockName -> {
String formattedName = blockName.replace(':', '_').toLowerCase();

// Register value placeholder
bpm.registerPlaceholder(addon,
gm.getDescription().getName().toLowerCase() + "_island_value_" + formattedName,
user -> String.valueOf(Objects.requireNonNullElse(
addon.getBlockConfig().getValue(gm.getOverWorld(), blockName),
0
))
);

// Register count placeholder
bpm.registerPlaceholder(addon,
gm.getDescription().getName().toLowerCase() + "_island_count_" + formattedName,
user -> {
Material material = Material.valueOf(blockName.toUpperCase());
return getBlockCount(gm, user, material);
}
);
});
}
}

/**
Expand Down Expand Up @@ -220,4 +303,43 @@ String getVisitedIslandLevel(GameModeAddon gm, User user) {
.orElse("0");
}

/**
* Gets the block count for a specific material in a user's island
* @param gm GameModeAddon
* @param user User requesting the count
* @param material Material to count
* @return String representation of the count
*/
private String getBlockCount(GameModeAddon gm, User user, Object material) {
if (user == null) {
return "0";
}
return getBlockCountForUser(gm, user, material);
}

/**
* Gets the block count for a specific material in a user's island
* @param gm GameModeAddon
* @param user User to get count for
* @param material Material to count
* @return String representation of the count
*/
private String getBlockCountForUser(GameModeAddon gm, User user, Object material) {
// Get the island for the user
Island island = addon.getIslands().getIsland(gm.getOverWorld(), user);
if (island == null) {
return "0";
}

// Get the level data for the island
IslandLevels data = addon.getManager().getLevelsData(island);
if (data == null) {
return "0";
}

// Get the total count from both above sea level and underwater
int count = data.getMdCount().getOrDefault(material, 0) + data.getUwCount().getOrDefault(material, 0);
return String.valueOf(count);
}

}
Loading