diff --git a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge_Damage.java b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge_Damage.java index 3053fac4c92..cb083e764ea 100644 --- a/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge_Damage.java +++ b/forge/src/mixins/java/org/spongepowered/forge/mixin/core/world/entity/LivingEntityMixin_Forge_Damage.java @@ -59,8 +59,8 @@ public abstract class LivingEntityMixin_Forge_Damage implements TrackedDamageBri return ForgeEventFactory.onShieldBlock(self, source, originalDamage); } - final SpongeDamageStep step = tracker.newStep(DamageStepTypes.SHIELD, originalDamage, ItemStackUtil.snapshotOf(self.getUseItem())); - float damage = (float) step.applyModifiersBefore(); + final SpongeDamageStep step = tracker.newStep(DamageStepTypes.SHIELD, ItemStackUtil.snapshotOf(self.getUseItem())); + float damage = (float) step.applyChildrenBefore(originalDamage); final ShieldBlockEvent event; if (step.isSkipped()) { event = new ShieldBlockEvent(self, source, damage); @@ -71,7 +71,7 @@ public abstract class LivingEntityMixin_Forge_Damage implements TrackedDamageBri damage -= event.getBlockedDamage(); } } - step.applyModifiersAfter(damage); + step.applyChildrenAfter(damage); return event; } diff --git a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/LivingEntityMixin_Neo_Damage.java b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/LivingEntityMixin_Neo_Damage.java index 34bdc2a842d..603730e6801 100644 --- a/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/LivingEntityMixin_Neo_Damage.java +++ b/neoforge/src/mixins/java/org/spongepowered/neoforge/mixin/core/world/entity/LivingEntityMixin_Neo_Damage.java @@ -66,8 +66,8 @@ public abstract class LivingEntityMixin_Neo_Damage implements TrackedDamageBridg } final float originalDamage = container.getNewDamage(); - final SpongeDamageStep step = tracker.newStep(DamageStepTypes.SHIELD, originalDamage, ItemStackUtil.snapshotOf(self.getUseItem())); - float damage = (float) step.applyModifiersBefore(); + final SpongeDamageStep step = tracker.newStep(DamageStepTypes.SHIELD, ItemStackUtil.snapshotOf(self.getUseItem())); + float damage = (float) step.applyChildrenBefore(originalDamage); container.setNewDamage(damage); final LivingShieldBlockEvent event; if (step.isSkipped()) { @@ -78,7 +78,7 @@ public abstract class LivingEntityMixin_Neo_Damage implements TrackedDamageBridg container.setBlockedDamage(event); damage = container.getNewDamage(); } - step.applyModifiersAfter(damage); + step.applyChildrenAfter(damage); return event; } diff --git a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeAttackTracker.java b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeAttackTracker.java index 6fc811873cb..820fbcd3a13 100644 --- a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeAttackTracker.java +++ b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeAttackTracker.java @@ -30,7 +30,7 @@ import org.spongepowered.api.entity.Entity; import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.SpongeEventFactory; -import org.spongepowered.api.event.cause.entity.damage.DamageStep; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; import org.spongepowered.api.event.entity.AttackEntityEvent; import org.spongepowered.api.event.entity.DamageCalculationEvent; import org.spongepowered.api.item.inventory.ItemStackSnapshot; @@ -39,8 +39,6 @@ import org.spongepowered.common.event.tracking.PhaseTracker; import org.spongepowered.common.item.util.ItemStackUtil; -import java.util.List; - public class SpongeAttackTracker extends SpongeDamageTracker { private final ItemStack weapon; private final ItemStackSnapshot weaponSnapshot; @@ -48,8 +46,8 @@ public class SpongeAttackTracker extends SpongeDamageTracker { private float attackStrength; private boolean strongSprint = false; - public SpongeAttackTracker(final DamageCalculationEvent.Pre preEvent, final ItemStack weapon) { - super(preEvent); + public SpongeAttackTracker(final DamageCalculationEvent.Pre preEvent, final DamageSource source, final ItemStack weapon) { + super(preEvent, source); this.weapon = weapon; this.weaponSnapshot = ItemStackUtil.snapshotOf(weapon); } @@ -88,14 +86,18 @@ public void setStrongSprint(final boolean strongSprint) { this.strongSprint = strongSprint; } - public boolean callAttackPostEvent(final Entity entity, final DamageSource source, final float finalDamage, final float knockbackModifier) { - final List steps = this.preparePostEvent(); + public boolean callAttackPostEvent(final Entity entity, final DamageSource source, float finalDamage, final float knockbackModifier) { + if (this.postEvent != null) { + throw new IllegalStateException("Post event already fired"); + } + + finalDamage = (float) this.newStep(DamageStepTypes.END).apply(finalDamage); - try (final CauseStackManager.StackFrame frame = PhaseTracker.getCauseStackManager().pushCauseFrame()) { + try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { SpongeDamageTracker.generateCauseFor(source, frame); final AttackEntityEvent.Post event = SpongeEventFactory.createAttackEntityEventPost(frame.currentCause(), - this.preEvent.originalBaseDamage(), this.preEvent.baseDamage(), finalDamage, finalDamage, knockbackModifier, knockbackModifier, entity, steps); + knockbackModifier, knockbackModifier, entity, this, this.preEvent.baseDamage(), finalDamage); this.postEvent = event; return SpongeCommon.post(event); @@ -103,16 +105,20 @@ public boolean callAttackPostEvent(final Entity entity, final DamageSource sourc } public static @Nullable SpongeAttackTracker callAttackPreEvent(final Entity entity, final DamageSource source, final float baseDamage, final ItemStack weapon) { - try (final CauseStackManager.StackFrame frame = PhaseTracker.getCauseStackManager().pushCauseFrame()) { + final SpongeAttackTracker tracker; + try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { SpongeDamageTracker.generateCauseFor(source, frame); - final AttackEntityEvent.Pre event = SpongeEventFactory.createAttackEntityEventPre(frame.currentCause(), baseDamage, baseDamage, entity); + final AttackEntityEvent.Pre event = SpongeEventFactory.createAttackEntityEventPre(frame.currentCause(), entity, baseDamage); if (SpongeCommon.post(event)) { return null; } - return new SpongeAttackTracker(event, weapon); + tracker = new SpongeAttackTracker(event, source, weapon); } + + tracker.newStep(DamageStepTypes.START).apply(baseDamage); + return tracker; } public static @Nullable SpongeAttackTracker of(final DamageSource source) { diff --git a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageModifier.java b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageModifier.java new file mode 100644 index 00000000000..9458aa7c365 --- /dev/null +++ b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageModifier.java @@ -0,0 +1,81 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.event.cause.entity.damage; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.event.CauseStackManager; +import org.spongepowered.api.event.cause.entity.damage.DamageModifier; +import org.spongepowered.api.event.cause.entity.damage.DamageStepType; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; + +public record SpongeDamageModifier(DamageStepType type, Optional> frame, Optional damage) implements DamageModifier { + + public static class Builder implements DamageModifier.Builder { + private @Nullable DamageStepType type; + private @Nullable Consumer frame; + private Function function; + + public Builder() { + this.reset(); + } + + @Override + public DamageModifier.Builder reset() { + this.type = null; + this.frame = null; + this.function = (step, damage) -> damage; + return this; + } + + @Override + public DamageModifier.Builder type(DamageStepType type) { + this.type = Objects.requireNonNull(type, "type"); + return this; + } + + @Override + public DamageModifier.Builder frame(Consumer frame) { + this.frame = Objects.requireNonNull(frame, "frame"); + return this; + } + + @Override + public DamageModifier.Builder damage(Function function) { + this.function = Objects.requireNonNull(function, "function"); + return this; + } + + @Override + public DamageModifier build() { + if (this.type == null) { + throw new IllegalStateException("type must be set"); + } + return new SpongeDamageModifier(this.type, Optional.ofNullable(this.frame), Optional.ofNullable(this.function)); + } + } +} diff --git a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageStep.java b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageStep.java index a5a912b4d30..4394ca58d05 100644 --- a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageStep.java +++ b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageStep.java @@ -27,37 +27,112 @@ import com.google.common.collect.ImmutableList; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.event.Cause; +import org.spongepowered.api.event.CauseStackManager; import org.spongepowered.api.event.cause.entity.damage.DamageModifier; import org.spongepowered.api.event.cause.entity.damage.DamageStep; +import org.spongepowered.api.event.cause.entity.damage.DamageStepHistory; import org.spongepowered.api.event.cause.entity.damage.DamageStepType; +import org.spongepowered.common.event.tracking.PhaseTracker; +import java.util.HashSet; import java.util.List; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.Set; import java.util.StringJoiner; +import java.util.function.Consumer; public final class SpongeDamageStep implements DamageStep { private static final Logger LOGGER = LogManager.getLogger(); + private final SpongeDamageTracker tracker; private final DamageStepType type; - private final Cause cause; - private final List modifiersBefore; - private final List modifiersAfter; + private final @Nullable SpongeDamageStep parent; + private final @Nullable Consumer frameModifier; + private final DamageModifier.@Nullable Function damageFunction; - private State state = State.BEFORE; + private Cause cause; private boolean skipped; - private final double damageBeforeModifiers; - private double damageBeforeStep; - private double damageAfterStep; - private double damageAfterModifiers; + private OptionalDouble damageBeforeChildren = OptionalDouble.empty(); + private OptionalDouble damageBeforeSelf = OptionalDouble.empty(); + private OptionalDouble damageAfterSelf = OptionalDouble.empty(); + private OptionalDouble damageAfterChildren = OptionalDouble.empty(); - public SpongeDamageStep(final DamageStepType type, final double initialDamage, final Cause cause, - final List modifiersBefore, final List modifiersAfter) { + private List childrenBefore; + private List childrenAfter; + + public SpongeDamageStep(final SpongeDamageTracker tracker, final DamageStepType type, final Object[] causes) { + this.tracker = tracker; + this.parent = null; this.type = type; - this.cause = cause; - this.damageBeforeModifiers = initialDamage; - this.modifiersBefore = ImmutableList.copyOf(modifiersBefore); - this.modifiersAfter = ImmutableList.copyOf(modifiersAfter); + this.frameModifier = (frame) -> { + SpongeDamageTracker.generateCauseFor(tracker.source, frame); + for (Object cause : causes) { + frame.pushCause(cause); + } + }; + this.damageFunction = null; + } + + public SpongeDamageStep(final SpongeDamageStep parent, final DamageModifier modifier) { + this.tracker = parent.tracker; + this.parent = parent; + this.type = modifier.type(); + this.frameModifier = modifier.frame().orElse(null); + this.damageFunction = modifier.damage().orElse(null); + } + + void populateChildren() { + this.populateChildren(new HashSet<>()); + } + + private void populateChildren(final Set parentTypes) { + parentTypes.add(this.type); + + final ImmutableList.Builder before = ImmutableList.builder(); + for (final DamageModifier modifier : this.tracker.preEvent().modifiersBefore(this.type)) { + if (parentTypes.contains(modifier.type())) { + LOGGER.warn("Modifier {} is supposed to be a child before step {} but this would cause a cycle so it will be ignored.", modifier, this); + } else { + before.add(new SpongeDamageStep(this, modifier)); + } + } + this.childrenBefore = before.build(); + + final ImmutableList.Builder after = ImmutableList.builder(); + for (final DamageModifier modifier : this.tracker.preEvent().modifiersAfter(this.type)) { + if (parentTypes.contains(modifier.type())) { + LOGGER.warn("Modifier {} is supposed to be a child after step {} but this would cause a cycle so it will be ignored.", modifier, this); + } else { + after.add(new SpongeDamageStep(this, modifier)); + } + } + this.childrenAfter = after.build(); + + final PhaseTracker phaseTracker = PhaseTracker.getInstance(); + try (final CauseStackManager.StackFrame frame = this.frameModifier == null ? null : phaseTracker.pushCauseFrame()) { + if (frame != null) { + try { + this.frameModifier.accept(frame); + } catch (final Throwable t) { + LOGGER.error("Failed to apply frame modifier of step {}", this, t); + } + } + + this.cause = phaseTracker.currentCause(); + + for (final SpongeDamageStep child : this.childrenBefore) { + child.populateChildren(parentTypes); + } + for (final SpongeDamageStep child : this.childrenAfter) { + child.populateChildren(parentTypes); + } + } + + parentTypes.remove(this.type); } @Override @@ -77,123 +152,110 @@ public boolean isSkipped() { @Override public void setSkipped(boolean skipped) { - if (this.state != State.BEFORE) { + if (this.damageAfterSelf.isPresent()) { throw new IllegalStateException("Step can only be skipped before occurring"); } this.skipped = skipped; } @Override - public double damageBeforeModifiers() { - return this.damageBeforeModifiers; + public OptionalDouble damageBeforeChildren() { + return this.damageBeforeChildren; } @Override - public double damageBeforeStep() { - if (this.state == State.BEFORE) { - throw new IllegalStateException("Before modifiers haven't finished"); - } - return this.damageBeforeStep; + public OptionalDouble damageBeforeSelf() { + return this.damageBeforeSelf; } @Override - public double damageAfterStep() { - if (this.state == State.BEFORE) { - throw new IllegalStateException("Step hasn't started"); - } - if (this.state == State.STEP) { - throw new IllegalStateException("Step hasn't finished"); - } - return this.damageAfterStep; + public OptionalDouble damageAfterSelf() { + return this.damageAfterSelf; } @Override - public double damageAfterModifiers() { - if (this.state != State.END) { - throw new IllegalStateException("Modifiers haven't finished"); - } - return this.damageAfterModifiers; + public OptionalDouble damageAfterChildren() { + return this.damageAfterChildren; } @Override - public List modifiersBefore() { - return this.modifiersBefore; + public DamageStepHistory history() { + return this.tracker; } @Override - public List modifiersAfter() { - return this.modifiersAfter; + public Optional parent() { + return Optional.ofNullable(this.parent); } - public State state() { - return this.state; + @Override + public List childrenBefore() { + return (List) this.childrenBefore; + } + + @Override + public List childrenAfter() { + return (List) this.childrenAfter; } @Override public String toString() { + // don't add the parent to avoid infinite recursion return new StringJoiner(", ", SpongeDamageStep.class.getSimpleName() + "[", "]") .add("type=" + this.type) .add("cause=" + this.cause) - .add("damageBeforeModifiers=" + this.damageBeforeModifiers) - .add("damageBeforeStep=" + this.damageBeforeStep) - .add("damageAfterStep=" + this.damageAfterStep) - .add("damageAfterModifiers=" + this.damageAfterModifiers) - .add("modifiersBefore=" + this.modifiersBefore) - .add("modifiersAfter=" + this.modifiersAfter) - .add("state=" + this.state) + .add("frameModifier=" + this.frameModifier) + .add("damageFunction=" + this.damageFunction) + .add("skipped=" + this.skipped) + .add("damageBeforeChildren=" + this.damageBeforeChildren) + .add("damageBeforeSelf=" + this.damageBeforeSelf) + .add("damageAfterSelf=" + this.damageAfterSelf) + .add("damageAfterChildren=" + this.damageAfterChildren) + .add("childrenBefore=" + this.childrenBefore) + .add("childrenAfter=" + this.childrenAfter) .toString(); } - public double applyModifiersBefore() { - if (this.state != State.BEFORE) { + public double applyChildrenBefore(double damage) { + if (this.damageBeforeChildren.isPresent()) { throw new IllegalStateException(); } - double damage = this.damageBeforeModifiers; - for (DamageModifier modifier : this.modifiersBefore) { - try { - damage = modifier.modify(this, damage); - } catch (Exception e) { - LOGGER.error("Failed to apply modifier {} before step {}", modifier, this, e); - } + this.damageBeforeChildren = OptionalDouble.of(damage); + for (final SpongeDamageStep child : this.childrenBefore) { + damage = child.apply(damage); } - - this.damageBeforeStep = damage; - this.state = State.STEP; - + this.damageBeforeSelf = OptionalDouble.of(damage); return damage; } - public double applyModifiersAfter(double damage) { - if (this.state != State.STEP) { + public double applyChildrenAfter(double damage) { + if (this.damageAfterSelf.isPresent() || this.damageBeforeSelf.isEmpty()) { throw new IllegalStateException(); } if (this.skipped) { - damage = this.damageBeforeStep; + damage = this.damageBeforeSelf.getAsDouble(); } - this.damageAfterStep = damage; - this.state = State.AFTER; + this.damageAfterSelf = OptionalDouble.of(damage); + for (final SpongeDamageStep child : this.childrenAfter) { + damage = child.apply(damage); + } + this.damageAfterChildren = OptionalDouble.of(damage); + return damage; + } - for (DamageModifier modifier : this.modifiersBefore) { + public double apply(double damage) { + damage = this.applyChildrenBefore(damage); + if (!this.skipped && this.damageFunction != null) { try { - damage = modifier.modify(this, damage); - } catch (Exception e) { - LOGGER.error("Failed to apply modifier {} after step {}", modifier, this, e); + damage = this.damageFunction.modify(this, damage); + } catch (final Throwable t) { + LOGGER.error("Failed to apply damage function of step {}", this, t); } } - - this.damageAfterModifiers = damage; - this.state = State.END; - + damage = this.applyChildrenAfter(damage); return damage; } - - public enum State { - BEFORE, - STEP, - AFTER, - END - } } diff --git a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageTracker.java b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageTracker.java index 8a363caf2ce..769deaf458c 100644 --- a/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageTracker.java +++ b/src/main/java/org/spongepowered/common/event/cause/entity/damage/SpongeDamageTracker.java @@ -24,7 +24,6 @@ */ package org.spongepowered.common.event.cause.entity.damage; -import com.google.common.collect.ImmutableList; import net.minecraft.core.BlockPos; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.player.Player; @@ -32,13 +31,13 @@ import org.apache.logging.log4j.Logger; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.entity.Entity; -import org.spongepowered.api.event.Cause; import org.spongepowered.api.event.CauseStackManager; -import org.spongepowered.api.event.EventContext; import org.spongepowered.api.event.EventContextKeys; import org.spongepowered.api.event.SpongeEventFactory; import org.spongepowered.api.event.cause.entity.damage.DamageStep; +import org.spongepowered.api.event.cause.entity.damage.DamageStepHistory; import org.spongepowered.api.event.cause.entity.damage.DamageStepType; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; import org.spongepowered.api.event.entity.DamageCalculationEvent; import org.spongepowered.api.event.entity.DamageEntityEvent; import org.spongepowered.api.registry.DefaultedRegistryReference; @@ -51,17 +50,25 @@ import org.spongepowered.common.util.VecHelper; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -public class SpongeDamageTracker { +public class SpongeDamageTracker implements DamageStepHistory { private static final Logger LOGGER = LogManager.getLogger(); - protected final List steps = new ArrayList<>(); + private final List rootSteps = new ArrayList<>(); protected final DamageCalculationEvent.Pre preEvent; + protected final DamageSource source; protected DamageCalculationEvent.Post postEvent; - public SpongeDamageTracker(final DamageCalculationEvent.Pre preEvent) { + public SpongeDamageTracker(final DamageCalculationEvent.Pre preEvent, final DamageSource source) { this.preEvent = preEvent; + this.source = source; + } + + @Override + public List rootSteps() { + return Collections.unmodifiableList(this.rootSteps); } public DamageCalculationEvent.Pre preEvent() { @@ -75,40 +82,41 @@ public DamageCalculationEvent.Post postEvent() { return this.postEvent; } - public SpongeDamageStep newStep(final DefaultedRegistryReference typeRef, final float damage, final Object... causes) { + public SpongeDamageStep newStep(final DefaultedRegistryReference typeRef, final Object... causes) { final DamageStepType type = typeRef.get(); - final SpongeDamageStep step = new SpongeDamageStep(type, damage, Cause.of(EventContext.empty(), List.of(causes)), this.preEvent.modifiersBefore(type), this.preEvent.modifiersAfter(type)); + final SpongeDamageStep step = new SpongeDamageStep(this, type, causes); + step.populateChildren(); if (this.postEvent != null) { - LOGGER.warn("A new step {} is being captured after the post event.", step); + LOGGER.warn("A new root step {} is being captured after the post event.", step); } - if (!this.steps.isEmpty()) { - final SpongeDamageStep previous = this.steps.getLast(); - if (previous.state() != SpongeDamageStep.State.END) { - LOGGER.warn("A new step {} is being captured but previous step {} hasn't finished.", step, previous); - this.steps.removeLast(); + if (!this.rootSteps.isEmpty()) { + final SpongeDamageStep previous = this.rootSteps.getLast(); + if (previous.damageAfterChildren().isEmpty()) { + LOGGER.warn("A new root step {} is being captured but previous root step {} hasn't finished.", step, previous); + this.rootSteps.removeLast(); } } - this.steps.add(step); + this.rootSteps.add(step); return step; } public float startStep(final DefaultedRegistryReference typeRef, final float damage, final Object... causes) { - return (float) this.newStep(typeRef, damage, causes).applyModifiersBefore(); + return (float) this.newStep(typeRef, causes).applyChildrenBefore(damage); } public @Nullable SpongeDamageStep currentStep(final DefaultedRegistryReference typeRef) { - if (this.steps.isEmpty()) { - LOGGER.warn("Expected a current step of type {} but no step has been captured yet.", typeRef.location()); + if (this.rootSteps.isEmpty()) { + LOGGER.warn("Expected a current root step of type {} but no step has been captured yet.", typeRef.location()); return null; } final DamageStepType type = typeRef.get(); - final SpongeDamageStep step = this.steps.getLast(); + final SpongeDamageStep step = this.rootSteps.getLast(); if (step.type() != type) { - LOGGER.warn("Expected a current step of type {} but got {}.", type, step); + LOGGER.warn("Expected a current root step of type {} but got {}.", type, step); return null; } return step; @@ -116,7 +124,7 @@ public float startStep(final DefaultedRegistryReference typeRef, public float endStep(final DefaultedRegistryReference typeRef, final float damage) { final SpongeDamageStep step = this.currentStep(typeRef); - return step == null ? damage : (float) step.applyModifiersAfter(damage); + return step == null ? damage : (float) step.applyChildrenAfter(damage); } public boolean isSkipped(final DefaultedRegistryReference typeRef) { @@ -126,8 +134,8 @@ public boolean isSkipped(final DefaultedRegistryReference typeRe public @Nullable SpongeDamageStep lastStep(final DefaultedRegistryReference typeRef) { final DamageStepType type = typeRef.get(); - for (int i = this.steps.size() - 1; i >= 0; i--) { - final SpongeDamageStep step = this.steps.get(i); + for (int i = this.rootSteps.size() - 1; i >= 0; i--) { + final SpongeDamageStep step = this.rootSteps.get(i); if (step.type() == type) { return step; } @@ -137,33 +145,21 @@ public boolean isSkipped(final DefaultedRegistryReference typeRe public float damageAfter(final DefaultedRegistryReference typeRef) { final SpongeDamageStep step = this.lastStep(typeRef); - return step == null ? 0 : (float) step.damageAfterModifiers(); + return step == null ? 0 : (float) step.damageAfterChildren().orElse(0); } - protected List preparePostEvent() { + public float callDamagePostEvent(final Entity entity, float finalDamage) { if (this.postEvent != null) { throw new IllegalStateException("Post event already fired"); } - if (!this.steps.isEmpty()) { - final SpongeDamageStep last = this.steps.getLast(); - if (last.state() != SpongeDamageStep.State.END) { - LOGGER.warn("Calling post event but last step {} hasn't finished.", last); - return ImmutableList.copyOf(this.steps.subList(0, this.steps.size() - 1)); - } - } + finalDamage = (float) this.newStep(DamageStepTypes.END).apply(finalDamage); - return ImmutableList.copyOf(this.steps); - } - - public float callDamagePostEvent(final Entity entity, final float finalDamage) { - final List steps = this.preparePostEvent(); - - try (final CauseStackManager.StackFrame frame = PhaseTracker.getCauseStackManager().pushCauseFrame()) { + try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { SpongeDamageTracker.generateCauseFor((DamageSource) this.preEvent.source(), frame); final DamageEntityEvent.Post event = SpongeEventFactory.createDamageEntityEventPost(frame.currentCause(), - this.preEvent.originalBaseDamage(), this.preEvent.baseDamage(), finalDamage, finalDamage, entity, steps); + entity, this, this.preEvent.baseDamage(), finalDamage); this.postEvent = event; if (SpongeCommon.post(event)) { @@ -190,34 +186,30 @@ protected static void generateCauseFor(final DamageSource source, final CauseSta } public static @Nullable SpongeDamageTracker callDamagePreEvent(final Entity entity, final DamageSource source, final float baseDamage) { - try (final CauseStackManager.StackFrame frame = PhaseTracker.getCauseStackManager().pushCauseFrame()) { + final SpongeDamageTracker tracker; + try (final CauseStackManager.StackFrame frame = PhaseTracker.getInstance().pushCauseFrame()) { SpongeDamageTracker.generateCauseFor(source, frame); - final DamageEntityEvent.Pre event = SpongeEventFactory.createDamageEntityEventPre(frame.currentCause(), baseDamage, baseDamage, entity); + final DamageEntityEvent.Pre event = SpongeEventFactory.createDamageEntityEventPre(frame.currentCause(), entity, baseDamage); if (SpongeCommon.post(event)) { return null; } - return new SpongeDamageTracker(event); + tracker = new SpongeDamageTracker(event, source); } + + tracker.newStep(DamageStepTypes.START).apply(baseDamage); + return tracker; } public static DamageEntityEvent.@Nullable Post callDamageEvents(final Entity entity, final DamageSource source, final float baseDamage) { - try (final CauseStackManager.StackFrame frame = PhaseTracker.getCauseStackManager().pushCauseFrame()) { - SpongeDamageTracker.generateCauseFor(source, frame); - - final DamageEntityEvent.Pre preEvent = SpongeEventFactory.createDamageEntityEventPre(frame.currentCause(), baseDamage, baseDamage, entity); - if (SpongeCommon.post(preEvent)) { - return null; - } - - final DamageEntityEvent.Post postEvent = SpongeEventFactory.createDamageEntityEventPost(frame.currentCause(), - preEvent.originalBaseDamage(), preEvent.baseDamage(), preEvent.baseDamage(), preEvent.baseDamage(), entity, List.of()); - if (SpongeCommon.post(postEvent)) { - return null; - } - - return postEvent; + final @Nullable SpongeDamageTracker tracker = SpongeDamageTracker.callDamagePreEvent(entity, source, baseDamage); + if (tracker == null) { + return null; } + final DamageStep step = tracker.currentStep(DamageStepTypes.START); + final float finalDamage = step == null ? baseDamage : (float) step.damageAfterChildren().orElse(baseDamage); + tracker.callDamagePostEvent(entity, finalDamage); + return (DamageEntityEvent.Post) tracker.postEvent; } } diff --git a/src/main/java/org/spongepowered/common/registry/SpongeBuilderProvider.java b/src/main/java/org/spongepowered/common/registry/SpongeBuilderProvider.java index e6e08443838..47c5bc1bf98 100644 --- a/src/main/java/org/spongepowered/common/registry/SpongeBuilderProvider.java +++ b/src/main/java/org/spongepowered/common/registry/SpongeBuilderProvider.java @@ -68,6 +68,7 @@ import org.spongepowered.api.entity.attribute.AttributeModifier; import org.spongepowered.api.entity.living.player.tab.TabListEntry; import org.spongepowered.api.event.EventContextKey; +import org.spongepowered.api.event.cause.entity.damage.DamageModifier; import org.spongepowered.api.event.cause.entity.damage.DamageType; import org.spongepowered.api.event.cause.entity.damage.source.DamageSource; import org.spongepowered.api.fluid.FluidStack; @@ -178,6 +179,7 @@ import org.spongepowered.common.entity.attribute.SpongeAttributeModifierBuilder; import org.spongepowered.common.entity.player.tab.TabListEntryBuilder; import org.spongepowered.common.event.SpongeEventContextKeyBuilder; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageModifier; import org.spongepowered.common.event.cause.entity.damage.SpongeDamageSourceBuilder; import org.spongepowered.common.event.cause.entity.damage.SpongeDamageTypeBuilder; import org.spongepowered.common.fluid.SpongeFluidStackBuilder; @@ -290,6 +292,7 @@ public void registerDefaultBuilders() { .register(Team.Builder.class, SpongeTeamBuilder::new) .register(Scoreboard.Builder.class, SpongeScoreboardBuilder::new) .register(DamageSource.Builder.class, SpongeDamageSourceBuilder::new) + .register(DamageModifier.Builder.class, SpongeDamageModifier.Builder::new) .register(Explosion.Builder.class, SpongeExplosionBuilder::new) .register(BlockState.Builder.class, SpongeBlockStateBuilder::new) .register(BlockSnapshot.Builder.class, SpongeBlockSnapshot.BuilderImpl::unpooled) diff --git a/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java b/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java index 8891c2041ad..201ebe38af7 100644 --- a/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java +++ b/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java @@ -46,6 +46,7 @@ import org.spongepowered.api.effect.ForwardingViewer; import org.spongepowered.api.effect.VanishState; import org.spongepowered.api.event.EventListenerRegistration; +import org.spongepowered.api.event.cause.entity.damage.DamageStepType; import org.spongepowered.api.event.cause.entity.damage.source.DamageSource; import org.spongepowered.api.item.inventory.ItemStack; import org.spongepowered.api.item.inventory.ItemStackComparators; @@ -123,6 +124,7 @@ import org.spongepowered.common.effect.SpongeCustomForwardingViewer; import org.spongepowered.common.entity.effect.SpongeVanishState; import org.spongepowered.common.event.SpongeEventListenerRegistration; +import org.spongepowered.common.event.cause.entity.damage.SpongeDamageStepType; import org.spongepowered.common.event.tracking.BlockChangeFlagManager; import org.spongepowered.common.item.SpongeItemStack; import org.spongepowered.common.item.SpongeItemStackSnapshot; @@ -244,6 +246,7 @@ public void registerDefaultFactories() { .registerFactory(RegistryReference.Factory.class, new SpongeRegistryReference.FactoryImpl()) .registerFactory(BlockVolumeFactory.class, new SpongeBlockVolumeFactory()) .registerFactory(DamageSource.Factory.class, new SpongeDamageSourceFactory()) + .registerFactory(DamageStepType.Factory.class, SpongeDamageStepType::new) .registerFactory(PaletteReference.Factory.class, new SpongePaletteReferenceFactory()) .registerFactory(EntityArchetypeEntry.Factory.class, new SpongeEntityArchetypeEntryFactory()) .registerFactory(ServerLocationCreator.Factory.class, new SpongeServerLocationCreatorFactory()) diff --git a/src/main/java/org/spongepowered/common/registry/SpongeRegistries.java b/src/main/java/org/spongepowered/common/registry/SpongeRegistries.java index c044c343400..29ba343d72a 100644 --- a/src/main/java/org/spongepowered/common/registry/SpongeRegistries.java +++ b/src/main/java/org/spongepowered/common/registry/SpongeRegistries.java @@ -63,7 +63,6 @@ public static void registerEarlyGlobalRegistries(final SpongeRegistryHolder hold holder.createFrozenRegistry(RegistryTypes.BODY_PART, SpongeRegistryLoader.bodyPart()); holder.createFrozenRegistry(RegistryTypes.CLICK_TYPE, SpongeRegistryLoader.clickType()); holder.createFrozenRegistry(RegistryTypes.CHUNK_REGENERATE_FLAG, SpongeRegistryLoader.chunkRegenerateFlag()); - holder.createFrozenRegistry(RegistryTypes.DAMAGE_STEP_TYPE, SpongeRegistryLoader.damageStepType()); holder.createFrozenRegistry(RegistryTypes.DISMOUNT_TYPE, SpongeRegistryLoader.dismountType()); holder.createFrozenRegistry(RegistryTypes.GOAL_EXECUTOR_TYPE, SpongeRegistryLoader.goalExecutorType()); holder.createFrozenRegistry(RegistryTypes.GOAL_TYPE, SpongeRegistryLoader.goalType()); @@ -95,6 +94,7 @@ private static void registerEarlyDynamicRegistries(final SpongeRegistryHolder ho holder.createRegistry(RegistryTypes.COMMAND_REGISTRAR_TYPE, CommandRegistryLoader.commandRegistrarType(), true); holder.createRegistry(RegistryTypes.PLACEHOLDER_PARSER, DynamicSpongeRegistryLoader.placeholderParser(), true); holder.createRegistry(RegistryTypes.TELEPORT_HELPER_FILTER, DynamicSpongeRegistryLoader.teleportHelperFilter(), true); + holder.createRegistry(RegistryTypes.DAMAGE_STEP_TYPE, SpongeRegistryLoader.damageStepType(), true); } @SuppressWarnings({"unchecked", "rawtypes"}) diff --git a/src/main/java/org/spongepowered/common/registry/loader/SpongeRegistryLoader.java b/src/main/java/org/spongepowered/common/registry/loader/SpongeRegistryLoader.java index 7753961256b..745e7529cef 100644 --- a/src/main/java/org/spongepowered/common/registry/loader/SpongeRegistryLoader.java +++ b/src/main/java/org/spongepowered/common/registry/loader/SpongeRegistryLoader.java @@ -228,12 +228,14 @@ public static RegistryLoader damageStepType() { DamageStepTypes.CRITICAL_HIT, DamageStepTypes.DEFENSIVE_POTION_EFFECT, DamageStepTypes.ENCHANTMENT_COOLDOWN, + DamageStepTypes.END, DamageStepTypes.FREEZING_BONUS, DamageStepTypes.HARD_HAT, DamageStepTypes.MAGIC, DamageStepTypes.NEGATIVE_POTION_EFFECT, DamageStepTypes.OFFENSIVE_POTION_EFFECT, DamageStepTypes.SHIELD, + DamageStepTypes.START, DamageStepTypes.SWEEPING, DamageStepTypes.WEAPON_BONUS, DamageStepTypes.WEAPON_ENCHANTMENT diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Damage.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Damage.java index 99f8bd35fd4..4956e0836a3 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Damage.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/LivingEntityMixin_Damage.java @@ -108,7 +108,7 @@ public abstract class LivingEntityMixin_Damage extends EntityMixin implements Li return damage; } final SpongeDamageStep step = tracker.currentStep(DamageStepTypes.SHIELD); - return step == null ? damage : (float) step.damageAfterModifiers(); + return step == null ? damage : (float) step.damageAfterChildren().getAsDouble(); } @ModifyVariable(method = "hurtServer", at = @At("LOAD"), argsOnly = true, slice = @Slice( @@ -195,12 +195,12 @@ public abstract class LivingEntityMixin_Damage extends EntityMixin implements Li return CombatRules.getDamageAfterMagicAbsorb(damage, protection); } - final SpongeDamageStep step = tracker.newStep(DamageStepTypes.ARMOR_ENCHANTMENT, damage, this); - damage = (float) step.applyModifiersBefore(); + final SpongeDamageStep step = tracker.newStep(DamageStepTypes.ARMOR_ENCHANTMENT, this); + damage = (float) step.applyChildrenBefore(damage); if (!step.isSkipped()) { damage = CombatRules.getDamageAfterMagicAbsorb(damage, protection); } - return (float) step.applyModifiersAfter(damage); + return (float) step.applyChildrenAfter(damage); } @Redirect(method = "hurtServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;playHurtSound(Lnet/minecraft/world/damagesource/DamageSource;)V")) diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Attack.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Attack.java index 1c4310f3cbd..adb9799fa76 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Attack.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/entity/player/PlayerMixin_Attack.java @@ -155,12 +155,12 @@ public abstract class PlayerMixin_Attack extends LivingEntityMixin_Damage implem return item.getAttackDamageBonus(target, originalDamage, source); } - final SpongeDamageStep step = tracker.newStep(DamageStepTypes.WEAPON_BONUS, originalDamage, tracker.weaponSnapshot()); - float damage = (float) step.applyModifiersBefore(); + final SpongeDamageStep step = tracker.newStep(DamageStepTypes.WEAPON_BONUS, tracker.weaponSnapshot()); + float damage = (float) step.applyChildrenBefore(originalDamage); if (!step.isSkipped()) { damage += item.getAttackDamageBonus(target, damage, source); } - return (float) step.applyModifiersAfter(damage) - originalDamage; + return (float) step.applyChildrenAfter(damage) - originalDamage; } @ModifyVariable(method = "attack", at = @At(value = "LOAD", ordinal = 0), ordinal = 0, slice = @Slice( @@ -249,21 +249,21 @@ public abstract class PlayerMixin_Attack extends LivingEntityMixin_Damage implem damage = (float) sweepTracker.preEvent().baseDamage(); // In vanilla, this step is outside the loop, but we move it to here so it can be modified per target - SpongeDamageStep step = sweepTracker.newStep(DamageStepTypes.SWEEPING, damage, sweepTracker.weaponSnapshot()); - damage = (float) step.applyModifiersBefore(); + SpongeDamageStep step = sweepTracker.newStep(DamageStepTypes.SWEEPING, sweepTracker.weaponSnapshot()); + damage = (float) step.applyChildrenBefore(damage); if (!step.isSkipped()) { damage = 1.0F + (float) this.shadow$getAttributeValue(Attributes.SWEEPING_DAMAGE_RATIO) * damage; } - damage = (float) step.applyModifiersAfter(damage); + damage = (float) step.applyChildrenAfter(damage); damage = this.shadow$getEnchantedDamage(sweepTarget, damage, source); - step = sweepTracker.newStep(DamageStepTypes.ENCHANTMENT_COOLDOWN, damage, sweepTracker.weaponSnapshot()); - damage = (float) step.applyModifiersBefore(); + step = sweepTracker.newStep(DamageStepTypes.ENCHANTMENT_COOLDOWN, sweepTracker.weaponSnapshot()); + damage = (float) step.applyChildrenBefore(damage); if (!step.isSkipped()) { damage *= mainTracker.attackStrength(); } - damage = (float) step.applyModifiersAfter(damage); + damage = (float) step.applyChildrenAfter(damage); if (sweepTracker.callAttackPostEvent((org.spongepowered.api.entity.Entity) sweepTarget, source, damage, 0.4F)) { this.attack$trackers.removeLast(); diff --git a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/enchantment/EnchantmentMixin_Attack.java b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/enchantment/EnchantmentMixin_Attack.java index ee4f6589349..dd01f14e2c1 100644 --- a/src/mixins/java/org/spongepowered/common/mixin/core/world/item/enchantment/EnchantmentMixin_Attack.java +++ b/src/mixins/java/org/spongepowered/common/mixin/core/world/item/enchantment/EnchantmentMixin_Attack.java @@ -62,11 +62,11 @@ public abstract class EnchantmentMixin_Attack { return; } - final SpongeDamageStep step = tracker.newStep(DamageStepTypes.WEAPON_ENCHANTMENT, damage.floatValue(), ItemStackUtil.snapshotOf(weapon), self); - damage.setValue((float) step.applyModifiersBefore()); + final SpongeDamageStep step = tracker.newStep(DamageStepTypes.WEAPON_ENCHANTMENT, ItemStackUtil.snapshotOf(weapon), self); + damage.setValue((float) step.applyChildrenBefore(damage.floatValue())); if (!step.isSkipped()) { this.shadow$modifyDamageFilteredValue(component, level, enchantmentLevel, weapon, target, source, damage); } - damage.setValue((float) step.applyModifiersAfter(damage.floatValue())); + damage.setValue((float) step.applyChildrenAfter(damage.floatValue())); } } diff --git a/testplugins/src/main/java/org/spongepowered/test/damage/DamageTest.java b/testplugins/src/main/java/org/spongepowered/test/damage/DamageTest.java index f08ac01bd8a..1659546f1e9 100644 --- a/testplugins/src/main/java/org/spongepowered/test/damage/DamageTest.java +++ b/testplugins/src/main/java/org/spongepowered/test/damage/DamageTest.java @@ -28,8 +28,12 @@ import com.google.inject.Inject; import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.JoinConfiguration; +import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; import org.spongepowered.api.command.Command; @@ -37,8 +41,11 @@ import org.spongepowered.api.command.parameter.CommandContext; import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.cause.entity.damage.DamageModifier; import org.spongepowered.api.event.cause.entity.damage.DamageScalings; import org.spongepowered.api.event.cause.entity.damage.DamageStep; +import org.spongepowered.api.event.cause.entity.damage.DamageStepType; +import org.spongepowered.api.event.cause.entity.damage.DamageStepTypes; import org.spongepowered.api.event.cause.entity.damage.DamageType; import org.spongepowered.api.event.cause.entity.damage.DamageTypes; import org.spongepowered.api.event.cause.entity.damage.source.DamageSource; @@ -59,12 +66,18 @@ import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; +import java.util.OptionalDouble; @Plugin("damagetest") public class DamageTest implements LoadableModule { private static final ResourceKey EXHAUSTING_DAMAGE = ResourceKey.of("damagetest", "test"); + private static final DamageStepType DOUBLE_CRITICAL = DamageStepType.create(); + private static final DamageStepType DOUBLE_DOUBLE_CRITICAL = DamageStepType.create(); + class Test { static final class Registry { @@ -123,13 +136,40 @@ private void onRegisterTagEvent(final RegisterTagEvent event) { event.tag(DamageTypeTags.BYPASSES_INVULNERABILITY).append(RegistryKey.of(RegistryTypes.DAMAGE_TYPE, EXHAUSTING_DAMAGE)); } + @Listener + public void onRegisterDamageStepType(final RegisterRegistryValueEvent.GameScoped event) { + event.registry(RegistryTypes.DAMAGE_STEP_TYPE, (r) -> { + r.register(ResourceKey.of(this.plugin, "double_critical"), DOUBLE_CRITICAL); + r.register(ResourceKey.of(this.plugin, "double_double_critical"), DOUBLE_DOUBLE_CRITICAL); + }); + } + + private static class CustomCause {} + private static class DamageListener { private static final DecimalFormat decimalFormat = new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.ROOT)); + private static String format(final OptionalDouble value) { + return DamageListener.format(value.orElse(Double.NaN)); + } + private static String format(final double value) { return DamageListener.decimalFormat.format(value); } + @Listener + private void onAttackPre(final AttackEntityEvent.Pre event) { + event.modifiersBefore(DamageStepTypes.CRITICAL_HIT) + .add(DamageModifier.builder().type(DOUBLE_CRITICAL) + .frame((frame) -> frame.pushCause(new CustomCause())) + .damage((step, damage) -> { + step.parent().get().skip(); + return damage * 2; + }).build()); + event.modifiersAfter(DOUBLE_CRITICAL) + .add(DamageModifier.builder().type(DOUBLE_DOUBLE_CRITICAL).damage((step, damage) -> damage * 2).build()); + } + @Listener private void onDamagePre(final DamageCalculationEvent.Pre event, @Root final DamageSource damageSource) { final Component eventName = event instanceof AttackEntityEvent ? @@ -138,7 +178,7 @@ private void onDamagePre(final DamageCalculationEvent.Pre event, @Root final Dam final Audience audience = Sponge.server(); audience.sendMessage(text().content("-------------").append(eventName, text(".Pre", NamedTextColor.YELLOW), text("---------------"))); audience.sendMessage(text().content(damageSource.type().key(RegistryTypes.DAMAGE_TYPE).value()) - .color(NamedTextColor.GOLD).append(text(" -> ", NamedTextColor.WHITE), event.entity().displayName().get())); + .color(NamedTextColor.GOLD).append(text(" → ", NamedTextColor.WHITE), event.entity().displayName().get())); audience.sendMessage(text("base damage: " + format(event.baseDamage()))); audience.sendMessage(text("-----------------------------------------------")); } @@ -151,14 +191,45 @@ private void onDamagePost(final DamageCalculationEvent.Post event, @Root final D final Audience audience = Sponge.server(); audience.sendMessage(text().content("-------------").append(eventName, text(".Post", NamedTextColor.GREEN), text("--------------"))); audience.sendMessage(text().content(damageSource.type().key(RegistryTypes.DAMAGE_TYPE).value()) - .color(NamedTextColor.GOLD).append(text(" -> ", NamedTextColor.WHITE), event.entity().displayName().get())); + .color(NamedTextColor.GOLD).append(text(" → ", NamedTextColor.WHITE), event.entity().displayName().get())); audience.sendMessage(text("base damage: " + format(event.baseDamage()))); audience.sendMessage(text("steps:")); - for (final DamageStep step : event.steps()) { - audience.sendMessage(text(" " + step.type().key(RegistryTypes.DAMAGE_STEP_TYPE).value() + ": " + format(step.damageBeforeStep()) + " -> " + format(step.damageAfterStep()))); + for (final DamageStep step : event.history().rootSteps()) { + audience.sendMessage(formatStep(step)); } - audience.sendMessage(text("final damage: " + format(event.originalFinalDamage()))); + audience.sendMessage(text("final damage: " + format(event.finalDamage()))); audience.sendMessage(text("-----------------------------------------------")); } + + private static Component formatStep(final DamageStep step) { + final List components = new ArrayList<>(); + components.add(Component.text(format(step.damageBeforeChildren()))); + formatStep(step, components); + return Component.join(JoinConfiguration.separator(Component.text(" → ", NamedTextColor.GRAY)), components); + } + + private static void formatStep(final DamageStep step, final List components) { + for (final DamageStep child : step.childrenBefore()) { + formatStep(child, components); + } + + final List causeClasses = new ArrayList<>(); + for (final Object obj : step.cause().all()) { + causeClasses.add(obj.getClass().getSimpleName()); + } + + components.add(Component.text() + .content(step.type().findKey(RegistryTypes.DAMAGE_STEP_TYPE).map(Key::value).orElse("?")) + .color(step.parent().isEmpty() ? NamedTextColor.AQUA : NamedTextColor.YELLOW) + .decoration(TextDecoration.STRIKETHROUGH, step.isSkipped()) + .hoverEvent(HoverEvent.showText(Component.text(String.join(" ", causeClasses)))) + .build()); + + components.add(Component.text(format(step.damageAfterSelf()))); + + for (final DamageStep child : step.childrenAfter()) { + formatStep(child, components); + } + } } } diff --git a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/LivingEntityMixin_Vanilla_Damage.java b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/LivingEntityMixin_Vanilla_Damage.java index 3e0a634bd9e..b91ee7554c7 100644 --- a/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/LivingEntityMixin_Vanilla_Damage.java +++ b/vanilla/src/mixins/java/org/spongepowered/vanilla/mixin/core/world/entity/LivingEntityMixin_Vanilla_Damage.java @@ -56,9 +56,9 @@ public abstract class LivingEntityMixin_Vanilla_Damage implements TrackedDamageB return true; } - final SpongeDamageStep step = tracker.newStep(DamageStepTypes.SHIELD, (float) tracker.preEvent().baseDamage(), ItemStackUtil.snapshotOf(self.getUseItem())); - step.applyModifiersBefore(); - step.applyModifiersAfter(0); + final SpongeDamageStep step = tracker.newStep(DamageStepTypes.SHIELD, ItemStackUtil.snapshotOf(self.getUseItem())); + step.applyChildrenBefore((float) tracker.preEvent().baseDamage()); + step.applyChildrenAfter(0); return !step.isSkipped(); } @@ -72,7 +72,7 @@ public abstract class LivingEntityMixin_Vanilla_Damage implements TrackedDamageB return damage; } final SpongeDamageStep step = tracker.currentStep(DamageStepTypes.SHIELD); - return step == null ? damage : (float) Math.max(step.damageBeforeStep(), 0); + return step == null ? damage : (float) Math.max(step.damageBeforeSelf().getAsDouble(), 0); } @ModifyVariable(method = "hurtServer", at = @At("STORE"), slice = @Slice( @@ -85,7 +85,7 @@ public abstract class LivingEntityMixin_Vanilla_Damage implements TrackedDamageB return blocked; } final SpongeDamageStep step = tracker.currentStep(DamageStepTypes.SHIELD); - return step == null ? blocked : step.damageAfterModifiers() <= 0; + return step == null ? blocked : step.damageAfterChildren().getAsDouble() <= 0; } @ModifyVariable(method = "actuallyHurt", at = @At("LOAD"), argsOnly = true, slice = @Slice(