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));
}