Skip to content
3 changes: 1 addition & 2 deletions src/main/java/io/github/pylonmc/pylon/base/PylonBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import io.github.pylonmc.pylon.base.content.building.IgneousCompositeListener;
import io.github.pylonmc.pylon.base.content.building.Immobilizer;
import io.github.pylonmc.pylon.base.content.machines.fluid.Sprinkler;
import io.github.pylonmc.pylon.base.content.tools.HealthTalisman;
import io.github.pylonmc.pylon.base.content.tools.ItemMagnet;
import io.github.pylonmc.pylon.base.content.tools.SoulboundRune;
import io.github.pylonmc.pylon.base.content.tools.HealthTalisman;
import io.github.pylonmc.pylon.base.content.tools.base.Rune;
import io.github.pylonmc.pylon.core.addon.PylonAddon;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
Expand Down Expand Up @@ -59,7 +59,6 @@ public void onEnable() {
pm.registerEvents(new Rune.RuneListener(), this);
new ItemMagnet.Ticker().runTaskTimer(this, 0, 10);
pm.registerEvents(new SoulboundRune.SoulboundRuneListener(), this);
new HealthTalisman.HealthTalismanTicker().runTaskTimer(this, 0, BaseConfig.HEALTH_TALISMAN_CHECK_INTERVAL);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,34 @@
package io.github.pylonmc.pylon.base.content.tools;

import com.google.common.base.Preconditions;
import io.github.pylonmc.pylon.base.PylonBase;
import io.github.pylonmc.pylon.base.content.tools.base.Talisman;
import io.github.pylonmc.pylon.core.config.adapter.ConfigAdapter;
import io.github.pylonmc.pylon.core.i18n.PylonArgument;
import io.github.pylonmc.pylon.core.item.PylonItem;
import io.github.pylonmc.pylon.core.util.gui.unit.UnitFormat;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.attribute.AttributeModifier;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;

import java.util.List;


@SuppressWarnings("UnstableApiUsage")
public class HealthTalisman extends PylonItem {
public class HealthTalisman extends Talisman {

private static final NamespacedKey HEALTH_BOOSTED_KEY = new NamespacedKey(PylonBase.getInstance(), "talisman_health_boosted");
private static final NamespacedKey HEALTH_TALISMAN_KEY = new NamespacedKey(PylonBase.getInstance(), "health_talisman");

private final int maxHealthBoost = getSettings().getOrThrow("max-health-boost", ConfigAdapter.INT);

public HealthTalisman(@NotNull ItemStack stack) {
super(stack);
}

public final AttributeModifier healthModifier = new AttributeModifier(
HEALTH_BOOSTED_KEY,
HEALTH_TALISMAN_KEY,
maxHealthBoost,
AttributeModifier.Operation.ADD_NUMBER
);
Expand All @@ -41,40 +38,34 @@ public HealthTalisman(@NotNull ItemStack stack) {
return List.of(PylonArgument.of("health-boost", UnitFormat.HEARTS.format(maxHealthBoost)));
}

public static class HealthTalismanTicker extends BukkitRunnable {
@Override
public void removeEffect(@NotNull Player player) {
super.removeEffect(player);
AttributeInstance playerHealth = player.getAttribute(Attribute.MAX_HEALTH);
Preconditions.checkNotNull(playerHealth);
playerHealth.removeModifier(HEALTH_TALISMAN_KEY);
}

@Override
// Suppresses warnings from doing PDC.has() and then .get() and assuming .get is not null, and assuming that the player has the max health attribute
@SuppressWarnings("DataFlowIssue")
public void run() {
for (Player player : Bukkit.getOnlinePlayers()) {
boolean foundItem = false;
PersistentDataContainer playerPDC = player.getPersistentDataContainer();
AttributeInstance playerHealth = player.getAttribute(Attribute.MAX_HEALTH);
Integer playerHealthBoost = playerPDC.get(HEALTH_BOOSTED_KEY, PersistentDataType.INTEGER);
for (ItemStack itemStack : player.getInventory()) {
PylonItem pylonItem = fromStack(itemStack);
if (!(pylonItem instanceof HealthTalisman talisman)) {
continue;
}
if (playerHealthBoost == null) {
playerHealth.addModifier(talisman.healthModifier);
playerPDC.set(HEALTH_BOOSTED_KEY, PersistentDataType.INTEGER, talisman.maxHealthBoost);
foundItem = true;
} else if (playerHealthBoost < talisman.maxHealthBoost) {
playerHealth.removeModifier(HEALTH_BOOSTED_KEY);
playerHealth.addModifier(talisman.healthModifier);
playerPDC.set(HEALTH_BOOSTED_KEY, PersistentDataType.INTEGER, talisman.maxHealthBoost);
foundItem = true;
} else if (talisman.maxHealthBoost == playerHealthBoost) {
foundItem = true;
}
}
if (!foundItem && playerHealthBoost != null) {
playerHealth.removeModifier(HEALTH_BOOSTED_KEY);
playerPDC.remove(HEALTH_BOOSTED_KEY);
}
}
}
@Override
public void applyEffect(@NotNull Player player) {
super.applyEffect(player);
AttributeInstance playerHealth = player.getAttribute(Attribute.MAX_HEALTH);
Preconditions.checkNotNull(playerHealth);
playerHealth.addModifier(healthModifier);
}

@Override
public int getLevel() {
return maxHealthBoost;
}

@Override
public NamespacedKey getTalismanKey() {
return HEALTH_TALISMAN_KEY;
}

@Override
public long getTickInterval() {
return 4L;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package io.github.pylonmc.pylon.base.content.tools.base;

import io.github.pylonmc.pylon.base.PylonBase;
import io.github.pylonmc.pylon.core.config.PylonConfig;
import io.github.pylonmc.pylon.core.item.PylonItem;
import io.github.pylonmc.pylon.core.item.base.PylonInventoryTicker;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.MustBeInvokedByOverriders;
import org.jetbrains.annotations.NotNull;

import java.util.HashMap;
import java.util.UUID;
import java.util.WeakHashMap;

public abstract class Talisman extends PylonItem implements PylonInventoryTicker {
private static final HashMap<NamespacedKey, HashMap<UUID, BukkitTask>> tasks = new HashMap<>();

public Talisman(@NotNull ItemStack stack) {
super(stack);
}

@Override
public void onTick(@NotNull Player player) {
tasks.putIfAbsent(getTalismanKey(), new HashMap<>());
boolean foundItem = false;
Integer currentTalismanLevel = player.getPersistentDataContainer().get(getTalismanKey(), PersistentDataType.INTEGER);
if (currentTalismanLevel == null) {
applyEffect(player);
foundItem = true;
} else if (currentTalismanLevel < getLevel()) {
removeEffect(player);
applyEffect(player);
foundItem = true;
} else if (currentTalismanLevel == getLevel()) {
foundItem = true;
}
BukkitTask toCancel = tasks.get(getTalismanKey()).get(player.getUniqueId());
if (foundItem) {
if (toCancel != null) {
toCancel.cancel();
}
tasks.get(getTalismanKey()).put(player.getUniqueId(), Bukkit.getScheduler().runTaskLater(PylonBase.getInstance(), () ->
removeEffect(player),
getTickInterval() * PylonConfig.getInventoryTickerBaseRate() + 1));
}
}

/**
* The implementation of this method MUST call super.applyEffect
* @param player The player who the effect is being applied to
*/
@MustBeInvokedByOverriders
public void applyEffect(@NotNull Player player) {
player.getPersistentDataContainer().set(getTalismanKey(), PersistentDataType.INTEGER, getLevel());
}

/**
* The implementation of this method MUST call super.removeEffect
* @param player The player who the effect is being removed from
*/
@MustBeInvokedByOverriders
public void removeEffect(@NotNull Player player) {
player.getPersistentDataContainer().remove(getTalismanKey());
}

/**
* Get the level of the talisman, this is used to determine which talismans should overwrite other ones
*/
public abstract int getLevel();

/**
* Get the generic key of the talisman, should be the same between all talismans of the same type, ie all health talisman levels have the same return value for this.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Get the generic key of the talisman, should be the same between all talismans of the same type, ie all health talisman levels have the same return value for this.
* Get the generic key of the talisman, should be the same between all talismans of the same type, e.x. all health talisman levels have the same return value for this.

Nit

*/
public abstract NamespacedKey getTalismanKey();
}