diff --git a/pom.xml b/pom.xml index 535457b6a0..c18dfeb44b 100644 --- a/pom.xml +++ b/pom.xml @@ -138,6 +138,12 @@ codex ${codex.version} + + studio.magemonkey + divinity + 1.0.2-R0.57-SNAPSHOT + provided + com.mojang diff --git a/src/main/java/studio/magemonkey/fabled/api/util/FlagManager.java b/src/main/java/studio/magemonkey/fabled/api/util/FlagManager.java index e4596380a8..debeba9c37 100644 --- a/src/main/java/studio/magemonkey/fabled/api/util/FlagManager.java +++ b/src/main/java/studio/magemonkey/fabled/api/util/FlagManager.java @@ -29,13 +29,22 @@ import org.bukkit.entity.LivingEntity; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; /** * The manager for temporary entity flag data */ public class FlagManager { - private static final Map data = new HashMap<>(); + private static final Map data = new HashMap<>(); + /** + * Guards against reentrant clearFlags calls on the same entity. + * Prevents infinite recursion when FlagExpireTrigger skills add new flags + * during flag-clearing (e.g., on player death), which would re-create a + * FlagData entry and cause clearFlags → clear → removeFlag → clearFlags → ∞. + */ + private static final Set clearingEntities = new HashSet<>(); /** * Retrieves the flag data for an entity. This creates new data if @@ -128,9 +137,21 @@ public static void clearFlags(LivingEntity entity) { if (entity == null) { return; } - FlagData result = data.remove(entity.getEntityId()); - if (result != null) { - result.clear(); + // Guard against reentrant calls on the same entity. + // FlagExpireTrigger skills may call addFlag during clearing, + // re-creating a FlagData entry; without this guard that would + // cause infinite recursion (clearFlags → clear → removeFlag → clearFlags …). + int id = entity.getEntityId(); + if (!clearingEntities.add(id)) { + return; // already being cleared — skip + } + try { + FlagData result = data.remove(id); + if (result != null) { + result.clear(); + } + } finally { + clearingEntities.remove(id); } } } diff --git a/src/main/java/studio/magemonkey/fabled/dynamic/mechanic/DurabilityMechanic.java b/src/main/java/studio/magemonkey/fabled/dynamic/mechanic/DurabilityMechanic.java index a4eb348f7b..42283b77f9 100644 --- a/src/main/java/studio/magemonkey/fabled/dynamic/mechanic/DurabilityMechanic.java +++ b/src/main/java/studio/magemonkey/fabled/dynamic/mechanic/DurabilityMechanic.java @@ -6,6 +6,9 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.Damageable; import org.bukkit.inventory.meta.ItemMeta; +import studio.magemonkey.divinity.stats.items.ItemStats; +import studio.magemonkey.divinity.stats.items.attributes.api.TypedStat; +import studio.magemonkey.divinity.stats.items.attributes.stats.DurabilityStat; import java.util.List; @@ -59,6 +62,14 @@ public boolean execute(final LivingEntity caster, } im.setDamage(im.getDamage() + amount); item.setItemMeta(im); + + try { + DurabilityStat duraStat = (DurabilityStat) ItemStats.getStat(TypedStat.Type.DURABILITY); + if (duraStat != null && ItemStats.hasStat(item, player, TypedStat.Type.DURABILITY)) { + duraStat.reduceDurability(player, item, amount); + } + } catch (Exception ignored) { /* Divinity not loaded */ } + return true; } } diff --git a/src/main/java/studio/magemonkey/fabled/dynamic/mechanic/HealMechanic.java b/src/main/java/studio/magemonkey/fabled/dynamic/mechanic/HealMechanic.java index 1c6a937b79..944b497c5b 100644 --- a/src/main/java/studio/magemonkey/fabled/dynamic/mechanic/HealMechanic.java +++ b/src/main/java/studio/magemonkey/fabled/dynamic/mechanic/HealMechanic.java @@ -28,6 +28,8 @@ import org.bukkit.Bukkit; import org.bukkit.entity.LivingEntity; +import studio.magemonkey.divinity.stats.EntityStats; +import studio.magemonkey.divinity.stats.items.attributes.api.TypedStat; import studio.magemonkey.fabled.api.event.SkillHealEvent; import java.util.List; @@ -61,6 +63,16 @@ public boolean execute(LivingEntity caster, int level, List target amount = target.getMaxHealth() * value / 100; } + try { + double healCast = EntityStats.get(caster).getItemStat(TypedStat.Type.HEALING_CAST, false); + if (healCast != 0) amount *= (1.0 + healCast / 100.0); + } catch (Exception ignored) { /* Divinity not loaded */ } + + try { + double healReceived = EntityStats.get(target).getItemStat(TypedStat.Type.HEALING_RECEIVED, false); + if (healReceived != 0) amount *= (1.0 + healReceived / 100.0); + } catch (Exception ignored) { /* Divinity not loaded */ } + SkillHealEvent event = new SkillHealEvent(caster, target, amount); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { diff --git a/src/main/java/studio/magemonkey/fabled/dynamic/mechanic/StatusMechanic.java b/src/main/java/studio/magemonkey/fabled/dynamic/mechanic/StatusMechanic.java index 185270e9a0..3cd7f30929 100644 --- a/src/main/java/studio/magemonkey/fabled/dynamic/mechanic/StatusMechanic.java +++ b/src/main/java/studio/magemonkey/fabled/dynamic/mechanic/StatusMechanic.java @@ -27,6 +27,8 @@ package studio.magemonkey.fabled.dynamic.mechanic; import org.bukkit.entity.LivingEntity; +import studio.magemonkey.divinity.stats.EntityStats; +import studio.magemonkey.divinity.stats.items.attributes.api.TypedStat; import studio.magemonkey.fabled.api.util.FlagManager; import java.util.List; @@ -35,8 +37,9 @@ * Applies a flag to each target */ public class StatusMechanic extends MechanicComponent { - private static final String KEY = "status"; - private static final String DURATION = "duration"; + private static final String KEY = "status"; + private static final String DURATION = "duration"; + private static final String IGNORE_CC_RES = "ignore-cc-resistance"; @Override public String getKey() { @@ -49,10 +52,20 @@ public boolean execute(LivingEntity caster, int level, List target return false; } - String key = settings.getString(KEY, "stun").toLowerCase(); - double seconds = parseValues(caster, DURATION, level, 3.0); - int ticks = (int) (seconds * 20); + String key = settings.getString(KEY, "stun").toLowerCase(); + double seconds = parseValues(caster, DURATION, level, 3.0); + boolean ignoreCCRes = settings.getBool(IGNORE_CC_RES, false); for (LivingEntity target : targets) { + int ticks = (int) (seconds * 20); + if (!ignoreCCRes) { + try { + EntityStats stats = EntityStats.get(target); + double resistance = stats.getItemStat(TypedStat.Type.CC_RESISTANCE, false); + if (resistance > 0) { + ticks = (int) (ticks * (1.0 - resistance / 100.0)); + } + } catch (Exception ignored) { /* Divinity not loaded */ } + } FlagManager.addFlag(target, key, ticks); } return !targets.isEmpty(); diff --git a/src/main/java/studio/magemonkey/fabled/dynamic/target/TargetComponent.java b/src/main/java/studio/magemonkey/fabled/dynamic/target/TargetComponent.java index 36b151d8f0..0d453dd342 100644 --- a/src/main/java/studio/magemonkey/fabled/dynamic/target/TargetComponent.java +++ b/src/main/java/studio/magemonkey/fabled/dynamic/target/TargetComponent.java @@ -116,7 +116,7 @@ boolean isValidTarget(final LivingEntity caster, final LivingEntity from, final if (target instanceof Player && (((Player) target).getGameMode() == GameMode.SPECTATOR || ((Player) target).getGameMode() == GameMode.CREATIVE)) return false; - return target != caster && Fabled.getSettings().isValidTarget(target) && (throughWall + return (self != IncludeCaster.FALSE || target != caster) && Fabled.getSettings().isValidTarget(target) && (throughWall || !TargetHelper.isObstructed(from.getEyeLocation(), target.getEyeLocation())) && (everyone || allies == Fabled.getSettings().isAlly(caster, target)); }