adjust API
This commit is contained in:
@@ -32,6 +32,29 @@ int appliedCount = AilmentApi.applyEffects(target, source,
|
||||
Bleed/Soul Rot stacking and `timed` for an explicit duration and amplifier. `AilmentApi.getEffect(type)` exposes the
|
||||
registered `MobEffect` when an integration needs to inspect an active effect without depending on registry internals.
|
||||
|
||||
Other mods can use the same source-safe machinery for their own registered effects:
|
||||
|
||||
```java
|
||||
MobEffect frost = MyEffects.FROST.get();
|
||||
|
||||
// Raw duration, vanilla merging, real Forge source, and persistent latest-source tracking.
|
||||
AilmentApi.applyEffect(target, source, frost, 100, 0);
|
||||
|
||||
// Add one stack per call, cap at 4 stacks, and refresh to 100 ticks.
|
||||
AilmentApi.applyStackingEffect(target, source, frost, 100, 4);
|
||||
|
||||
// Add one stack, extend remaining duration, and Arcane-scale each added duration chunk once.
|
||||
AilmentApi.applyStackingEffect(target, source, frost, 100, 4, true, true);
|
||||
|
||||
LivingEntity loadedSource = AilmentApi.getEffectSource(target, frost);
|
||||
UUID sourceId = AilmentApi.getEffectSourceUuid(target, frost);
|
||||
```
|
||||
|
||||
`applyScaledEffect` provides the non-stacking Arcane-scaled variant. Generic applications are server-only, reject
|
||||
invalid or cross-dimension sources, and honor team/friendly-fire rules for harmful player-on-player effects. Source
|
||||
metadata is keyed by the external effect's registry ID and is cleared lazily when queried after the effect expires;
|
||||
`clearEffectSource` can clear it explicitly without removing the effect.
|
||||
|
||||
Fracture stacks up to three times. Each stack removes 2 armor and 1 armor toughness; reapplication adds a stack and
|
||||
refreshes its duration. Arcane scales that refreshed duration but never changes the fixed attribute penalties.
|
||||
|
||||
@@ -73,10 +96,15 @@ snapshotted on application so source unloading does not alter an active effect.
|
||||
Entity type tags:
|
||||
|
||||
- `ascend_ailments:bleed_immune`
|
||||
- `ascend_ailments:soulless` (immune to Soul Rot)
|
||||
- `ascend_ailments:sturdy` (immune to Fracture)
|
||||
- `ascend_ailments:mental_control_resistant`
|
||||
- `ascend_ailments:mental_control_immune`
|
||||
|
||||
Players remain Bleed-compatible and non-player undead are automatically rejected independently of tags.
|
||||
Vanilla undead are included in `soulless`, and any vanilla or modded entity classified with `MobType.UNDEAD` is
|
||||
also rejected independently of the tag. `sturdy` includes iron golems by default. Non-player undead remain naturally
|
||||
Bleed-immune; `bleed_immune` takes precedence even for players and also prevents and clears Hemorrhage buildup.
|
||||
Integrations can query `AilmentApi.canSoulRot`, `canFracture`, and `canBleed` before presenting or applying abilities.
|
||||
|
||||
Item tags:
|
||||
|
||||
|
||||
@@ -8,14 +8,17 @@ import net.fretux.ailments.util.MentalControlUtil;
|
||||
import net.fretux.ailments.util.ModEntityTypeTags;
|
||||
import net.fretux.ailments.util.HemorrhageTracker;
|
||||
import net.minecraft.world.effect.MobEffect;
|
||||
import net.minecraft.world.effect.MobEffectCategory;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.MobType;
|
||||
import net.minecraft.world.entity.monster.EnderMan;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/** Stable, source-aware entry point for applying and querying Ascend: Ailments effects. */
|
||||
public final class AilmentApi {
|
||||
@@ -85,6 +88,97 @@ public final class AilmentApi {
|
||||
return applied;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies an effect owned by another mod with standard Forge source propagation and persistent source tracking.
|
||||
* The requested duration is used exactly and is not Arcane-scaled. Vanilla effect-merging rules apply.
|
||||
*/
|
||||
public static boolean applyEffect(LivingEntity target, @Nullable LivingEntity source, MobEffect effect,
|
||||
int requestedTicks, int amplifier) {
|
||||
return applyExternalEffect(target, source, effect, requestedTicks, amplifier, false);
|
||||
}
|
||||
|
||||
/** Applies an amplifier-zero external effect with source propagation and persistent source tracking. */
|
||||
public static boolean applyEffect(LivingEntity target, @Nullable LivingEntity source, MobEffect effect,
|
||||
int requestedTicks) {
|
||||
return applyEffect(target, source, effect, requestedTicks, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies an external effect and scales its requested duration once using the optional Ascend Arcane curve.
|
||||
* Use this overload only when the owning mod wants Ailments to be the duration-scaling authority.
|
||||
*/
|
||||
public static boolean applyScaledEffect(LivingEntity target, @Nullable LivingEntity source, MobEffect effect,
|
||||
int requestedTicks, int amplifier) {
|
||||
return applyExternalEffect(target, source, effect, requestedTicks, amplifier, true);
|
||||
}
|
||||
|
||||
/** Applies an amplifier-zero external effect with one-time Arcane duration scaling. */
|
||||
public static boolean applyScaledEffect(LivingEntity target, @Nullable LivingEntity source, MobEffect effect,
|
||||
int requestedTicks) {
|
||||
return applyScaledEffect(target, source, effect, requestedTicks, 0);
|
||||
}
|
||||
|
||||
/** Adds one amplifier stack to an external effect, refreshes its duration, and tracks the latest source. */
|
||||
public static boolean applyStackingEffect(LivingEntity target, @Nullable LivingEntity source, MobEffect effect,
|
||||
int requestedTicks, int maximumStacks) {
|
||||
return applyStackingEffect(target, source, effect, requestedTicks, maximumStacks, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one stack to an external effect with configurable duration behavior and optional Arcane scaling.
|
||||
* {@code maximumStacks} uses a human-facing one-based count; the applied amplifier is capped at one less.
|
||||
* When {@code extendDuration} is true, the scaled requested duration is added to the remaining duration.
|
||||
*/
|
||||
public static boolean applyStackingEffect(LivingEntity target, @Nullable LivingEntity source, MobEffect effect,
|
||||
int requestedTicks, int maximumStacks, boolean extendDuration,
|
||||
boolean scaleDurationWithArcane) {
|
||||
Objects.requireNonNull(effect, "effect");
|
||||
if (!externalApplicationValid(target, source, effect, requestedTicks, 0)
|
||||
|| maximumStacks < 1 || maximumStacks > 256) return false;
|
||||
MobEffectInstance current = target.getEffect(effect);
|
||||
if (current != null && current.isInfiniteDuration()) return false;
|
||||
int amplifier = current == null ? 0 : current.getAmplifier() >= maximumStacks - 1
|
||||
? maximumStacks - 1 : current.getAmplifier() + 1;
|
||||
int duration = scaleDurationWithArcane ? AilmentScaling.scaleDuration(requestedTicks, source) : requestedTicks;
|
||||
if (extendDuration && current != null) duration = safeAdd(current.getDuration(), duration);
|
||||
boolean applied = addStackingEffect(target, source, effect, duration, amplifier, false, true, true);
|
||||
if (applied) trackExternalSource(target, source, effect);
|
||||
return applied;
|
||||
}
|
||||
|
||||
/** Returns the UUID of the latest successful source tracked for an active external effect. */
|
||||
@Nullable public static UUID getEffectSourceUuid(LivingEntity target, MobEffect effect) {
|
||||
Objects.requireNonNull(target, "target");
|
||||
Objects.requireNonNull(effect, "effect");
|
||||
String key = externalSourceKey(effect);
|
||||
if (key == null) return null;
|
||||
if (!target.hasEffect(effect)) {
|
||||
EffectSourceUtil.clear(target, key);
|
||||
return null;
|
||||
}
|
||||
return EffectSourceUtil.getSourceUuid(target, key);
|
||||
}
|
||||
|
||||
/** Returns the latest tracked source when it is currently loaded, or {@code null}. */
|
||||
@Nullable public static LivingEntity getEffectSource(LivingEntity target, MobEffect effect) {
|
||||
if (getEffectSourceUuid(target, effect) == null) return null;
|
||||
return EffectSourceUtil.getSource(target, externalSourceKey(effect));
|
||||
}
|
||||
|
||||
/** Resolves the latest tracked source only when it is loaded in the target's current level. */
|
||||
@Nullable public static LivingEntity getEffectSourceInLevel(LivingEntity target, MobEffect effect) {
|
||||
if (getEffectSourceUuid(target, effect) == null) return null;
|
||||
return EffectSourceUtil.getSourceInLevel(target, externalSourceKey(effect));
|
||||
}
|
||||
|
||||
/** Clears source metadata created by the generic external-effect API without removing the effect. */
|
||||
public static void clearEffectSource(LivingEntity target, MobEffect effect) {
|
||||
Objects.requireNonNull(target, "target");
|
||||
Objects.requireNonNull(effect, "effect");
|
||||
String key = externalSourceKey(effect);
|
||||
if (key != null) EffectSourceUtil.clear(target, key);
|
||||
}
|
||||
|
||||
/** Returns the registered MobEffect represented by a stable public ailment identifier. */
|
||||
public static MobEffect getEffect(AilmentType type) {
|
||||
Objects.requireNonNull(type, "type");
|
||||
@@ -108,7 +202,8 @@ public final class AilmentApi {
|
||||
/** Applies one Soul Rot stack; disabling extension always refreshes the configured base duration. */
|
||||
public static boolean applySoulRot(LivingEntity target, @Nullable LivingEntity source,
|
||||
boolean extendWithStacks) {
|
||||
if (!serverValid(target) || !pvpProcReady(target, source, AilmentType.SOUL_ROT)) return false;
|
||||
if (!serverValid(target) || !canSoulRot(target)
|
||||
|| !pvpProcReady(target, source, AilmentType.SOUL_ROT)) return false;
|
||||
MobEffect effect = ModEffects.SOUL_ROT.get();
|
||||
MobEffectInstance current = target.getEffect(effect);
|
||||
int currentStacks = current == null ? 0 : current.getAmplifier() + 1;
|
||||
@@ -130,7 +225,8 @@ public final class AilmentApi {
|
||||
/** Applies an explicit, capped Soul Rot amplifier and source-scaled requested duration. */
|
||||
public static boolean applySoulRot(LivingEntity target, @Nullable LivingEntity source,
|
||||
int requestedTicks, int amplifier) {
|
||||
if (!serverValid(target) || !pvpProcReady(target, source, AilmentType.SOUL_ROT)) return false;
|
||||
if (!serverValid(target) || !canSoulRot(target)
|
||||
|| !pvpProcReady(target, source, AilmentType.SOUL_ROT)) return false;
|
||||
int capped = Math.min(Math.max(0, amplifier),
|
||||
AilmentsConfig.value(AilmentsConfig.SOUL_ROT_MAX_STACKS) - 1);
|
||||
return finishApplication(applyDot(target, source, ModEffects.SOUL_ROT.get(), EffectSourceUtil.SOUL_ROT,
|
||||
@@ -183,7 +279,7 @@ public final class AilmentApi {
|
||||
*/
|
||||
public static boolean applyFracture(LivingEntity target, @Nullable LivingEntity source,
|
||||
int requestedTicks, int amplifier) {
|
||||
if (!serverValid(target) || requestedTicks < 1 || amplifier < 0
|
||||
if (!serverValid(target) || !canFracture(target) || requestedTicks < 1 || amplifier < 0
|
||||
|| !pvpProcReady(target, source, AilmentType.FRACTURE)) return false;
|
||||
MobEffect effect = ModEffects.FRACTURE.get();
|
||||
MobEffectInstance current = target.getEffect(effect);
|
||||
@@ -246,8 +342,16 @@ public final class AilmentApi {
|
||||
}
|
||||
|
||||
public static boolean canBleed(LivingEntity target) {
|
||||
if (target instanceof Player) return true;
|
||||
return target.getMobType() != MobType.UNDEAD && !target.getType().is(ModEntityTypeTags.BLEED_IMMUNE);
|
||||
if (target.getType().is(ModEntityTypeTags.BLEED_IMMUNE)) return false;
|
||||
return target instanceof Player || target.getMobType() != MobType.UNDEAD;
|
||||
}
|
||||
/** Returns false for the Soulless tag and for any entity classified as undead by Minecraft or another mod. */
|
||||
public static boolean canSoulRot(LivingEntity target) {
|
||||
return target.getMobType() != MobType.UNDEAD && !target.getType().is(ModEntityTypeTags.SOULLESS);
|
||||
}
|
||||
/** Returns false for entity types in the Sturdy tag. */
|
||||
public static boolean canFracture(LivingEntity target) {
|
||||
return !target.getType().is(ModEntityTypeTags.STURDY);
|
||||
}
|
||||
public static boolean isMentalControlResistant(LivingEntity target) {
|
||||
return MentalControlUtil.isMentalControlResistant(target);
|
||||
@@ -319,12 +423,30 @@ public final class AilmentApi {
|
||||
}
|
||||
return applied;
|
||||
}
|
||||
private static boolean applyExternalEffect(LivingEntity target, @Nullable LivingEntity source, MobEffect effect,
|
||||
int requestedTicks, int amplifier, boolean scaleDurationWithArcane) {
|
||||
Objects.requireNonNull(effect, "effect");
|
||||
if (!externalApplicationValid(target, source, effect, requestedTicks, amplifier)) return false;
|
||||
int duration = scaleDurationWithArcane ? AilmentScaling.scaleDuration(requestedTicks, source) : requestedTicks;
|
||||
boolean applied = addSourcedEffect(target, source, effect, duration, amplifier, false, true, true);
|
||||
if (applied) trackExternalSource(target, source, effect);
|
||||
return applied;
|
||||
}
|
||||
private static MobEffectInstance instance(MobEffect effect, int duration, int amplifier, boolean icon) {
|
||||
return new MobEffectInstance(effect, duration, amplifier, false, false, icon);
|
||||
}
|
||||
private static MobEffectInstance instance(MobEffect effect, int duration, int amplifier, boolean ambient,
|
||||
boolean visible, boolean icon) {
|
||||
return new MobEffectInstance(effect, duration, amplifier, ambient, visible, icon);
|
||||
}
|
||||
private static boolean addSourcedEffect(LivingEntity target, @Nullable LivingEntity source, MobEffect effect,
|
||||
int duration, int amplifier, boolean icon) {
|
||||
boolean applied = target.addEffect(instance(effect, duration, amplifier, icon), source);
|
||||
return addSourcedEffect(target, source, effect, duration, amplifier, false, false, icon);
|
||||
}
|
||||
private static boolean addSourcedEffect(LivingEntity target, @Nullable LivingEntity source, MobEffect effect,
|
||||
int duration, int amplifier, boolean ambient, boolean visible,
|
||||
boolean icon) {
|
||||
boolean applied = target.addEffect(instance(effect, duration, amplifier, ambient, visible, icon), source);
|
||||
if (applied && AscendCompat.isAscendLoaded()) {
|
||||
// Ascend observes the standard Forge source and scales duration during addEffect. Ailments already
|
||||
// applied its source curve, so restore the requested duration to prevent double scaling.
|
||||
@@ -335,19 +457,44 @@ public final class AilmentApi {
|
||||
}
|
||||
private static boolean addStackingEffect(LivingEntity target, @Nullable LivingEntity source, MobEffect effect,
|
||||
int duration, int amplifier, boolean icon) {
|
||||
return addStackingEffect(target, source, effect, duration, amplifier, false, false, icon);
|
||||
}
|
||||
private static boolean addStackingEffect(LivingEntity target, @Nullable LivingEntity source, MobEffect effect,
|
||||
int duration, int amplifier, boolean ambient, boolean visible,
|
||||
boolean icon) {
|
||||
MobEffectInstance previous = target.getEffect(effect);
|
||||
if (previous == null || previous.getAmplifier() >= amplifier)
|
||||
return addSourcedEffect(target, source, effect, duration, amplifier, icon);
|
||||
return addSourcedEffect(target, source, effect, duration, amplifier, ambient, visible, icon);
|
||||
|
||||
// Vanilla preserves a lower-amplifier effect as a hidden fallback when upgrading. These ailments refresh
|
||||
// to one authoritative stack state, so suppress that fallback without emitting a remove/add lifecycle.
|
||||
// Vanilla preserves a lower-amplifier effect as a hidden fallback when upgrading. Stacking applications use
|
||||
// one authoritative stack state, so suppress that fallback without emitting a remove/add lifecycle.
|
||||
int previousDuration = previous.getDuration();
|
||||
previous.mapDuration(ignored -> 0);
|
||||
boolean applied = addSourcedEffect(target, source, effect, duration, amplifier, icon);
|
||||
boolean applied = addSourcedEffect(target, source, effect, duration, amplifier, ambient, visible, icon);
|
||||
if (!applied && target.getEffect(effect) == previous)
|
||||
previous.mapDuration(ignored -> previousDuration);
|
||||
return applied;
|
||||
}
|
||||
private static boolean externalApplicationValid(LivingEntity target, @Nullable LivingEntity source,
|
||||
MobEffect effect, int requestedTicks, int amplifier) {
|
||||
if (!serverValid(target) || requestedTicks < 1 || amplifier < 0) return false;
|
||||
if ((effect == ModEffects.BLEED.get() && !canBleed(target))
|
||||
|| (effect == ModEffects.SOUL_ROT.get() && !canSoulRot(target))
|
||||
|| (effect == ModEffects.FRACTURE.get() && !canFracture(target))) return false;
|
||||
if (source != null && (!source.isAlive() || source.isSpectator()
|
||||
|| source.level().isClientSide || source.level() != target.level())) return false;
|
||||
return effect.getCategory() != MobEffectCategory.HARMFUL
|
||||
|| !(source instanceof Player sourcePlayer && target instanceof Player targetPlayer)
|
||||
|| source == target || sourcePlayer.canHarmPlayer(targetPlayer);
|
||||
}
|
||||
private static void trackExternalSource(LivingEntity target, @Nullable LivingEntity source, MobEffect effect) {
|
||||
String key = externalSourceKey(effect);
|
||||
if (key != null) EffectSourceUtil.setSource(target, key, source);
|
||||
}
|
||||
@Nullable private static String externalSourceKey(MobEffect effect) {
|
||||
var id = ForgeRegistries.MOB_EFFECTS.getKey(effect);
|
||||
return id == null ? null : "ascend_ailments.external." + id;
|
||||
}
|
||||
private static boolean serverValid(LivingEntity target) {
|
||||
return target != null && !target.level().isClientSide && target.isAlive()
|
||||
&& (!(target instanceof Player player) || !player.isSpectator());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.fretux.ailments.effect;
|
||||
|
||||
import net.fretux.ailments.api.AilmentApi;
|
||||
import net.fretux.ailments.config.AilmentsConfig;
|
||||
import net.fretux.ailments.damage.ModDamageSources;
|
||||
import net.fretux.ailments.util.EffectSourceUtil;
|
||||
@@ -20,6 +21,10 @@ public final class BleedEffect extends MobEffect {
|
||||
|
||||
@Override public void applyEffectTick(LivingEntity entity, int amplifier) {
|
||||
if (entity.level().isClientSide) return;
|
||||
if (!AilmentApi.canBleed(entity)) {
|
||||
HemorrhageTracker.clearIfPresent(entity);
|
||||
return;
|
||||
}
|
||||
double base = amplifier <= 0 ? AilmentsConfig.value(AilmentsConfig.BLEED_DAMAGE_0)
|
||||
: amplifier == 1 ? AilmentsConfig.value(AilmentsConfig.BLEED_DAMAGE_1)
|
||||
: AilmentsConfig.value(AilmentsConfig.BLEED_DAMAGE_2);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.fretux.ailments.effect;
|
||||
|
||||
import net.fretux.ailments.api.AilmentApi;
|
||||
import net.fretux.ailments.config.AilmentsConfig;
|
||||
import net.fretux.ailments.util.EffectSourceUtil;
|
||||
import net.minecraft.world.effect.MobEffect;
|
||||
@@ -13,7 +14,8 @@ public final class SoulRotEffect extends MobEffect {
|
||||
@Override public boolean isDurationEffectTick(int duration, int amplifier) { return duration % 20 == 0; }
|
||||
|
||||
@Override public void applyEffectTick(LivingEntity entity, int amplifier) {
|
||||
if (entity.level().isClientSide || !(entity instanceof Player player)) return;
|
||||
if (entity.level().isClientSide || !AilmentApi.canSoulRot(entity)
|
||||
|| !(entity instanceof Player player)) return;
|
||||
float base = (float) (AilmentsConfig.value(AilmentsConfig.SOUL_ROT_EXHAUSTION_BASE)
|
||||
+ AilmentsConfig.value(AilmentsConfig.SOUL_ROT_EXHAUSTION_PER_AMP) * amplifier);
|
||||
player.causeFoodExhaustion(base * EffectSourceUtil.getPotency(entity, EffectSourceUtil.SOUL_ROT));
|
||||
|
||||
@@ -58,7 +58,8 @@ public final class AilmentEvents {
|
||||
@SubscribeEvent
|
||||
public static void onHeal(LivingHealEvent event) {
|
||||
if (!event.getEntity().level().isClientSide && !event.isCanceled()
|
||||
&& event.getEntity().hasEffect(ModEffects.SOUL_ROT.get()))
|
||||
&& event.getEntity().hasEffect(ModEffects.SOUL_ROT.get())
|
||||
&& AilmentApi.canSoulRot(event.getEntity()))
|
||||
event.setAmount((float) (event.getAmount() * AilmentsConfig.value(AilmentsConfig.SOUL_ROT_HEALING)));
|
||||
}
|
||||
|
||||
@@ -97,7 +98,7 @@ public final class AilmentEvents {
|
||||
float amount = event.getAmount();
|
||||
|
||||
// 1. Victim vulnerability.
|
||||
if (victim.hasEffect(ModEffects.SOUL_ROT.get()))
|
||||
if (victim.hasEffect(ModEffects.SOUL_ROT.get()) && AilmentApi.canSoulRot(victim))
|
||||
amount *= (float) AilmentsConfig.value(AilmentsConfig.SOUL_ROT_DAMAGE_TAKEN);
|
||||
|
||||
// 2. Charm victim and attacker rules.
|
||||
|
||||
@@ -26,8 +26,8 @@ public final class ControlEffectHelper {
|
||||
|
||||
public static void handleLivingTick(LivingEntity entity) {
|
||||
if (entity.level().isClientSide) return;
|
||||
handleSoulRotFood(entity);
|
||||
cleanDotData(entity);
|
||||
handleSoulRotFood(entity);
|
||||
if (entity.hasEffect(ModEffects.FEAR.get())) tickFear(entity);
|
||||
else {
|
||||
EffectSourceUtil.clear(entity, EffectSourceUtil.FEAR);
|
||||
@@ -181,9 +181,25 @@ public final class ControlEffectHelper {
|
||||
}
|
||||
|
||||
private static void cleanDotData(LivingEntity entity) {
|
||||
if (!entity.hasEffect(ModEffects.SOUL_ROT.get())) EffectSourceUtil.clear(entity, EffectSourceUtil.SOUL_ROT);
|
||||
if (!entity.hasEffect(ModEffects.BLEED.get())) EffectSourceUtil.clear(entity, EffectSourceUtil.BLEED);
|
||||
if (!entity.hasEffect(ModEffects.FRACTURE.get())) EffectSourceUtil.clear(entity, EffectSourceUtil.FRACTURE);
|
||||
if (!AilmentApi.canSoulRot(entity)) {
|
||||
if (entity.hasEffect(ModEffects.SOUL_ROT.get())) entity.removeEffect(ModEffects.SOUL_ROT.get());
|
||||
EffectSourceUtil.clear(entity, EffectSourceUtil.SOUL_ROT);
|
||||
} else if (!entity.hasEffect(ModEffects.SOUL_ROT.get())) {
|
||||
EffectSourceUtil.clear(entity, EffectSourceUtil.SOUL_ROT);
|
||||
}
|
||||
if (!AilmentApi.canBleed(entity)) {
|
||||
if (entity.hasEffect(ModEffects.BLEED.get())) entity.removeEffect(ModEffects.BLEED.get());
|
||||
EffectSourceUtil.clear(entity, EffectSourceUtil.BLEED);
|
||||
HemorrhageTracker.clearIfPresent(entity);
|
||||
} else if (!entity.hasEffect(ModEffects.BLEED.get())) {
|
||||
EffectSourceUtil.clear(entity, EffectSourceUtil.BLEED);
|
||||
}
|
||||
if (!AilmentApi.canFracture(entity)) {
|
||||
if (entity.hasEffect(ModEffects.FRACTURE.get())) entity.removeEffect(ModEffects.FRACTURE.get());
|
||||
EffectSourceUtil.clear(entity, EffectSourceUtil.FRACTURE);
|
||||
} else if (!entity.hasEffect(ModEffects.FRACTURE.get())) {
|
||||
EffectSourceUtil.clear(entity, EffectSourceUtil.FRACTURE);
|
||||
}
|
||||
}
|
||||
private ControlEffectHelper() {}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.fretux.ailments.util;
|
||||
|
||||
import net.fretux.ailments.config.AilmentsConfig;
|
||||
import net.fretux.ailments.api.AilmentApi;
|
||||
import net.fretux.ailments.damage.ModDamageSources;
|
||||
import net.fretux.ailments.network.AilmentNetwork;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@@ -16,8 +17,12 @@ public final class HemorrhageTracker {
|
||||
* Arcane potency is snapshotted when Bleed is applied, so an unloaded source cannot change an active meter.
|
||||
*/
|
||||
public static boolean recordBleedDamage(LivingEntity target, int amplifier, double arcanePotency) {
|
||||
if (target.level().isClientSide
|
||||
|| amplifier + 1 < AilmentsConfig.value(AilmentsConfig.HEMORRHAGE_MINIMUM_STACKS)) return false;
|
||||
if (target.level().isClientSide) return false;
|
||||
if (!AilmentApi.canBleed(target)) {
|
||||
clear(target);
|
||||
return false;
|
||||
}
|
||||
if (amplifier + 1 < AilmentsConfig.value(AilmentsConfig.HEMORRHAGE_MINIMUM_STACKS)) return false;
|
||||
double threshold = AilmentsConfig.value(AilmentsConfig.HEMORRHAGE_THRESHOLD);
|
||||
double added = AilmentsConfig.value(AilmentsConfig.HEMORRHAGE_FILL_PER_TICK)
|
||||
* Math.max(0.0, arcanePotency);
|
||||
@@ -41,6 +46,10 @@ public final class HemorrhageTracker {
|
||||
}
|
||||
|
||||
public static double getProgress(LivingEntity target) {
|
||||
if (!AilmentApi.canBleed(target)) {
|
||||
clearIfPresent(target);
|
||||
return 0.0;
|
||||
}
|
||||
CompoundTag data = target.getPersistentData();
|
||||
return data.contains(PROGRESS) ? Math.max(0.0, data.getDouble(PROGRESS)) : 0.0;
|
||||
}
|
||||
@@ -55,6 +64,12 @@ public final class HemorrhageTracker {
|
||||
hideDisplay(target);
|
||||
}
|
||||
|
||||
/** Clears stored buildup and its HUD only when buildup actually exists. */
|
||||
public static void clearIfPresent(LivingEntity target) {
|
||||
if (!target.getPersistentData().contains(PROGRESS)) return;
|
||||
clear(target);
|
||||
}
|
||||
|
||||
/** Synchronizes the compact client HUD after login or a meter update. */
|
||||
public static void syncDisplay(LivingEntity target) {
|
||||
if (!(target instanceof ServerPlayer player)) return;
|
||||
|
||||
@@ -8,6 +8,8 @@ import net.minecraft.world.entity.EntityType;
|
||||
|
||||
public final class ModEntityTypeTags {
|
||||
public static final TagKey<EntityType<?>> BLEED_IMMUNE = tag("bleed_immune");
|
||||
public static final TagKey<EntityType<?>> SOULLESS = tag("soulless");
|
||||
public static final TagKey<EntityType<?>> STURDY = tag("sturdy");
|
||||
public static final TagKey<EntityType<?>> MENTAL_CONTROL_RESISTANT = tag("mental_control_resistant");
|
||||
public static final TagKey<EntityType<?>> MENTAL_CONTROL_IMMUNE = tag("mental_control_immune");
|
||||
private static TagKey<EntityType<?>> tag(String path) {
|
||||
|
||||
Reference in New Issue
Block a user