fix various issues throughout the entire project
This commit is contained in:
@@ -92,7 +92,7 @@ Item tags:
|
||||
Direct melee hits with items in these tags apply their corresponding ailment. Soul Rot, Bleed, and Fracture use
|
||||
their configured stack/duration behavior; the other tags use `taggedWeaponEffectDurationTicks`. Bleed also applies to non-vanilla item
|
||||
registry paths containing a configured katana-family keyword. Automatic application, proc chance, matching keywords,
|
||||
and the PvP proc cooldown are configurable under `integration` in `ascend-ailments-common.toml`. PvP cooldowns are
|
||||
and the PvP proc cooldown are configurable under `integration` in `ascend-ailments-server.toml`. PvP cooldowns are
|
||||
tracked per attacking player and ailment; their configured minimum is 60 ticks (3 seconds).
|
||||
|
||||
## Admin commands
|
||||
|
||||
@@ -25,8 +25,9 @@ public final class AscendAilments {
|
||||
ModEffects.register(modBus);
|
||||
AilmentNetwork.register();
|
||||
modBus.addListener(this::commonSetup);
|
||||
context.registerConfig(ModConfig.Type.COMMON, AilmentsConfig.SPEC,
|
||||
"ascend-ailments-common.toml");
|
||||
// Gameplay settings belong to the server and are synchronized to multiplayer clients by Forge.
|
||||
context.registerConfig(ModConfig.Type.SERVER, AilmentsConfig.SPEC,
|
||||
"ascend-ailments-server.toml");
|
||||
MinecraftForge.EVENT_BUS.addListener(this::registerCommands);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.fretux.ailments.api;
|
||||
|
||||
import net.fretux.ailments.config.AilmentsConfig;
|
||||
import net.fretux.ailments.compat.AscendCompat;
|
||||
import net.fretux.ailments.registry.ModEffects;
|
||||
import net.fretux.ailments.util.EffectSourceUtil;
|
||||
import net.fretux.ailments.util.MentalControlUtil;
|
||||
@@ -117,8 +118,7 @@ public final class AilmentApi {
|
||||
int base = AilmentsConfig.value(AilmentsConfig.SOUL_ROT_DURATION);
|
||||
if (extendWithStacks) base = safeAdd(base, amplifier * 20);
|
||||
int duration = AilmentScaling.scaleDuration(base, source);
|
||||
target.removeEffect(effect);
|
||||
boolean applied = target.addEffect(instance(effect, duration, amplifier, true));
|
||||
boolean applied = addStackingEffect(target, source, effect, duration, amplifier, true);
|
||||
if (applied) {
|
||||
EffectSourceUtil.setSource(target, EffectSourceUtil.SOUL_ROT, source);
|
||||
EffectSourceUtil.setPotency(target, EffectSourceUtil.SOUL_ROT,
|
||||
@@ -148,8 +148,7 @@ public final class AilmentApi {
|
||||
: amplifier == 1 ? AilmentsConfig.value(AilmentsConfig.BLEED_DURATION_1)
|
||||
: AilmentsConfig.value(AilmentsConfig.BLEED_DURATION_2);
|
||||
int duration = AilmentScaling.scaleDuration(base, source);
|
||||
target.removeEffect(effect);
|
||||
boolean applied = target.addEffect(instance(effect, duration, amplifier, true));
|
||||
boolean applied = addStackingEffect(target, source, effect, duration, amplifier, true);
|
||||
if (applied) {
|
||||
EffectSourceUtil.setSource(target, EffectSourceUtil.BLEED, source);
|
||||
EffectSourceUtil.setPotency(target, EffectSourceUtil.BLEED,
|
||||
@@ -191,8 +190,7 @@ public final class AilmentApi {
|
||||
int stackedAmplifier = current == null ? 0 : Math.min(2, current.getAmplifier() + 1);
|
||||
int appliedAmplifier = Math.min(2, Math.max(stackedAmplifier, amplifier));
|
||||
int duration = AilmentScaling.scaleDuration(requestedTicks, source);
|
||||
target.removeEffect(effect);
|
||||
boolean applied = target.addEffect(instance(effect, duration, appliedAmplifier, true));
|
||||
boolean applied = addStackingEffect(target, source, effect, duration, appliedAmplifier, true);
|
||||
if (applied) EffectSourceUtil.setSource(target, EffectSourceUtil.FRACTURE, source);
|
||||
return finishApplication(applied, target, source, AilmentType.FRACTURE);
|
||||
}
|
||||
@@ -233,12 +231,8 @@ public final class AilmentApi {
|
||||
public static boolean applyOvercharm(LivingEntity target, @Nullable LivingEntity source, int requestedTicks) {
|
||||
if (!serverValid(target) || !pvpProcReady(target, source, AilmentType.OVERCHARM)) return false;
|
||||
int duration = AilmentScaling.scaleDuration(requestedTicks, source);
|
||||
boolean result = target.addEffect(instance(ModEffects.OVERCHARM.get(), duration, 0, true));
|
||||
boolean result = addSourcedEffect(target, source, ModEffects.OVERCHARM.get(), duration, 0, true);
|
||||
if (result) {
|
||||
// Ascend itself scales beneficial effects received by players. Restore the source-scaled duration so
|
||||
// this API remains the single authority and cannot be scaled twice when both mods are installed.
|
||||
MobEffectInstance active = target.getEffect(ModEffects.OVERCHARM.get());
|
||||
if (active != null) active.mapDuration(ignored -> duration);
|
||||
EffectSourceUtil.setSource(target, EffectSourceUtil.OVERCHARM, source);
|
||||
}
|
||||
return finishApplication(result, target, source, AilmentType.OVERCHARM);
|
||||
@@ -247,7 +241,7 @@ public final class AilmentApi {
|
||||
public static boolean applyWinded(LivingEntity target, @Nullable LivingEntity source, int requestedTicks) {
|
||||
if (!serverValid(target) || !pvpProcReady(target, source, AilmentType.WINDED)) return false;
|
||||
int duration = AilmentScaling.scaleDuration(requestedTicks, source);
|
||||
return finishApplication(target.addEffect(instance(ModEffects.WINDED.get(), duration, 0, true)),
|
||||
return finishApplication(addSourcedEffect(target, source, ModEffects.WINDED.get(), duration, 0, true),
|
||||
target, source, AilmentType.WINDED);
|
||||
}
|
||||
|
||||
@@ -311,15 +305,14 @@ public final class AilmentApi {
|
||||
|
||||
private static boolean applySourced(LivingEntity target, @Nullable LivingEntity source, MobEffect effect,
|
||||
String key, int duration, int amplifier) {
|
||||
boolean applied = target.addEffect(instance(effect, Math.max(1, duration), amplifier, true));
|
||||
boolean applied = addSourcedEffect(target, source, effect, Math.max(1, duration), amplifier, true);
|
||||
if (applied) EffectSourceUtil.setSource(target, key, source);
|
||||
return applied;
|
||||
}
|
||||
private static boolean applyDot(LivingEntity target, @Nullable LivingEntity source, MobEffect effect,
|
||||
String key, int requestedTicks, int amplifier, AilmentType type) {
|
||||
int duration = AilmentScaling.scaleDuration(requestedTicks, source);
|
||||
target.removeEffect(effect);
|
||||
boolean applied = target.addEffect(instance(effect, duration, amplifier, true));
|
||||
boolean applied = addSourcedEffect(target, source, effect, duration, amplifier, true);
|
||||
if (applied) {
|
||||
EffectSourceUtil.setSource(target, key, source);
|
||||
EffectSourceUtil.setPotency(target, key, AilmentScaling.getPotencyMultiplier(source, type));
|
||||
@@ -329,6 +322,32 @@ public final class AilmentApi {
|
||||
private static MobEffectInstance instance(MobEffect effect, int duration, int amplifier, boolean icon) {
|
||||
return new MobEffectInstance(effect, duration, amplifier, false, false, 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);
|
||||
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.
|
||||
MobEffectInstance active = target.getEffect(effect);
|
||||
if (active != null && active.getAmplifier() == amplifier) active.mapDuration(ignored -> duration);
|
||||
}
|
||||
return applied;
|
||||
}
|
||||
private static boolean addStackingEffect(LivingEntity target, @Nullable LivingEntity source, MobEffect effect,
|
||||
int duration, int amplifier, boolean icon) {
|
||||
MobEffectInstance previous = target.getEffect(effect);
|
||||
if (previous == null || previous.getAmplifier() >= amplifier)
|
||||
return addSourcedEffect(target, source, effect, duration, amplifier, 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.
|
||||
int previousDuration = previous.getDuration();
|
||||
previous.mapDuration(ignored -> 0);
|
||||
boolean applied = addSourcedEffect(target, source, effect, duration, amplifier, icon);
|
||||
if (!applied && target.getEffect(effect) == previous)
|
||||
previous.mapDuration(ignored -> previousDuration);
|
||||
return applied;
|
||||
}
|
||||
private static boolean serverValid(LivingEntity target) {
|
||||
return target != null && !target.level().isClientSide && target.isAlive()
|
||||
&& (!(target instanceof Player player) || !player.isSpectator());
|
||||
@@ -347,7 +366,10 @@ public final class AilmentApi {
|
||||
return (int) Math.min(Integer.MAX_VALUE, (long) a + b);
|
||||
}
|
||||
private static boolean pvpProcReady(LivingEntity target, @Nullable LivingEntity source, AilmentType type) {
|
||||
if (!(source instanceof Player) || !(target instanceof Player) || source == target) return true;
|
||||
if (source != null && source.level() != target.level()) return false;
|
||||
if (!(source instanceof Player sourcePlayer) || !(target instanceof Player targetPlayer) || source == target)
|
||||
return true;
|
||||
if (!sourcePlayer.canHarmPlayer(targetPlayer)) return false;
|
||||
String key = pvpCooldownKey(type);
|
||||
long now = source.level().getGameTime();
|
||||
return !source.getPersistentData().contains(key)
|
||||
|
||||
@@ -25,7 +25,7 @@ public final class AscendCompat {
|
||||
try {
|
||||
resolve();
|
||||
return statsCapability != null && getAttributeLevel != null;
|
||||
} catch (ReflectiveOperationException | LinkageError ignored) {
|
||||
} catch (ReflectiveOperationException | LinkageError | RuntimeException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -39,20 +39,27 @@ public final class AscendCompat {
|
||||
Optional<?> stats = lazy.resolve();
|
||||
if (stats.isEmpty()) return 0;
|
||||
return Math.max(0, ((Number) getAttributeLevel.invoke(stats.get(), "arcane")).intValue());
|
||||
} catch (ReflectiveOperationException | LinkageError ignored) {
|
||||
} catch (ReflectiveOperationException | LinkageError | RuntimeException ignored) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static synchronized void resolve() throws ReflectiveOperationException {
|
||||
if (resolved) return;
|
||||
resolved = true;
|
||||
Class<?> provider = Class.forName(PROVIDER, false, AscendCompat.class.getClassLoader());
|
||||
Field capabilityField = provider.getField("PLAYER_STATS");
|
||||
statsCapability = (Capability<?>) capabilityField.get(null);
|
||||
Capability<?> resolvedCapability = (Capability<?>) capabilityField.get(null);
|
||||
Class<?> statsClass = Class.forName("net.fretux.ascend.player.PlayerStats", false,
|
||||
AscendCompat.class.getClassLoader());
|
||||
getAttributeLevel = statsClass.getMethod("getAttributeLevel", String.class);
|
||||
Method resolvedGetter = statsClass.getMethod("getAttributeLevel", String.class);
|
||||
Class<?> returnType = resolvedGetter.getReturnType();
|
||||
boolean numericPrimitive = returnType == byte.class || returnType == short.class || returnType == int.class
|
||||
|| returnType == long.class || returnType == float.class || returnType == double.class;
|
||||
if (!Number.class.isAssignableFrom(returnType) && !numericPrimitive)
|
||||
throw new NoSuchMethodException("getAttributeLevel(String) does not return a number");
|
||||
statsCapability = resolvedCapability;
|
||||
getAttributeLevel = resolvedGetter;
|
||||
resolved = true;
|
||||
}
|
||||
private AscendCompat() {}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,23 @@ import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.damagesource.DamageType;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class ModDamageSources {
|
||||
public static final ResourceKey<DamageType> BLEED = ResourceKey.create(Registries.DAMAGE_TYPE,
|
||||
ResourceLocation.fromNamespaceAndPath(AscendAilments.MOD_ID, "bleed"));
|
||||
|
||||
public static DamageSource bleed(Level level) {
|
||||
return new DamageSource(level.registryAccess().registryOrThrow(Registries.DAMAGE_TYPE)
|
||||
.getHolderOrThrow(BLEED));
|
||||
return bleed(level, null);
|
||||
}
|
||||
|
||||
public static DamageSource bleed(Level level, @Nullable Entity source) {
|
||||
var type = level.registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolderOrThrow(BLEED);
|
||||
// Periodic Bleed has an owning attacker for attribution, but no direct entity (it is not another melee hit).
|
||||
return source == null ? new DamageSource(type) : new DamageSource(type, null, source);
|
||||
}
|
||||
private ModDamageSources() {}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ public final class BleedEffect extends MobEffect {
|
||||
: amplifier == 1 ? AilmentsConfig.value(AilmentsConfig.BLEED_DAMAGE_1)
|
||||
: AilmentsConfig.value(AilmentsConfig.BLEED_DAMAGE_2);
|
||||
float potency = EffectSourceUtil.getPotency(entity, EffectSourceUtil.BLEED);
|
||||
boolean damaged = entity.hurt(ModDamageSources.bleed(entity.level()), (float) (base * potency));
|
||||
LivingEntity source = EffectSourceUtil.getSourceInLevel(entity, EffectSourceUtil.BLEED);
|
||||
boolean damaged = entity.hurt(ModDamageSources.bleed(entity.level(), source), (float) (base * potency));
|
||||
if (damaged) HemorrhageTracker.recordBleedDamage(entity, amplifier, potency);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.fretux.ailments.event;
|
||||
import net.fretux.ailments.AscendAilments;
|
||||
import net.fretux.ailments.api.AilmentApi;
|
||||
import net.fretux.ailments.config.AilmentsConfig;
|
||||
import net.fretux.ailments.damage.ModDamageSources;
|
||||
import net.fretux.ailments.registry.ModEffects;
|
||||
import net.fretux.ailments.util.ControlEffectHelper;
|
||||
import net.fretux.ailments.util.EffectSourceUtil;
|
||||
@@ -20,10 +21,12 @@ import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingChangeTargetEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingDeathEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingDamageEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingHealEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingHurtEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.eventbus.api.EventPriority;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
@@ -54,12 +57,14 @@ public final class AilmentEvents {
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onHeal(LivingHealEvent event) {
|
||||
if (!event.isCanceled() && event.getEntity().hasEffect(ModEffects.SOUL_ROT.get()))
|
||||
if (!event.getEntity().level().isClientSide && !event.isCanceled()
|
||||
&& event.getEntity().hasEffect(ModEffects.SOUL_ROT.get()))
|
||||
event.setAmount((float) (event.getAmount() * AilmentsConfig.value(AilmentsConfig.SOUL_ROT_HEALING)));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onAttack(LivingAttackEvent event) {
|
||||
if (event.getEntity().level().isClientSide) return;
|
||||
Entity attacker = event.getSource().getEntity();
|
||||
if (attacker instanceof LivingEntity living && !(living instanceof Player)
|
||||
&& living.hasEffect(ModEffects.CHARM.get())
|
||||
@@ -68,6 +73,7 @@ public final class AilmentEvents {
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onChangeTarget(LivingChangeTargetEvent event) {
|
||||
if (event.getEntity().level().isClientSide) return;
|
||||
if (!(event.getEntity() instanceof Mob mob)) return;
|
||||
LivingEntity taunter = mob.hasEffect(ModEffects.TAUNT.get())
|
||||
&& !MentalControlUtil.isMentalControlResistant(mob)
|
||||
@@ -82,11 +88,12 @@ public final class AilmentEvents {
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onHurt(LivingHurtEvent event) {
|
||||
if (event.isCanceled()) return;
|
||||
if (event.getEntity().level().isClientSide || event.isCanceled()) return;
|
||||
LivingEntity victim = event.getEntity();
|
||||
DamageSource damageSource = event.getSource();
|
||||
Entity causing = damageSource.getEntity();
|
||||
LivingEntity attacker = causing instanceof LivingEntity living ? living : null;
|
||||
LivingEntity attacker = !damageSource.is(ModDamageSources.BLEED) && causing instanceof LivingEntity living
|
||||
? living : null;
|
||||
float amount = event.getAmount();
|
||||
|
||||
// 1. Victim vulnerability.
|
||||
@@ -119,14 +126,25 @@ public final class AilmentEvents {
|
||||
boolean directPlayerMelee = attacker instanceof Player && damageSource.getDirectEntity() == attacker;
|
||||
if (alreadyCharmedByAttacker && directPlayerMelee)
|
||||
amount *= (float) AilmentsConfig.value(AilmentsConfig.OVERCHARM_DAMAGE_BONUS);
|
||||
}
|
||||
event.setAmount(amount);
|
||||
}
|
||||
|
||||
/** Applies hit-triggered ailments only after armor, absorption, and earlier cancellation hooks are resolved. */
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||
public static void onDamage(LivingDamageEvent event) {
|
||||
if (event.getEntity().level().isClientSide || event.isCanceled() || event.getAmount() <= 0) return;
|
||||
LivingEntity victim = event.getEntity();
|
||||
DamageSource damageSource = event.getSource();
|
||||
if (damageSource.is(ModDamageSources.BLEED)) return;
|
||||
Entity causing = damageSource.getEntity();
|
||||
if (!(causing instanceof LivingEntity attacker) || attacker == victim) return;
|
||||
|
||||
if (attacker.hasEffect(ModEffects.OVERCHARM.get()))
|
||||
AilmentApi.applyCharm(victim, attacker,
|
||||
AilmentsConfig.value(AilmentsConfig.OVERCHARM_CHARM_DURATION));
|
||||
}
|
||||
|
||||
if (attacker != null && attacker != victim && amount > 0
|
||||
&& damageSource.getDirectEntity() == attacker)
|
||||
if (damageSource.getDirectEntity() == attacker)
|
||||
applyTaggedWeaponAilments(attacker.getMainHandItem(), victim, attacker);
|
||||
event.setAmount(amount);
|
||||
}
|
||||
|
||||
private static void applyTaggedWeaponAilments(ItemStack stack, LivingEntity victim, LivingEntity attacker) {
|
||||
|
||||
@@ -6,17 +6,23 @@ import net.fretux.ailments.registry.ModEffects;
|
||||
import net.minecraft.commands.arguments.EntityAnchorArgument;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.food.FoodData;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public final class ControlEffectHelper {
|
||||
private static final String FEAR_DISTANCE = "ascend_ailments.fear.last_distance_sqr";
|
||||
private static final String SOUL_FOOD = "ascend_ailments.soul_rot.last_food";
|
||||
private static final String SOUL_SATURATION = "ascend_ailments.soul_rot.last_saturation";
|
||||
private static final UUID FEAR_MOVEMENT_UUID = UUID.fromString("8158dc2a-8de9-49a2-b086-bd99ade47661");
|
||||
private static final UUID FEAR_ATTACK_UUID = UUID.fromString("4468e408-78e8-49f6-a7bc-9ae9028a0f48");
|
||||
|
||||
public static void handleLivingTick(LivingEntity entity) {
|
||||
if (entity.level().isClientSide) return;
|
||||
@@ -26,6 +32,7 @@ public final class ControlEffectHelper {
|
||||
else {
|
||||
EffectSourceUtil.clear(entity, EffectSourceUtil.FEAR);
|
||||
entity.getPersistentData().remove(FEAR_DISTANCE);
|
||||
clearFearPlayerPenalties(entity);
|
||||
}
|
||||
if (entity.hasEffect(ModEffects.TAUNT.get())) tickTaunt(entity);
|
||||
else EffectSourceUtil.clear(entity, EffectSourceUtil.TAUNT);
|
||||
@@ -36,14 +43,20 @@ public final class ControlEffectHelper {
|
||||
}
|
||||
|
||||
private static void tickFear(LivingEntity entity) {
|
||||
LivingEntity source = validControlSource(entity, EffectSourceUtil.FEAR);
|
||||
if (source == null) {
|
||||
entity.removeEffect(ModEffects.FEAR.get());
|
||||
EffectSourceUtil.clear(entity, EffectSourceUtil.FEAR);
|
||||
if (EffectSourceUtil.getSourceUuid(entity, EffectSourceUtil.FEAR) == null) {
|
||||
removeControlEffect(entity, ModEffects.FEAR.get(), EffectSourceUtil.FEAR);
|
||||
entity.getPersistentData().remove(FEAR_DISTANCE);
|
||||
return;
|
||||
}
|
||||
LivingEntity source = validControlSource(entity, EffectSourceUtil.FEAR);
|
||||
if (source == null) {
|
||||
if (entity instanceof Mob mob) mob.getNavigation().stop();
|
||||
entity.getPersistentData().remove(FEAR_DISTANCE);
|
||||
clearFearPlayerPenalties(entity);
|
||||
return;
|
||||
}
|
||||
if (MentalControlUtil.isMentalControlResistant(entity)) {
|
||||
clearFearPlayerPenalties(entity);
|
||||
MobEffectInstance winded = entity.getEffect(ModEffects.WINDED.get());
|
||||
if (winded == null || winded.getDuration() < 10)
|
||||
entity.addEffect(new MobEffectInstance(ModEffects.WINDED.get(), 25, 0, false, false, false));
|
||||
@@ -86,17 +99,17 @@ public final class ControlEffectHelper {
|
||||
}
|
||||
}
|
||||
data.putDouble(FEAR_DISTANCE, distance);
|
||||
if (distance <= 36.0) {
|
||||
player.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 20, 3, false, false, false));
|
||||
player.addEffect(new MobEffectInstance(MobEffects.WEAKNESS, 20, 0, false, false, false));
|
||||
}
|
||||
setFearPlayerPenalties(player, distance <= 36.0);
|
||||
}
|
||||
|
||||
private static void tickTaunt(LivingEntity entity) {
|
||||
if (EffectSourceUtil.getSourceUuid(entity, EffectSourceUtil.TAUNT) == null) {
|
||||
removeControlEffect(entity, ModEffects.TAUNT.get(), EffectSourceUtil.TAUNT);
|
||||
return;
|
||||
}
|
||||
LivingEntity source = validControlSource(entity, EffectSourceUtil.TAUNT);
|
||||
if (source == null) {
|
||||
entity.removeEffect(ModEffects.TAUNT.get());
|
||||
EffectSourceUtil.clear(entity, EffectSourceUtil.TAUNT);
|
||||
if (entity instanceof Mob mob && mob.getTarget() != null) mob.setTarget(null);
|
||||
return;
|
||||
}
|
||||
if (MentalControlUtil.isMentalControlResistant(entity)) return;
|
||||
@@ -106,10 +119,8 @@ public final class ControlEffectHelper {
|
||||
}
|
||||
|
||||
private static void validateCharm(LivingEntity entity) {
|
||||
if (validControlSource(entity, EffectSourceUtil.CHARM) == null) {
|
||||
entity.removeEffect(ModEffects.CHARM.get());
|
||||
EffectSourceUtil.clear(entity, EffectSourceUtil.CHARM);
|
||||
}
|
||||
if (EffectSourceUtil.getSourceUuid(entity, EffectSourceUtil.CHARM) == null)
|
||||
removeControlEffect(entity, ModEffects.CHARM.get(), EffectSourceUtil.CHARM);
|
||||
}
|
||||
|
||||
public static boolean isOwnCharmSource(LivingEntity charmed, LivingEntity possibleSource) {
|
||||
@@ -122,8 +133,37 @@ public final class ControlEffectHelper {
|
||||
}
|
||||
|
||||
private static LivingEntity validControlSource(LivingEntity target, String key) {
|
||||
LivingEntity source = EffectSourceUtil.getSource(target, key);
|
||||
return source != null && source.isAlive() && source.level() == target.level() ? source : null;
|
||||
LivingEntity source = EffectSourceUtil.getSourceInLevel(target, key);
|
||||
return source != null && source.isAlive() ? source : null;
|
||||
}
|
||||
|
||||
private static void setFearPlayerPenalties(Player player, boolean active) {
|
||||
setTransientModifier(player.getAttribute(Attributes.MOVEMENT_SPEED), FEAR_MOVEMENT_UUID,
|
||||
"Ascend Ailments fear movement", -0.60, AttributeModifier.Operation.MULTIPLY_TOTAL, active);
|
||||
setTransientModifier(player.getAttribute(Attributes.ATTACK_DAMAGE), FEAR_ATTACK_UUID,
|
||||
"Ascend Ailments fear weakness", -4.0, AttributeModifier.Operation.ADDITION, active);
|
||||
}
|
||||
|
||||
private static void setTransientModifier(AttributeInstance attribute, UUID id, String name, double amount,
|
||||
AttributeModifier.Operation operation, boolean active) {
|
||||
if (attribute == null) return;
|
||||
AttributeModifier existing = attribute.getModifier(id);
|
||||
if (!active) {
|
||||
if (existing != null) attribute.removeModifier(id);
|
||||
} else if (existing == null) {
|
||||
attribute.addTransientModifier(new AttributeModifier(id, name, amount, operation));
|
||||
}
|
||||
}
|
||||
|
||||
private static void clearFearPlayerPenalties(LivingEntity entity) {
|
||||
if (entity instanceof Player player) setFearPlayerPenalties(player, false);
|
||||
}
|
||||
|
||||
private static void removeControlEffect(LivingEntity entity, net.minecraft.world.effect.MobEffect effect,
|
||||
String key) {
|
||||
entity.removeEffect(effect);
|
||||
EffectSourceUtil.clear(entity, key);
|
||||
if (key.equals(EffectSourceUtil.FEAR)) clearFearPlayerPenalties(entity);
|
||||
}
|
||||
|
||||
private static void handleSoulRotFood(LivingEntity entity) {
|
||||
|
||||
@@ -34,13 +34,26 @@ public final class EffectSourceUtil {
|
||||
UUID id = getSourceUuid(target, ailment);
|
||||
if (id == null || !(target.level() instanceof ServerLevel level)) return null;
|
||||
MinecraftServer server = level.getServer();
|
||||
Entity player = server.getPlayerList().getPlayer(id);
|
||||
if (player instanceof LivingEntity living) return living;
|
||||
Entity local = level.getEntity(id);
|
||||
if (local instanceof LivingEntity living) return living;
|
||||
for (ServerLevel candidate : server.getAllLevels()) {
|
||||
if (candidate == level) continue;
|
||||
Entity entity = candidate.getEntity(id);
|
||||
if (entity instanceof LivingEntity living) return living;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Resolves a loaded source only in the target's current level without scanning every server dimension. */
|
||||
@Nullable public static LivingEntity getSourceInLevel(LivingEntity target, String ailment) {
|
||||
UUID id = getSourceUuid(target, ailment);
|
||||
if (id == null || !(target.level() instanceof ServerLevel level)) return null;
|
||||
Entity entity = level.getEntity(id);
|
||||
return entity instanceof LivingEntity living ? living : null;
|
||||
}
|
||||
|
||||
public static void setPotency(LivingEntity target, String ailment, double potency) {
|
||||
target.getPersistentData().putFloat(potencyKey(ailment), (float) Math.max(0, potency));
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ public final class HemorrhageTracker {
|
||||
if (target.isAlive()) {
|
||||
float damage = (float) (target.getMaxHealth()
|
||||
* AilmentsConfig.value(AilmentsConfig.HEMORRHAGE_MAX_HEALTH_DAMAGE));
|
||||
if (damage > 0) target.hurt(ModDamageSources.bleed(target.level()), damage);
|
||||
LivingEntity source = EffectSourceUtil.getSourceInLevel(target, EffectSourceUtil.BLEED);
|
||||
if (damage > 0) target.hurt(ModDamageSources.bleed(target.level(), source), damage);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user