diff --git a/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageFunction.java b/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageFunction.java deleted file mode 100644 index 6efb3adc728..00000000000 --- a/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageFunction.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * This file is part of SpongeAPI, 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.api.event.cause.entity.damage; - -import org.checkerframework.checker.nullness.qual.Nullable; - -import java.util.Objects; -import java.util.StringJoiner; -import java.util.function.DoubleUnaryOperator; - -public class DamageFunction implements ModifierFunction { - - public static final DoubleUnaryOperator ZERO_DAMAGE = value -> 0.0D; - - /** - * Constructs a new damage function. - * - * @param first The damage modifier to use - * @param second The unary operator to use - * @return The resulting damage function - */ - public static DamageFunction of(final DamageModifier first, final DoubleUnaryOperator second) { - return new DamageFunction(first, second); - } - - private final DamageModifier modifier; - private final DoubleUnaryOperator function; - - /** - * Creates a new {@link DamageFunction} with the provided - * {@link DamageModifier}. The caveat is that the provided - * {@link DamageFunction} is by default going to provide {@code 0} - * damage modifications. - * - * @param modifier The damage modifier - */ - public DamageFunction(final DamageModifier modifier) { - this(modifier, DamageFunction.ZERO_DAMAGE); - } - - /** - * Creates a new {@link DamageFunction} with the provided - * {@link DamageModifier} and {@link DoubleUnaryOperator}. - * - * @param modifier The modifier - * @param function The function - */ - public DamageFunction(final DamageModifier modifier, final DoubleUnaryOperator function) { - this.modifier = java.util.Objects.requireNonNull(modifier, "modifier"); - this.function = java.util.Objects.requireNonNull(function, "function"); - } - - /** - * Gets the {@link DamageModifier} for this function. - * - * @return The damage modifier - */ - @Override - public DamageModifier modifier() { - return this.modifier; - } - - /** - * Gets the {@link DoubleUnaryOperator} for this function. - * - * @return The damage function - */ - @Override - public DoubleUnaryOperator function() { - return this.function; - } - - @Override - public String toString() { - return new StringJoiner(",", DamageFunction.class.getSimpleName() + "[", "]") - .add("modifier=" + this.modifier()) - .add("function=" + this.function()) - .toString(); - } - - @Override - public boolean equals(final @Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || this.getClass() != o.getClass()) { - return false; - } - final DamageFunction that = (DamageFunction) o; - return Objects.equals(this.modifier, that.modifier) - && Objects.equals(this.function, that.function); - } - - @Override - public int hashCode() { - return Objects.hash(this.modifier, this.function); - } -} diff --git a/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageModifier.java b/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageModifier.java index 19079591c34..1e7a0284b49 100644 --- a/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageModifier.java +++ b/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageModifier.java @@ -24,271 +24,18 @@ */ package org.spongepowered.api.event.cause.entity.damage; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.entity.Entity; -import org.spongepowered.api.event.Cause; -import org.spongepowered.api.item.ItemTypes; -import org.spongepowered.api.item.enchantment.Enchantment; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.ItemStackLike; -import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.api.util.CopyableBuilder; - -import java.util.Objects; -import java.util.Optional; -import java.util.StringJoiner; -import java.util.function.DoubleUnaryOperator; -import java.util.function.Supplier; - /** - * Represents a modifier that will apply a function on a damage value to deal - * towards an entity such that the raw damage is the input of a - * {@link DoubleUnaryOperator} such that the output will be the final damage - * applied to the {@link Entity}. + * A damage modifier that will be applied before or after a {@link DamageStep}. */ +@FunctionalInterface public interface DamageModifier { /** - * Creates a new {@link Builder} for constructing a {@link DamageModifier}. - * - * @return A new builder - */ - static Builder builder() { - return new Builder(); - } - - /** - * Gets the {@link DamageModifierType} for this {@link DamageModifier}. - * - * @return The damage modifier type - */ - DamageModifierType type(); - - /** - * Returns the damage modifier group. - *

Grouped modifiers calculate their damage independently from each other

- * - * @return The damage modifier group - */ - String group(); - - /** - * Gets the cause of this {@link DamageModifier}. - * - * @return The cause of this damage modifier - */ - Cause cause(); - - /** - * Gets the contributing {@link ItemStackSnapshot} that provided the - * "reason" for this {@link DamageModifier} to exist. An example of a - * contributing {@link ItemStack} is if an {@link ItemTypes#DIAMOND_SWORD} - * provided an {@link Enchantment} that provided a - * {@link DamageModifierTypes#WEAPON_ENCHANTMENT}, this modifier would have - * the {@link ItemStackSnapshot} for the weapon used. Some modifiers however, - * do not require an {@link ItemStack} to be the contributing factor for - * this modifier to exist. + * Modifies the damage. * - * @return The contributing item, if available + * @param step The damage step this modifier is associated with. + * @param damage The current damage value. + * @return The next damage value */ - Optional contributingItem(); - - /** - * A builder that creates {@link DamageModifier}s, for use in both plugin and - * implementation requirements. - */ - final class Builder implements org.spongepowered.api.util.Builder, CopyableBuilder { - - @Nullable DamageModifierType type; - @Nullable Cause cause; - @Nullable String group; - @Nullable ItemStackSnapshot snapshot; - - Builder() { - } - - - /** - * Sets the {@link DamageModifierType} for the {@link DamageModifier} to - * build. - * - * @param damageModifierType The damage modifier type - * @return This builder, for chaining - */ - public Builder type(final Supplier damageModifierType) { - return this.type(damageModifierType.get()); - } - - /** - * Sets the {@link DamageModifierType} for the {@link DamageModifier} to - * build. - * - * @param damageModifierType The damage modifier type - * @return This builder, for chaining - */ - public Builder type(final DamageModifierType damageModifierType) { - this.type = java.util.Objects.requireNonNull(damageModifierType); - return this; - } - - /** - * The main attack damage calculated for an {@link org.spongepowered.api.event.entity.AttackEntityEvent} - * - * @return This builder, for chaining - */ - public Builder attackDamageGroup() { - return this.group("minecraft:attack_damage"); - } - - /** - * The enchantment attack damage calculated for an {@link org.spongepowered.api.event.entity.AttackEntityEvent} - * - * @return This builder, for chaining - */ - public Builder attackEnchantmentGroup() { - return this.group("minecraft:attack_enchantment"); - } - - /** - * The damage calculated for an {@link org.spongepowered.api.event.entity.DamageEntityEvent} - * - * @return This builder, for chaining - */ - public Builder damageReductionGroup() { - return this.group("minecraft:damage_reduction"); - } - - public Builder group(final String group) { - this.group = group; - return this; - } - - /** - * @deprecated Use {@link #item(ItemStackLike)} instead. - */ - @Deprecated(forRemoval = true) - public Builder item(final ItemStack itemStack) { - return this.item((ItemStackLike) itemStack); - } - - /** - * @deprecated Use {@link #item(ItemStackLike)} instead. - */ - @Deprecated(forRemoval = true) - public Builder item(final ItemStackSnapshot snapshot) { - return this.item((ItemStackLike) snapshot); - } - - public Builder item(final ItemStackLike item) { - this.snapshot = java.util.Objects.requireNonNull(item, "item").asImmutable(); - return this; - } - - /** - * Sets the {@link Cause} for the {@link DamageModifier} to build. - * - * @param cause The cause for the damage modifier - * @return This builder, for chaining - */ - public Builder cause(final Cause cause) { - this.cause = java.util.Objects.requireNonNull(cause); - return this; - } - - /** - * Creates a new {@link DamageModifier} with this builder's provided - * {@link Cause} and {@link DamageModifierType}. - * - * @return The newly created damage modifier - */ - @Override - public DamageModifier build() { - if (this.type == null) { - throw new IllegalStateException("The DamageModifierType must not be null!"); - } - if (this.cause == null) { - throw new IllegalStateException("The cause for the DamageModifier must not be null!"); - } - return new ImplementedDamageModifier(this); - } - - @Override - public Builder from(final DamageModifier value) { - this.type = value.type(); - this.cause = value.cause(); - this.snapshot = value.contributingItem().orElse(null); - return this; - } - - @Override - public Builder reset() { - this.type = null; - this.cause = null; - return this; - } - - - private static class ImplementedDamageModifier implements DamageModifier { - private final DamageModifierType type; - private final Cause cause; - @Nullable private final ItemStackSnapshot snapshot; - private final String group; - - ImplementedDamageModifier(final Builder builder) { - this.type = java.util.Objects.requireNonNull(builder.type, "DamageType is null!"); - this.cause = java.util.Objects.requireNonNull(builder.cause, "Cause is null!"); - this.group = java.util.Objects.requireNonNull(builder.group, "Group is null!"); - this.snapshot = builder.snapshot; - } - - @Override - public DamageModifierType type() { - return this.type; - } - - @Override - public Cause cause() { - return this.cause; - } - - @Override - public Optional contributingItem() { - return Optional.ofNullable(this.snapshot); - } - - @Override - public String group() { - return group; - } - - @Override - public int hashCode() { - return Objects.hash(this.type, this.cause); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null || this.getClass() != obj.getClass()) { - return false; - } - final ImplementedDamageModifier other = (ImplementedDamageModifier) obj; - return Objects.equals(this.type, other.type) - && Objects.equals(this.cause, other.cause) - && Objects.equals(this.snapshot, other.snapshot); - } - - @Override - public String toString() { - return new StringJoiner(", ", "DamageModifier[", "]") - .add("type=" + this.type) - .add("cause=" + this.cause) - .add("snapshot=" + this.snapshot) - .toString(); - } - } - - } + double modify(DamageStep step, double damage); } diff --git a/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageModifierTypes.java b/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageModifierTypes.java deleted file mode 100644 index 69fe4d3804f..00000000000 --- a/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageModifierTypes.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * This file is part of SpongeAPI, 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.api.event.cause.entity.damage; - -import org.spongepowered.api.ResourceKey; -import org.spongepowered.api.Sponge; -import org.spongepowered.api.effect.potion.PotionEffect; -import org.spongepowered.api.effect.potion.PotionEffectType; -import org.spongepowered.api.effect.potion.PotionEffectTypes; -import org.spongepowered.api.entity.Entity; -import org.spongepowered.api.entity.living.player.Player; -import org.spongepowered.api.event.cause.entity.damage.source.DamageSource; -import org.spongepowered.api.item.enchantment.Enchantment; -import org.spongepowered.api.item.enchantment.EnchantmentType; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.api.registry.DefaultedRegistryReference; -import org.spongepowered.api.registry.Registry; -import org.spongepowered.api.registry.RegistryKey; -import org.spongepowered.api.registry.RegistryScope; -import org.spongepowered.api.registry.RegistryScopes; -import org.spongepowered.api.registry.RegistryTypes; -import org.spongepowered.api.world.World; -import org.spongepowered.api.world.difficulty.Difficulty; - -@SuppressWarnings("unused") -@RegistryScopes(scopes = RegistryScope.GAME) -public final class DamageModifierTypes { - - // @formatter:off - - // SORTFIELDS:ON - - /** - * Represents a {@link DamageModifier} that "absorbs" damage based on - * the {@link PotionEffectTypes#ABSORPTION} level on the - * {@link Entity}. - */ - public static final DefaultedRegistryReference ABSORPTION = DamageModifierTypes.key(ResourceKey.sponge("absorption")); - - /** - * Represents a {@link DamageModifier} that will reduce damage based on - * the armor {@link ItemStack}s. - */ - public static final DefaultedRegistryReference ARMOR = DamageModifierTypes.key(ResourceKey.sponge("armor")); - - /** - * Represents a {@link DamageModifier} that will reduce damage based on - * the {@link EnchantmentType}s applicable to an {@link ItemStack} that is - * considered to be "armor" currently equipped on the owner. - * - *

Usually, within the {@link DamageModifier#cause ()} will reside - * an {@link ItemStackSnapshot} and an {@link Enchantment} signifying - * that the {@link EnchantmentType} of the {@link ItemStack} is modifying the - * incoming/outgoing damage. There can be multiple {@link DamageModifier}s - * of this type in a single event due to the variety of possibilities in - * customization of armor handling.

- */ - public static final DefaultedRegistryReference ARMOR_ENCHANTMENT = DamageModifierTypes.key(ResourceKey.sponge("armor_enchantment")); - - /** - * Represents the {@link DamageModifier} that will reduce damage from a - * {@link Player} if their attack cooldown has not been completed yet. - */ - public static final DefaultedRegistryReference ATTACK_COOLDOWN = DamageModifierTypes.key(ResourceKey.sponge("attack_cooldown")); - - /** - * Represents a {@link DamageModifier} that will modify damage from the attacks strength. - *

For vanilla this only reduces damage when repeating attacks too quickly

- */ - public static final DefaultedRegistryReference ATTACK_STRENGTH = DamageModifierTypes.key(ResourceKey.sponge("attack_strength")); - - /** - * Represents the {@link DamageModifier} that will modify damage output - * based on the fact that the attacking source is critically hitting the - * target. - */ - public static final DefaultedRegistryReference CRITICAL_HIT = DamageModifierTypes.key(ResourceKey.sponge("critical_hit")); - - /** - * Represents a {@link DamageModifier} that will reduce damage based on - * the {@link PotionEffectTypes#RESISTANCE} or any other - * {@link PotionEffectType} that can be deemed as reducing incoming damage. - * - *

Usually, within the {@link DamageModifier#cause ()} will reside - * a {@link PotionEffect} including the amplifier and duration, signifying - * that the {@link PotionEffectType} is modifying the incoming damage.

- */ - public static final DefaultedRegistryReference DEFENSIVE_POTION_EFFECT = DamageModifierTypes.key(ResourceKey.sponge("defensive_potion_effect")); - - /** - * Represents a {@link DamageModifier} that enhances damage based on the - * current {@link Difficulty} of the {@link World}. - */ - public static final DefaultedRegistryReference DIFFICULTY = DamageModifierTypes.key(ResourceKey.sponge("difficulty")); - - /** - * Represents a {@link DamageModifier} that will modify freezing damage. - * E.g. {@link org.spongepowered.api.entity.living.monster.Blaze} take more damage from freezing sources. - */ - public static final DefaultedRegistryReference FREEZING_BONUS = DamageModifierTypes.key(ResourceKey.sponge("freezing_bonus")); - - - /** - * Represents the {@link DamageModifier} that will modify damage from - * a {@link DamageSource#source()} that is a {@link org.spongepowered.api.entity.FallingBlock}. - * - *

Usually, within the {@link DamageModifier#cause ()} will reside - * an {@link ItemStackSnapshot} and an {@link Enchantment} signifying - * that the {@link EnchantmentType} of the {@link ItemStack} is modifying the - * incoming/outgoing damage.

- */ - public static final DefaultedRegistryReference HARD_HAT = DamageModifierTypes.key(ResourceKey.sponge("hard_hat")); - - /** - * Represents a {@link DamageModifier} that will modify damage based on - * magic. - */ - public static final DefaultedRegistryReference MAGIC = DamageModifierTypes.key(ResourceKey.sponge("magic")); - - /** - * Represents a {@link DamageModifier} that will reduce outgoing damage - * based on a {@link PotionEffect}. - * - *

Usually, within the {@link DamageModifier#cause ()} will reside - * a {@link PotionEffect} including the amplifier and duration, signifying - * that the {@link PotionEffectType} is reducing the outgoing damage.

- */ - public static final DefaultedRegistryReference NEGATIVE_POTION_EFFECT = DamageModifierTypes.key(ResourceKey.sponge("negative_potion_effect")); - - /** - * Represents the {@link DamageModifier} that will increase damage from - * a {@link PotionEffect} affecting the attacker. - */ - public static final DefaultedRegistryReference OFFENSIVE_POTION_EFFECT = DamageModifierTypes.key(ResourceKey.sponge("offensive_potion_effect")); - - /** - * Represents a {@link DamageModifier} that will reduce damage due to using a shield. - */ - public static final DefaultedRegistryReference SHIELD = DamageModifierTypes.key(ResourceKey.sponge("shield")); - - /** - * Represents a {@link DamageModifier} that is applied for a sweeping attack. - */ - public static final DefaultedRegistryReference SWEEPING = DamageModifierTypes.key(ResourceKey.sponge("sweeping")); - - /** - * Represents a {@link DamageModifier} that will add bonus damage based on the weapon used. - */ - public static final DefaultedRegistryReference WEAPON_BONUS = DamageModifierTypes.key(ResourceKey.sponge("weapon_bonus")); - - /** - * Represents the {@link DamageModifier} that will modify damage from - * an {@link EnchantmentType} on an equipped {@link ItemStack}. - * - *

Usually, within the {@link DamageModifier#cause ()} will reside - * an {@link ItemStackSnapshot} and an {@link Enchantment} signifying - * that the {@link EnchantmentType} of the {@link ItemStack} is modifying the - * incoming/outgoing damage.

- */ - public static final DefaultedRegistryReference WEAPON_ENCHANTMENT = DamageModifierTypes.key(ResourceKey.sponge("weapon_enchantment")); - - // SORTFIELDS:OFF - - // @formatter:on - - private DamageModifierTypes() { - } - - public static Registry registry() { - return Sponge.game().registry(RegistryTypes.DAMAGE_MODIFIER_TYPE); - } - - private static DefaultedRegistryReference key(final ResourceKey location) { - return RegistryKey.of(RegistryTypes.DAMAGE_MODIFIER_TYPE, location).asDefaultedReference(Sponge::game); - } -} diff --git a/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageStep.java b/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageStep.java new file mode 100644 index 00000000000..bdb84430473 --- /dev/null +++ b/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageStep.java @@ -0,0 +1,121 @@ +/* + * This file is part of SpongeAPI, 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.api.event.cause.entity.damage; + +import org.spongepowered.api.event.Cause; + +import java.util.List; + +/** + * Represents a step in the damage calculation. + */ +public interface DamageStep { + + /** + * Gets the {@link DamageStepType} for this {@link DamageStep}. + * + * @return The damage step type + */ + DamageStepType type(); + + /** + * Gets the cause of this {@link DamageStep}. + * + * @return The cause of this step + */ + Cause cause(); + + /** + * Gets whether this step is skipped. + * When skipped, only the step and its side effects are ignored, modifiers are still applied. + * A modifier willing to ignore every previous modifiers should revert the damage to {@link #damageBeforeModifiers()}. + * + * @return Whether this step is skipped + */ + boolean isSkipped(); + + /** + * Sets whether this step is skipped. + * + * @see #isSkipped() + * @throws IllegalStateException if called after the step has finished. + */ + void setSkipped(boolean skipped); + + /** + * Skips this step. + * + * @see #isSkipped() + * @throws IllegalStateException if called after the step has finished. + */ + default void skip() { + this.setSkipped(true); + } + + /** + * The damage just before the modifiers of this step. + * + * @return The damage before this step + */ + double damageBeforeModifiers(); + + /** + * The damage just before this step. + * + * @return The damage before this step + * @throws IllegalStateException if called before the "before" modifiers have finished. + */ + double damageBeforeStep(); + + /** + * The damage just after this step. + * + * @return The damage after this step + * @throws IllegalStateException if called before this step has finished + */ + double damageAfterStep(); + + /** + * The damage just after the modifiers of this step. + * + * @return The damage after this step + * @throws IllegalStateException if called before the modifiers have finished. + */ + double damageAfterModifiers(); + + /** + * Gets an immutable list of all modifiers that applies just before this step. + * + * @return The list of modifiers + */ + List modifiersBefore(); + + /** + * Gets an immutable list of all modifiers that applies just after this step. + * + * @return The list of modifiers + */ + List modifiersAfter(); +} diff --git a/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageModifierType.java b/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageStepType.java similarity index 84% rename from src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageModifierType.java rename to src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageStepType.java index e27d91de2bc..32288996331 100644 --- a/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageModifierType.java +++ b/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageStepType.java @@ -29,13 +29,13 @@ import org.spongepowered.api.util.annotation.CatalogedBy; /** - * A type of {@link DamageModifier} that can apply a "grouping" so to speak + * A type of {@link DamageStep} that can apply a "grouping" so to speak * for the damage modifier. The use case is being able to differentiate between - * various {@link DamageModifier}s based on the {@link DamageModifierType} + * various {@link DamageStep}s based on the {@link DamageStepType} * without digging through the {@link Cause} provided by - * {@link DamageModifier#cause()}. + * {@link DamageStep#cause()}. */ -@CatalogedBy(DamageModifierTypes.class) -public interface DamageModifierType extends DefaultedRegistryValue { +@CatalogedBy(DamageStepTypes.class) +public interface DamageStepType extends DefaultedRegistryValue { } diff --git a/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageStepTypes.java b/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageStepTypes.java new file mode 100644 index 00000000000..c65dd4573cc --- /dev/null +++ b/src/main/java/org/spongepowered/api/event/cause/entity/damage/DamageStepTypes.java @@ -0,0 +1,190 @@ +/* + * This file is part of SpongeAPI, 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.api.event.cause.entity.damage; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.effect.potion.PotionEffect; +import org.spongepowered.api.effect.potion.PotionEffectType; +import org.spongepowered.api.effect.potion.PotionEffectTypes; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.event.cause.entity.damage.source.DamageSource; +import org.spongepowered.api.item.enchantment.Enchantment; +import org.spongepowered.api.item.enchantment.EnchantmentType; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.ItemStackSnapshot; +import org.spongepowered.api.registry.DefaultedRegistryReference; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.registry.RegistryKey; +import org.spongepowered.api.registry.RegistryScope; +import org.spongepowered.api.registry.RegistryScopes; +import org.spongepowered.api.registry.RegistryTypes; + +@SuppressWarnings("unused") +@RegistryScopes(scopes = RegistryScope.GAME) +public final class DamageStepTypes { + + // @formatter:off + + // SORTFIELDS:ON + + /** + * Represents a {@link DamageStep} that "absorbs" damage based on + * the {@link PotionEffectTypes#ABSORPTION} level on the + * {@link Entity}. + */ + public static final DefaultedRegistryReference ABSORPTION = DamageStepTypes.key(ResourceKey.sponge("absorption")); + + /** + * Represents a {@link DamageStep} that will reduce damage based on + * the armor {@link ItemStack}s. + */ + public static final DefaultedRegistryReference ARMOR = DamageStepTypes.key(ResourceKey.sponge("armor")); + + /** + * Represents a {@link DamageStep} that will reduce damage based on + * the {@link EnchantmentType}s applicable to an {@link ItemStack} that is + * considered to be "armor" currently equipped on the owner. + * + *

Usually, within the {@link DamageStep#cause()} will reside + * an {@link ItemStackSnapshot} and an {@link Enchantment} signifying + * that the {@link EnchantmentType} of the {@link ItemStack} is modifying the + * incoming/outgoing damage. There can be multiple {@link DamageStep}s + * of this type in a single event due to the variety of possibilities in + * customization of armor handling.

+ */ + public static final DefaultedRegistryReference ARMOR_ENCHANTMENT = DamageStepTypes.key(ResourceKey.sponge("armor_enchantment")); + + /** + * Represents the {@link DamageStep} that will reduce the base damage from a + * {@link Player} if their attack cooldown has not been completed yet. + */ + public static final DefaultedRegistryReference BASE_COOLDOWN = DamageStepTypes.key(ResourceKey.sponge("base_cooldown")); + + /** + * Represents the {@link DamageStep} that will modify damage output + * based on the fact that the attacking source is critically hitting the + * target. + */ + public static final DefaultedRegistryReference CRITICAL_HIT = DamageStepTypes.key(ResourceKey.sponge("critical_hit")); + + /** + * Represents a {@link DamageStep} that will reduce damage based on + * the {@link PotionEffectTypes#RESISTANCE} or any other + * {@link PotionEffectType} that can be deemed as reducing incoming damage. + * + *

Usually, within the {@link DamageStep#cause()} will reside + * a {@link PotionEffect} including the amplifier and duration, signifying + * that the {@link PotionEffectType} is modifying the incoming damage.

+ */ + public static final DefaultedRegistryReference DEFENSIVE_POTION_EFFECT = DamageStepTypes.key(ResourceKey.sponge("defensive_potion_effect")); + + /** + * Represents the {@link DamageStep} that will reduce the enchantment damage from a + * {@link Player} if their attack cooldown has not been completed yet. + */ + public static final DefaultedRegistryReference ENCHANTMENT_COOLDOWN = DamageStepTypes.key(ResourceKey.sponge("enchantment_cooldown")); + + /** + * Represents a {@link DamageStep} that will modify freezing damage. + * E.g. {@link org.spongepowered.api.entity.living.monster.Blaze} take more damage from freezing sources. + */ + public static final DefaultedRegistryReference FREEZING_BONUS = DamageStepTypes.key(ResourceKey.sponge("freezing_bonus")); + + /** + * Represents the {@link DamageStep} that will modify damage from + * a {@link DamageSource#source()} that is a {@link org.spongepowered.api.entity.FallingBlock}. + * + *

Usually, within the {@link DamageStep#cause()} will reside + * an {@link ItemStackSnapshot} and an {@link Enchantment} signifying + * that the {@link EnchantmentType} of the {@link ItemStack} is modifying the + * incoming/outgoing damage.

+ */ + public static final DefaultedRegistryReference HARD_HAT = DamageStepTypes.key(ResourceKey.sponge("hard_hat")); + + /** + * Represents a {@link DamageStep} that will modify damage based on + * magic. + */ + public static final DefaultedRegistryReference MAGIC = DamageStepTypes.key(ResourceKey.sponge("magic")); + + /** + * Represents a {@link DamageStep} that will reduce outgoing damage + * based on a {@link PotionEffect}. + * + *

Usually, within the {@link DamageStep#cause()} will reside + * a {@link PotionEffect} including the amplifier and duration, signifying + * that the {@link PotionEffectType} is reducing the outgoing damage.

+ */ + public static final DefaultedRegistryReference NEGATIVE_POTION_EFFECT = DamageStepTypes.key(ResourceKey.sponge("negative_potion_effect")); + + /** + * Represents the {@link DamageStep} that will increase damage from + * a {@link PotionEffect} affecting the attacker. + */ + public static final DefaultedRegistryReference OFFENSIVE_POTION_EFFECT = DamageStepTypes.key(ResourceKey.sponge("offensive_potion_effect")); + + /** + * Represents a {@link DamageStep} that will reduce damage due to using a shield. + */ + public static final DefaultedRegistryReference SHIELD = DamageStepTypes.key(ResourceKey.sponge("shield")); + + /** + * Represents a {@link DamageStep} that is applied for a sweeping attack. + */ + public static final DefaultedRegistryReference SWEEPING = DamageStepTypes.key(ResourceKey.sponge("sweeping")); + + /** + * Represents a {@link DamageStep} that will add bonus damage based on the weapon used. + */ + public static final DefaultedRegistryReference WEAPON_BONUS = DamageStepTypes.key(ResourceKey.sponge("weapon_bonus")); + + /** + * Represents the {@link DamageStep} that will modify damage from + * an {@link EnchantmentType} on an equipped {@link ItemStack}. + * + *

Usually, within the {@link DamageStep#cause()} will reside + * an {@link ItemStackSnapshot} and an {@link Enchantment} signifying + * that the {@link EnchantmentType} of the {@link ItemStack} is modifying the + * incoming/outgoing damage.

+ */ + public static final DefaultedRegistryReference WEAPON_ENCHANTMENT = DamageStepTypes.key(ResourceKey.sponge("weapon_enchantment")); + + // SORTFIELDS:OFF + + // @formatter:on + + private DamageStepTypes() { + } + + public static Registry registry() { + return Sponge.game().registry(RegistryTypes.DAMAGE_STEP_TYPE); + } + + private static DefaultedRegistryReference key(final ResourceKey location) { + return RegistryKey.of(RegistryTypes.DAMAGE_STEP_TYPE, location).asDefaultedReference(Sponge::game); + } +} diff --git a/src/main/java/org/spongepowered/api/event/entity/AttackEntityEvent.java b/src/main/java/org/spongepowered/api/event/entity/AttackEntityEvent.java index 208f68a4108..38948d73ca1 100644 --- a/src/main/java/org/spongepowered/api/event/entity/AttackEntityEvent.java +++ b/src/main/java/org/spongepowered/api/event/entity/AttackEntityEvent.java @@ -25,38 +25,19 @@ package org.spongepowered.api.event.entity; import org.spongepowered.api.block.entity.carrier.Dispenser; -import org.spongepowered.api.effect.potion.PotionEffect; import org.spongepowered.api.entity.Entity; -import org.spongepowered.api.entity.living.Living; import org.spongepowered.api.entity.living.monster.skeleton.Skeleton; import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.entity.projectile.arrow.ArrowLike; -import org.spongepowered.api.event.Cancellable; import org.spongepowered.api.event.Cause; -import org.spongepowered.api.event.Event; -import org.spongepowered.api.event.cause.entity.damage.DamageFunction; -import org.spongepowered.api.event.cause.entity.damage.DamageModifier; -import org.spongepowered.api.event.cause.entity.damage.DamageModifierType; -import org.spongepowered.api.event.cause.entity.damage.DamageModifierTypes; import org.spongepowered.api.event.cause.entity.damage.DamageType; import org.spongepowered.api.event.cause.entity.damage.source.DamageSource; -import org.spongepowered.api.event.impl.entity.AbstractAttackEntityEvent; import org.spongepowered.api.item.ItemTypes; import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.item.inventory.ItemStackSnapshot; -import org.spongepowered.api.util.Tuple; -import org.spongepowered.api.util.annotation.eventgen.ImplementedBy; -import org.spongepowered.api.util.annotation.eventgen.PropertySettings; import org.spongepowered.api.world.World; import org.spongepowered.api.world.difficulty.Difficulties; import org.spongepowered.api.world.difficulty.Difficulty; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.DoubleUnaryOperator; -import java.util.function.Function; - /** * Represents the base event for when an {@link Entity} is being "attacked". * The uniqueness of this event is that all {@link DamageSource}s can deal @@ -77,7 +58,7 @@ * to a particular {@link DamageType}, has no static finalized amount of damage * to deal to a particular {@link Entity}. To properly represent this, * a {@link DamageSource} has various "states" such as: - * {@link DamageSource#isAbsolute()}, or {@link DamageSource#isBypassingArmor()}. + * {@link DamageSource#isMagic()}, or {@link DamageSource#isBypassingArmor()}. * Quite simply, the {@link DamageSource} will always be the "first" element * within a {@link Cause} that can be retrieved from {@link #cause()}.

* @@ -88,316 +69,43 @@ * the {@link Cause}. The same can be said if an {@link ArrowLike} was shot from * a {@link Dispenser} that was triggered by a {@link Player} flipping a * switch.

- * - *

Continuing with the notion of "modifiers" to damage, the "base" damage - * is modified or added onto after various unknown methods are called or - * processed on the damage. Optimally, these modifiers can be traced to a - * particular object, be it an {@link ItemStack}, {@link Difficulty}, or - * simply an an attribute. The interesting part is that these "modifiers" - * do not just define a static value to add to the "base" damage, they - * are usually a loose form of a {@link Function} that are applied to - * the "base" damage. Given that {@link Cause} has a unique capability of - * storing any and every {@link Object} willing to be passed into it, we - * can easily represent these "sources" of "modifiers" in a {@link Cause}. - * Now, knowing the "source" will not provide enough information, so a - * {@link DamageModifierType} is provided with a {@link DamageModifier} to - * paint the fullest picture of "explaining" the {@link DamageModifier} as to - * why it is present, and why it is "modifying" the "base" damage. Finally, - * we can associate a {@link DamageModifier} with a {@link Function} that is - * passed the current "damage" into {@link Function#apply(Object)}, being added - * to the current "damage". After all {@link DamageModifier} {@link Function}s - * are "applied", the overall "damage" is now the final damage to actually - * throw a {@link AttackEntityEvent}.

- * - *

Note that due to the mechanics of the game, {@link DamageModifier}s - * are always ordered in the order of which they apply their modifier onto - * the "base" damage. The implementation for {@link #finalOutputDamage()} can - * be exemplified like so:

- * - *
{@code
- * double damage = this.baseDamage;
- * for (Map.Entry> entry : this.modifierFunctions.entrySet()) { - * damage += checkNotNull(entry.getValue().apply(damage)); - * } - * return damage; - * }
- * - * TODO explain groups - *

After which, the "final" damage is simply the summation of the - * "base" damage and all "modified damage" for each {@link DamageModifier} - * provided in this event.

- * - *

Coming forward, it is possible to further customize not only the "base" - * damage, but override pre-existing {@link DamageModifier} {@link Function}s - * by calling {@link #setOutputDamage(DamageModifier, DoubleUnaryOperator)} at which point the - * end result may be undefined. However, if a custom {@link DamageModifier} - * that aims to alter the "final" damage based on some custom circumstances, - * calling {@link #setOutputDamage(DamageModifier, DoubleUnaryOperator)} on a new - * {@link DamageModifier} instance, easily created from the - * {@link org.spongepowered.api.event.cause.entity.damage.DamageModifier.Builder}, - * the provided pairing will be added at the - * "end" of the list for "modifying" the "base" damage.

- * - *

Note that this event is intended for processing incoming damage to - * an {@link Entity} prior to any {@link DamageModifier}s associated with - * the {@link #entity()}. The {@link AttackEntityEvent} is used - * to process the various {@link DamageModifier}s of which originate or are - * associated with the targeted {@link Entity}.

*/ -@ImplementedBy(AbstractAttackEntityEvent.class) -public interface AttackEntityEvent extends Event, Cancellable { - - /** - * For use with the {@link DamageSource} that is known as the "source" - * of the damage. - */ - String SOURCE = "Source"; - /** - * For use with a {@link DamageModifier} where it's type is a - * {@link DamageModifierTypes#HARD_HAT} and the {@link Cause} contains - * an {@link ItemStackSnapshot}, usually a helmet. - */ - String HARD_HAT_ARMOR = "HardHat"; - - /** - * or use with a {@link DamageModifier} where its type is a - * {@link DamageModifierTypes#SHIELD} and the {@link Cause} contains - * an {@link ItemStackSnapshot} (in Vanilla, a shield). - */ - String SHIELD = "Shield"; - - /** - * For use with a {@link DamageModifier} where it's type is a - * {@link DamageModifierTypes#ARMOR} and the {@link Cause} contains - * an {@link ItemStackSnapshot}. Separate from hard hat but still - * considered as "armor" where it will absorb a certain amount of damage - * before dealing damage to the wearer. - */ - String GENERAL_ARMOR = "GeneralArmor"; - /** - * For use with a {@link DamageModifier} where it's type is a - * {@link DamageModifierTypes#ARMOR} and the {@link Cause} contains - * an {@link ItemStackSnapshot} for a "helmet". - */ - String HELMET = AttackEntityEvent.GENERAL_ARMOR + ":head"; - /** - * For use with a {@link DamageModifier} where it's type is a - * {@link DamageModifierTypes#ARMOR} and the {@link Cause} contains - * an {@link ItemStackSnapshot} for a "chestplate". - */ - String CHESTPLATE = AttackEntityEvent.GENERAL_ARMOR + ":chestplate"; - /** - * For use with a {@link DamageModifier} where it's type is a - * {@link DamageModifierTypes#ARMOR} and the {@link Cause} contains - * an {@link ItemStackSnapshot} for "leggings". - */ - String LEGGINGS = AttackEntityEvent.GENERAL_ARMOR + ":leggings"; - /** - * For use with a {@link DamageModifier} where it's type is a - * {@link DamageModifierTypes#ARMOR} and the {@link Cause} contains - * an {@link ItemStackSnapshot} for "boots". - */ - String BOOTS = AttackEntityEvent.GENERAL_ARMOR + ":boots"; - /** - * For use with a {@link DamageModifier} where it's type is a - * {@link DamageModifierTypes#HARD_HAT} and the {@link Cause} contains - * a {@link PotionEffect}. - */ - String RESISTANCE = "Resistance"; - /** - * For use with a {@link DamageModifier} where it's type is a - * {@link DamageModifierTypes#ABSORPTION} and the {@link Cause} contains - * a {@link PotionEffect}. - */ - String ABSORPTION = "AbsorptionPotion"; - /** - * For use with a {@link DamageModifier} where the root cause is "created" - * by an object, usually the {@link Entity} or {@link Living} entity. - */ - String CREATOR = "Creator"; - /** - * For use with a {@link DamageSource} with a {@link DamageSource#source() entity} or - * {@link DamageSource#blockSnapshot()} such that it was last "notified" by the object - * represented in the cause. - * - *

Usually this is used where a {@link Player} interacted with the - * now {@link DamageSource} such that they

- */ - String NOTIFIER = "Notifier"; - - /** - * Gets the {@link Entity}. - * - * @return The entity - */ - Entity entity(); - - /** - * Gets the original "raw" amount of damage to deal to the targeted - * {@link Entity}. - * - * @return The original "raw" damage - */ - double originalDamage(); - - /** - * Gets the original "final" amount of damage after all original - * {@link DamageModifier}s are applied to {@link #originalDamage()}. - * The "final" damage is considered the amount of health being lost by - * the {@link Entity}, if health is tracked. - * - * @return The final amount of damage to originally deal - */ - @PropertySettings(requiredParameter = false, generateMethods = false) - double originalFinalDamage(); - - /** - * Gets an immutable {@link Map} of all original {@link DamageModifier}s - * and their associated "modified" damage. Note that ordering is not - * retained. - * - * @return An immutable map of the original modified damages - */ - @PropertySettings(requiredParameter = false, generateMethods = false) - Map> originalDamages(); - - /** - * Gets the original damage for the provided {@link DamageModifier}. If - * the provided {@link DamageModifier} was not included in - * {@link #originalDamages()}, an {@link IllegalArgumentException} is - * thrown. - * - * @param damageModifier The original damage modifier - * @return The original damage change - */ - Tuple originalModifierDamage(DamageModifier damageModifier); - - /** - * Gets the original {@link List} of {@link DamageModifier} to - * {@link Function} that was originally passed into the event. - * - * @return The list of damage modifier functions - */ - List originalFunctions(); - - /** - * Gets the "base" damage to deal to the targeted {@link Entity}. The - * "base" damage is the original value before passing along the chain of - * {@link Function}s for all known {@link DamageModifier}s. - * - * @return The base damage - */ - @PropertySettings(requiredParameter = false, generateMethods = false) - double baseOutputDamage(); - - /** - * Sets the "base" damage to deal to the targeted {@link Entity}. The - * "base" damage is the original value before passing along the chain of - * {@link Function}s for all known {@link DamageModifier}s. - * - * @param baseDamage The base damage - */ - void setBaseOutputDamage(double baseDamage); +public interface AttackEntityEvent extends DamageCalculationEvent { /** - * Gets the final damage that will be passed into the proceeding - * {@link AttackEntityEvent}. The final damage is the end result of the - * {@link #baseOutputDamage()} being applied in {@link Function#apply(Object)} - * available from all the {@link Tuple}s of {@link DamageModifier} to - * {@link Function} in {@link #originalFunctions()}. - * - * @return The final damage to deal + * Fires before the damage steps and their side effects are applied. + * The final damage is still unknown. */ - @PropertySettings(requiredParameter = false, generateMethods = false) - double finalOutputDamage(); + interface Pre extends AttackEntityEvent, DamageCalculationEvent.Pre { + } /** - * Checks whether the provided {@link DamageModifier} is applicable to the - * current available {@link DamageModifier}s. - * - * @param damageModifier The damage modifier to check - * @return True if the damage modifier is relevant to this event + * Fires after the damage steps and their side effects have been applied. */ - boolean isModifierApplicable(DamageModifier damageModifier); + interface Post extends AttackEntityEvent, DamageCalculationEvent.Post { - /** - * Gets the damage for the provided {@link DamageModifier}. Providing that - * {@link #isModifierApplicable(DamageModifier)} returns true, - * the cached "damage" for the {@link DamageModifier} is returned. - * - * @param damageModifier The damage modifier to get the damage for - * @return The modifier - */ - Tuple outputDamage(DamageModifier damageModifier); - - /** - * Sets the provided {@link Function} to be used for the given - * {@link DamageModifier}. If the {@link DamageModifier} is already - * included in {@link #modifiers()}, the {@link Function} replaces - * the existing function. If there is no {@link Tuple} for the - * {@link DamageModifier}, a new one is created and added to the end - * of the list of {@link Function}s to be applied to the - * {@link #baseOutputDamage()}. - * - *

If needing to create a custom {@link DamageModifier} is required, - * usage of the - * {@link org.spongepowered.api.event.cause.entity.damage.DamageModifier.Builder} - * is recommended.

- * - * @param damageModifier The damage modifier - * @param function The function to map to the modifier - */ - void setOutputDamage(DamageModifier damageModifier, DoubleUnaryOperator function); + /** + * Gets the original knockback modifier. + * + * @see #knockbackModifier() + * @return The original knockback modifier + */ + double originalKnockbackModifier(); - /** - * Adds the provided {@link DamageModifier} and {@link Function} to the - * list of modifiers, such that the {@link Set} containing - * {@link DamageModifierType}s provided in {@code before} will appear - * after the provided damage modifier. - * - * @param damageModifier The damage modifier to add - * @param function The associated function - * @param before The set containing the modifier types to come after - * the provided modifier - */ - void addDamageModifierBefore(DamageModifier damageModifier, DoubleUnaryOperator function, Set before); + /** + * Gets the knockback modifier. The modifier itself will apply to the + * momentum of the attacked entity. + * + * @return The knockback modifier + */ + double knockbackModifier(); - /** - * Adds the provided {@link DamageModifier} and {@link Function} to the list - * of modifiers, such that the modifier will appear in order after any - * current modifiers whose type are included in the provided {@link Set} - * of {@link DamageModifierType}s. - * - * @param damageModifier The damage modifier to add - * @param function The associated function - * @param after The set of modifier types to come before the new modifier - */ - void addDamageModifierAfter(DamageModifier damageModifier, DoubleUnaryOperator function, Set after); - - /** - * Gets a list of simple {@link Tuple}s of {@link DamageModifier} keyed to - * their representative {@link Function}s. All {@link DamageModifier}s are - * applicable to the entity based on the {@link DamageSource} and any - * possible invulnerabilities due to the {@link DamageSource}. - * - * @return A list of damage modifiers to functions - */ - @PropertySettings(requiredParameter = false, generateMethods = false) - List modifiers(); - - /** - * Gets the knock back modifier. The modifier itself will apply to the - * momentum of the attacked entity. - * - * @return The knock back modifier - */ - float knockbackModifier(); - - /** - * Sets the knock back modifier. The modifier itself will apply to the - * momentum of the attacked entity. - * - * @param modifier The knock back modifier to set - */ - void setKnockbackModifier(float modifier); + /** + * Sets the knockback modifier. + * + * @see #knockbackModifier() + * @param modifier The knockback modifier to set + */ + void setKnockbackModifier(double modifier); + } } diff --git a/src/main/java/org/spongepowered/api/event/entity/DamageCalculationEvent.java b/src/main/java/org/spongepowered/api/event/entity/DamageCalculationEvent.java new file mode 100644 index 00000000000..046b2635711 --- /dev/null +++ b/src/main/java/org/spongepowered/api/event/entity/DamageCalculationEvent.java @@ -0,0 +1,157 @@ +/* + * This file is part of SpongeAPI, 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.api.event.entity; + +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.event.Cancellable; +import org.spongepowered.api.event.Cause; +import org.spongepowered.api.event.Event; +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.DamageStepType; +import org.spongepowered.api.event.impl.entity.AbstractDamageCalculationEventPre; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.util.annotation.eventgen.ImplementedBy; +import org.spongepowered.api.world.difficulty.Difficulty; + +import java.util.List; + +/** + * The base event for when some damage is calculated, + * whether it is the damage output of an attack, or the damage inflicted to an entity. + * + *

The damage calculation starts with a base damage which is modified + * by a series of operations to obtain a final value. + * Some of these operations are captured as {@link DamageStep}s and are modifiable.

+ * + *

Optimally, these steps can be traced to a + * particular object, be it an {@link ItemStack}, {@link Difficulty}, or + * simply an an attribute. Given that {@link Cause} has a unique capability of + * storing any and every {@link Object} willing to be passed into it, we + * can easily represent these "sources" of "steps" in a {@link Cause}. + * Now, knowing the "source" will not provide enough information, so a + * {@link DamageStepType} is provided with a {@link DamageStep} to + * paint the fullest picture of "explaining" the {@link DamageStep} as to + * why it is present, and why it is modifying the base damage.

+ */ +public interface DamageCalculationEvent extends Event, Cancellable { + + /** + * Gets the targeted {@link Entity}. + * + * @return The targeted entity + */ + Entity entity(); + + /** + * Gets the original base damage to deal to the targeted {@link Entity}. + * + * @see #baseDamage() + * @return The original base damage + */ + double originalBaseDamage(); + + /** + * Gets the base damage to deal to the targeted {@link Entity}. + * The base damage is the value before the calculation and its {@link DamageStep}s. + * + * @return The base damage + */ + double baseDamage(); + + /** + * Fires before the damage steps and their side effects are applied. + * The final damage is still unknown. + */ + @ImplementedBy(AbstractDamageCalculationEventPre.class) + interface Pre extends DamageCalculationEvent { + + /** + * Sets the base damage to deal to the targeted {@link Entity}. + * + * @see #baseDamage() + * @param baseDamage The base damage + */ + void setBaseDamage(double baseDamage); + + /** + * Gets a mutable list of all modifiers that applies just before the step. + * + * @param type The step type + * @return The list of modifiers + */ + List modifiersBefore(DamageStepType type); + + /** + * Gets a mutable list of all modifiers that applies just after the step. + * + * @param type The step type + * @return The list of modifiers + */ + List modifiersAfter(DamageStepType type); + } + + /** + * Fires after the damage steps and their side effects have been applied. + * The steps have been captured and can't be changed. + * The final damage can still be changed. + */ + interface Post extends DamageCalculationEvent { + + /** + * Gets the original final damage to deal to the targeted {@link Entity}. + * + * @see #finalDamage() + * @return The final amount of damage to originally deal + */ + double originalFinalDamage(); + + /** + * Gets the final damage to deal to the targeted {@link Entity}. + * The final damage is the value after the calculation and its {@link DamageStep}s. + * The final damage is the amount of health being lost by the {@link Entity}, if health is tracked. + * + * @return The final damage + */ + double finalDamage(); + + /** + * Sets the final damage to deal to the targeted {@link Entity}. + * + * @see #finalDamage() + * @param finalDamage The base damage + */ + void setFinalDamage(double finalDamage); + + /** + * Gets the list of the captured steps during the damage calculation in the order they have been applied. + * Note that this list is not an exhaustive representation of all the operations applied, + * especially in a modded environment. + * + * @return The list of steps + */ + List steps(); + } +} diff --git a/src/main/java/org/spongepowered/api/event/entity/DamageEntityEvent.java b/src/main/java/org/spongepowered/api/event/entity/DamageEntityEvent.java index 990a05a1deb..833798d7dc7 100644 --- a/src/main/java/org/spongepowered/api/event/entity/DamageEntityEvent.java +++ b/src/main/java/org/spongepowered/api/event/entity/DamageEntityEvent.java @@ -25,36 +25,24 @@ package org.spongepowered.api.event.entity; import org.spongepowered.api.block.entity.carrier.Dispenser; +import org.spongepowered.api.data.Keys; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.entity.living.monster.skeleton.Skeleton; import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.entity.projectile.arrow.ArrowLike; -import org.spongepowered.api.event.Cancellable; import org.spongepowered.api.event.Cause; -import org.spongepowered.api.event.Event; -import org.spongepowered.api.event.cause.entity.damage.DamageFunction; -import org.spongepowered.api.event.cause.entity.damage.DamageModifier; -import org.spongepowered.api.event.cause.entity.damage.DamageModifier.Builder; -import org.spongepowered.api.event.cause.entity.damage.DamageModifierType; import org.spongepowered.api.event.cause.entity.damage.DamageType; import org.spongepowered.api.event.cause.entity.damage.source.DamageSource; -import org.spongepowered.api.event.impl.entity.AbstractDamageEntityEvent; import org.spongepowered.api.item.ItemTypes; import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.util.Tuple; -import org.spongepowered.api.util.annotation.eventgen.ImplementedBy; -import org.spongepowered.api.util.annotation.eventgen.PropertySettings; import org.spongepowered.api.world.World; import org.spongepowered.api.world.difficulty.Difficulties; import org.spongepowered.api.world.difficulty.Difficulty; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.DoubleUnaryOperator; +import java.util.Optional; /** - * Represents the base event for when an {@link Entity} is being "attacked". + * Represents the base event for when an {@link Entity} is being "damaged". * The uniqueness of this event is that all {@link DamageSource}s can deal * varying amounts of damage with varying modifiers based on various reasons. * Due to this ambiguous variety of information that is possible to provide, @@ -73,7 +61,7 @@ * to a particular {@link DamageType}, has no static finalized amount of damage * to deal to a particular {@link Entity}. To properly represent this, * a {@link DamageSource} has various "states" such as: - * {@link DamageSource#isAbsolute()}, or {@link DamageSource#isBypassingArmor()}. + * {@link DamageSource#isMagic()}, or {@link DamageSource#isBypassingArmor()}. * Quite simply, the {@link DamageSource} will always be the "first" element * within a {@link Cause} that can be retrieved from {@link #cause()}.

* @@ -84,233 +72,30 @@ * the {@link Cause}. The same can be said if an {@link ArrowLike} was shot from * a {@link Dispenser} that was triggered by a {@link Player} flipping a * switch.

- * - *

Continuing with the notion of "modifiers" to damage, the "base" damage - * is modified or added onto after various unknown methods are called or - * processed on the damage. Optimally, these modifiers can be traced to a - * particular object, be it an {@link ItemStack}, {@link Difficulty}, or - * simply an an attribute. The interesting part is that these "modifiers" - * do not just define a static value to add to the "base" damage, they - * are usually a loose form of a {@link DamageFunction} that are applied to - * the "base" damage. Given that {@link Cause} has a unique capability of - * storing any and every {@link Object} willing to be passed into it, we - * can easily represent these "sources" of "modifiers" in a {@link Cause}. - * Now, knowning the "source" will not provide enough information, so a - * {@link DamageModifierType} is provided with a {@link DamageModifier} to - * paint the fullest picture of "explaining" the {@link DamageModifier} as to - * why it is present, and why it is "modifying" the "base" damage. Finally, - * we can associate a {@link DamageModifier} with a {@link DamageFunction} that is - * passed the current "damage" into {@link DoubleUnaryOperator#applyAsDouble(double)} - * , being added - * to the current "damage". After all {@link DamageModifier} {@link DamageFunction}s - * are "applied", the overall "damage" is now the final damage to actually - * throw a {@link DamageEntityEvent}.

- * - *

Note that due to the mechanics of the game, {@link DamageModifier}s - * are always ordered in the order of which they apply their modifier onto - * the "base" damage. The implementation for {@link #finalDamage()} can be - * exemplified like so:

- * - *
{@code
- * double damage = this.baseDamage;
- * for (Map.Entry> entry : this.modifierFunctions.entrySet()) { - * damage += checkNotNull(entry.getValue().apply(damage)); - * } - * return damage; - * }
- * - * TODO explain groups - *

After which, the "final" damage is simply the summation of the - * "base" damage and all "modified damage" for each {@link DamageModifier} - * provided in this event.

- * - *

Coming forward, it is possible to further customize not only the "base" - * damage, but override pre-existing {@link DamageModifier} {@link DamageFunction}s - * by calling {@link #setDamage(DamageModifier, DoubleUnaryOperator)} at which point the - * end result may be undefined. However, if a custom {@link DamageModifier} - * that aims to alter the "final" damage based on some custom circumstances, - * calling {@link #setDamage(DamageModifier, DoubleUnaryOperator)} on a new - * {@link DamageModifier} instance, easily created from the - * {@link org.spongepowered.api.event.cause.entity.damage.DamageModifier.Builder}, - * the provided pairing will be added at the - * "end" of the list for "modifying" the "base" damage.

- * - * TODO this is wrong? - *

Note that this event is intended for processing incoming damage to - * an {@link Entity} prior to any {@link DamageModifier}s associated with - * the {@link #entity()}. The {@link DamageEntityEvent} is used - * to process the various {@link DamageModifier}s of which originate or are - * associated with the targeted {@link Entity}.

*/ -@ImplementedBy(AbstractDamageEntityEvent.class) -public interface DamageEntityEvent extends Event, Cancellable { - - /** - * Gets the {@link Entity}. - * - * @return The entity - */ - Entity entity(); - - /** - * Gets the original "raw" amount of damage to deal to the targeted - * {@link Entity}. - * - * @return The original "raw" damage - */ - double originalDamage(); - - /** - * Gets the original "final" amount of damage after all original - * {@link DamageModifier}s are applied to {@link #originalDamage()}. - * The "final" damage is considered the amount of health being lost by - * the {@link Entity}, if health is tracked. - * - * @return The final amount of damage to originally deal - */ - @PropertySettings(requiredParameter = false, generateMethods = false) - double originalFinalDamage(); - - /** - * Gets an immutable {@link Map} of all original {@link DamageModifier}s - * and their associated "modified" damage. Note that ordering is not - * retained. - * - * @return An immutable map of the original modified damages - */ - @PropertySettings(requiredParameter = false, generateMethods = false) - Map> originalDamages(); - - /** - * Gets the original damage for the provided {@link DamageModifier}. If - * the provided {@link DamageModifier} was not included in - * {@link #originalDamages()}, an {@link IllegalArgumentException} is - * thrown. - * - * @param damageModifier The original damage modifier - * @return The original damage change - */ - Tuple originalModifierDamage(DamageModifier damageModifier); - - /** - * Gets the original {@link List} of {@link DamageModifier} to - * {@link DamageFunction} that was originally passed into the event. - * - * @return The list of damage modifier functions - */ - List originalFunctions(); - - /** - * Gets the "base" damage to deal to the targeted {@link Entity}. The - * "base" damage is the original value before passing along the chain of - * {@link DamageFunction}s for all known {@link DamageModifier}s. - * - * @return The base damage - */ - @PropertySettings(requiredParameter = false, generateMethods = false) - double baseDamage(); - - /** - * Sets the "base" damage to deal to the targeted {@link Entity}. The - * "base" damage is the original value before passing along the chain of - * {@link DamageFunction}s for all known {@link DamageModifier}s. - * - * @param baseDamage The base damage - */ - void setBaseDamage(double baseDamage); - - /** - * Gets the final damage that will be passed into the proceeding - * {@link DamageEntityEvent}. The final damage is the end result of the - * {@link #baseDamage()} being applied in {@link DoubleUnaryOperator#applyAsDouble(double)} - * available from all the {@link Tuple}s of {@link DamageModifier} to - * {@link DamageFunction} in {@link #originalFunctions()}. - * - * @return The final damage to deal - */ - @PropertySettings(requiredParameter = false, generateMethods = false) - double finalDamage(); - - /** - * Checks whether the provided {@link DamageModifier} is applicable to the - * current available {@link DamageModifier}s. - * - * @param damageModifier The damage modifier to check - * @return True if the damage modifier is relevant to this event - */ - boolean isModifierApplicable(DamageModifier damageModifier); - - /** - * Gets the damage for the provided {@link DamageModifier}. Providing that - * {@link #isModifierApplicable(DamageModifier)} returns true, - * the cached "damage" for the {@link DamageModifier} is returned. - * - * @param damageModifier The damage modifier to get the damage for - * @return The modifier - */ - Tuple damage(DamageModifier damageModifier); - - /** - * Sets the provided {@link DamageFunction} to be used for the given - * {@link DamageModifier}. If the {@link DamageModifier} is already - * included in {@link #modifiers()}, the {@link DoubleUnaryOperator} replaces - * the existing function. If there is no {@link Tuple} for the - * {@link DamageModifier}, a new one is created and added to the end - * of the list of {@link DoubleUnaryOperator}s to be applied to the - * {@link #baseDamage()}. - * - *

If needing to create a custom {@link DamageModifier} is required, - * usage of the {@link Builder} is recommended.

- * - * @param damageModifier The damage modifier - * @param function The function to map to the modifier - */ - void setDamage(DamageModifier damageModifier, DoubleUnaryOperator function); - - /** - * Adds the provided {@link DamageModifier} and {@link DoubleUnaryOperator} to the - * list of modifiers, such that the {@link Set} containing - * {@link DamageModifierType}s provided in {@code before} will appear - * after the provided damage modifier. - * - * @param damageModifier The damage modifier to add - * @param function The associated function - * @param before The set containing the modifier types to come after - * the provided modifier - */ - void addDamageModifierBefore(DamageModifier damageModifier, DoubleUnaryOperator function, Set before); - - /** - * Adds the provided {@link DamageModifier} and {@link DoubleUnaryOperator} to the list - * of modifiers, such that the modifier will appear in order after any - * current modifiers whose type are included in the provided {@link Set} of - * {@link DamageModifierType}s. - * - * @param damageModifier The damage modifier to add - * @param function The associated function - * @param after The set of modifier types to come before the new modifier - */ - void addModifierAfter(DamageModifier damageModifier, DoubleUnaryOperator function, Set after); +public interface DamageEntityEvent extends DamageCalculationEvent { /** - * Gets a list of simple {@link Tuple}s of {@link DamageModifier} keyed to - * their representative {@link DamageFunction}s. All {@link DamageModifier}s are - * applicable to the entity based on the {@link DamageSource} and any - * possible invulnerabilities due to the {@link DamageSource}. - * - * @return A list of damage modifiers to functions + * Fires before the damage steps and their side effects are applied. + * The final damage is still unknown. */ - @PropertySettings(requiredParameter = false, generateMethods = false) - List modifiers(); + interface Pre extends DamageEntityEvent, DamageCalculationEvent.Pre { + } /** - * Returns whether or not this event will cause the entity to die if the - * event is not cancelled. Only supported for living entities, returns false - * if {@link #entity()} is not a living entity. - * - * @return Whether the entity will die + * Fires after the damage steps and their side effects have been applied. */ - @PropertySettings(requiredParameter = false, generateMethods = false) - boolean willCauseDeath(); + interface Post extends DamageEntityEvent, DamageCalculationEvent.Post { + /** + * Returns whether this event will cause the entity to die if the event is not cancelled. + * Only supported for living entities, returns false if {@link #entity()} is not a living entity. + * + * @return Whether the entity will die + */ + default boolean willCauseDeath() { + final Optional health = this.entity().get(Keys.HEALTH); + return health.isPresent() && health.get() - this.finalDamage() <= 0; + } + } } diff --git a/src/main/java/org/spongepowered/api/event/impl/entity/AbstractAttackEntityEvent.java b/src/main/java/org/spongepowered/api/event/impl/entity/AbstractAttackEntityEvent.java deleted file mode 100644 index 6d8531e060a..00000000000 --- a/src/main/java/org/spongepowered/api/event/impl/entity/AbstractAttackEntityEvent.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * This file is part of SpongeAPI, 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.api.event.impl.entity; - -import org.spongepowered.api.event.cause.entity.damage.DamageFunction; -import org.spongepowered.api.event.cause.entity.damage.DamageModifier; -import org.spongepowered.api.event.cause.entity.damage.DamageModifierType; -import org.spongepowered.api.event.cause.entity.damage.ModifierFunction; -import org.spongepowered.api.event.entity.AttackEntityEvent; -import org.spongepowered.api.util.Tuple; -import org.spongepowered.api.util.annotation.eventgen.UseField; - -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.function.DoubleUnaryOperator; -import java.util.function.Function; -import java.util.stream.Collectors; - -public abstract class AbstractAttackEntityEvent extends AbstractModifierEvent implements AttackEntityEvent { - - @UseField protected double originalDamage; - @UseField protected List originalFunctions; - - @UseField protected double baseDamage; - - @Override - protected final void init() { - this.originalFunctions = this.init(this.originalDamage, this.originalFunctions); - this.baseDamage = this.originalDamage; - } - - @Override - public final Tuple originalModifierDamage(final DamageModifier damageModifier) { - Objects.requireNonNull(damageModifier, "Damage modifier cannot be null!"); - for (final var tuple : this.originalModifiers) { - if (tuple.first().equals(damageModifier)) { - return tuple.second(); - } - } - throw new IllegalArgumentException("The provided damage modifier is not applicable: " + damageModifier.toString()); - } - - @Override - public final double originalFinalDamage() { - return this.originalFinalAmount; - } - - @Override - public final Map> originalDamages() { - - return this.originalModifierMap; - } - - @Override - public final double finalOutputDamage() { - return this.finalAmount(this.baseDamage); - } - - @Override - public final boolean isModifierApplicable(final DamageModifier damageModifier) { - return this.modifiers.containsKey(Objects.requireNonNull(damageModifier)); - } - - @Override - public final Tuple outputDamage(final DamageModifier damageModifier) { - if (!this.modifiers.containsKey(Objects.requireNonNull(damageModifier, "Damage Modifier cannot be null!"))) { - throw new IllegalArgumentException("The provided damage modifier is not applicable: " + damageModifier.toString()); - } - return this.modifiers.get(Objects.requireNonNull(damageModifier)); - } - - @Override - public final void setOutputDamage(final DamageModifier damageModifier, final DoubleUnaryOperator function) { - Objects.requireNonNull(damageModifier, "Damage modifier was null!"); - Objects.requireNonNull(function, "Function was null!"); - int indexToAddTo = 0; - boolean addAtEnd = true; - for (final Iterator iterator = this.modifierFunctions.iterator(); iterator.hasNext(); ) { - final ModifierFunction tuple = iterator.next(); - if (tuple.modifier().equals(damageModifier)) { - iterator.remove(); - addAtEnd = false; - break; - } - indexToAddTo++; - } - if (addAtEnd) { - this.modifierFunctions.add(new DamageFunction(damageModifier, function)); - } else { - this.modifierFunctions.add(indexToAddTo, new DamageFunction(damageModifier, function)); - } - this.recalculate(this.baseDamage); - } - - @Override - public void addDamageModifierBefore(final DamageModifier damageModifier, final DoubleUnaryOperator function, final Set before) { - Objects.requireNonNull(damageModifier, "Damage modifier was null!"); - Objects.requireNonNull(function, "Function was null!"); - int indexToAddBefore = -1; - int index = 0; - for (final ModifierFunction tuple : this.modifierFunctions) { - if (tuple.modifier().equals(damageModifier)) { - throw new IllegalArgumentException("Cannot add a duplicate modifier"); - } - if (before.contains(tuple.modifier().type())) { - indexToAddBefore = index; - } - index++; - - } - if (indexToAddBefore == -1) { - this.modifierFunctions.add(new DamageFunction(damageModifier, function)); - } else { - this.modifierFunctions.add(indexToAddBefore, new DamageFunction(damageModifier, function)); - } - this.recalculate(this.baseDamage); - } - - @Override - public void addDamageModifierAfter(final DamageModifier damageModifier, final DoubleUnaryOperator function, final Set after) { - Objects.requireNonNull(damageModifier, "Damage modifier was null!"); - Objects.requireNonNull(function, "Function was null!"); - int indexToAddAfter = -1; - int index = 0; - for (final ModifierFunction tuple : this.modifierFunctions) { - if (tuple.modifier().equals(damageModifier)) { - throw new IllegalArgumentException("Cannot add a duplicate modifier"); - } - if (after.contains(tuple.modifier().type())) { - indexToAddAfter = index; - } - index++; - - } - if (indexToAddAfter == -1) { - this.modifierFunctions.add(new DamageFunction(damageModifier, function)); - } else { - this.modifierFunctions.add(indexToAddAfter + 1, new DamageFunction(damageModifier, function)); - } - this.recalculate(this.baseDamage); - } - - @Override - public double baseOutputDamage() { - return this.baseDamage; - } - - @Override - public final void setBaseOutputDamage(final double baseDamage) { - this.baseDamage = baseDamage; - this.recalculate(this.baseDamage); - } - - @Override - protected DamageFunction convertTuple(final DamageModifier obj, final DoubleUnaryOperator function) { - return new DamageFunction(obj, function); - } - - @Override - public List modifiers() { - return this.modifierFunctions.stream().map((Function, DamageFunction>) entry -> { - if (entry instanceof DamageFunction) { - return (DamageFunction) entry; - } else { - return new DamageFunction(entry.modifier(), entry.function()); - } - }).collect(Collectors.toUnmodifiableList()); - } -} diff --git a/src/main/java/org/spongepowered/api/event/cause/entity/damage/ModifierFunction.java b/src/main/java/org/spongepowered/api/event/impl/entity/AbstractDamageCalculationEventPre.java similarity index 53% rename from src/main/java/org/spongepowered/api/event/cause/entity/damage/ModifierFunction.java rename to src/main/java/org/spongepowered/api/event/impl/entity/AbstractDamageCalculationEventPre.java index a13e4dfaecf..28ccbe5dda7 100644 --- a/src/main/java/org/spongepowered/api/event/cause/entity/damage/ModifierFunction.java +++ b/src/main/java/org/spongepowered/api/event/impl/entity/AbstractDamageCalculationEventPre.java @@ -22,31 +22,29 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.api.event.cause.entity.damage; +package org.spongepowered.api.event.impl.entity; -import java.util.function.DoubleUnaryOperator; +import org.spongepowered.api.event.cause.entity.damage.DamageModifier; +import org.spongepowered.api.event.cause.entity.damage.DamageStepType; +import org.spongepowered.api.event.entity.DamageCalculationEvent; +import org.spongepowered.api.event.impl.AbstractEvent; -/** - * A function associating a - * {@link DamageModifier} with a {@link DoubleUnaryOperator} of the resultant - * effect. - * - * @param The modifier type - */ -public interface ModifierFunction { +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; - /** - * Gets the modifier used by this modifier function. - * - * @return The modifier - */ - M modifier(); +public abstract class AbstractDamageCalculationEventPre extends AbstractEvent implements DamageCalculationEvent.Pre { + private final Map> modifiersBeforeMap = new HashMap<>(); + private final Map> modifiersAfterMap = new HashMap<>(); - /** - * Gets the double unary operator used by this function. - * - * @return The unary operator - */ - DoubleUnaryOperator function(); + @Override + public List modifiersBefore(DamageStepType type) { + return this.modifiersBeforeMap.computeIfAbsent(type, k -> new LinkedList<>()); + } + @Override + public List modifiersAfter(DamageStepType type) { + return this.modifiersAfterMap.computeIfAbsent(type, k -> new LinkedList<>()); + } } diff --git a/src/main/java/org/spongepowered/api/event/impl/entity/AbstractDamageEntityEvent.java b/src/main/java/org/spongepowered/api/event/impl/entity/AbstractDamageEntityEvent.java deleted file mode 100644 index 598d34e222e..00000000000 --- a/src/main/java/org/spongepowered/api/event/impl/entity/AbstractDamageEntityEvent.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * This file is part of SpongeAPI, 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.api.event.impl.entity; - -import org.spongepowered.api.data.Keys; -import org.spongepowered.api.event.cause.entity.damage.DamageFunction; -import org.spongepowered.api.event.cause.entity.damage.DamageModifier; -import org.spongepowered.api.event.cause.entity.damage.DamageModifierType; -import org.spongepowered.api.event.cause.entity.damage.ModifierFunction; -import org.spongepowered.api.event.entity.DamageEntityEvent; -import org.spongepowered.api.util.Tuple; -import org.spongepowered.api.util.annotation.eventgen.UseField; - -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.function.DoubleUnaryOperator; -import java.util.function.Function; -import java.util.stream.Collectors; - -public abstract class AbstractDamageEntityEvent extends AbstractModifierEvent implements DamageEntityEvent { - - @UseField protected double originalDamage; - @UseField protected List originalFunctions; - @UseField protected double baseDamage; - - @Override - protected final void init() { - this.originalFunctions = this.init(this.originalDamage, this.originalFunctions); - this.baseDamage = this.originalDamage; - } - - @Override - public final Tuple originalModifierDamage(DamageModifier damageModifier) { - Objects.requireNonNull(damageModifier, "The damage modifier cannot be null!"); - for (var tuple : this.originalModifiers) { - if (tuple.first().equals(damageModifier)) { - return tuple.second(); - } - } - throw new IllegalArgumentException("The provided damage modifier is not applicable: " + damageModifier.toString()); - } - - @Override - public final double originalFinalDamage() { - return this.originalFinalAmount; - } - - @Override - public final Map> originalDamages() { - - return this.originalModifierMap; - } - - @Override - public final double finalDamage() { - return this.finalAmount(this.baseDamage); - } - - @Override - public final boolean isModifierApplicable(DamageModifier damageModifier) { - return this.modifiers.containsKey(Objects.requireNonNull(damageModifier)); - } - - @Override - public final Tuple damage(DamageModifier damageModifier) { - if (!this.modifiers.containsKey(Objects.requireNonNull(damageModifier, "Damage Modifier cannot be null!"))) { - throw new IllegalArgumentException("The provided damage modifier is not applicable: " + damageModifier); - } - return this.modifiers.get(Objects.requireNonNull(damageModifier)); - } - - @Override - public final void setDamage(DamageModifier damageModifier, DoubleUnaryOperator function) { - Objects.requireNonNull(damageModifier, "Damage modifier was null!"); - Objects.requireNonNull(function, "Function was null!"); - int indexToAddTo = 0; - boolean addAtEnd = true; - for (Iterator iterator = this.modifierFunctions.iterator(); iterator.hasNext(); ) { - final ModifierFunction tuple = iterator.next(); - if (tuple.modifier().equals(damageModifier)) { - iterator.remove(); - addAtEnd = false; - break; - } - indexToAddTo++; - } - if (addAtEnd) { - this.modifierFunctions.add(new DamageFunction(damageModifier, function)); - } else { - this.modifierFunctions.add(indexToAddTo, new DamageFunction(damageModifier, function)); - } - this.recalculate(this.baseDamage); - } - - @Override - public void addDamageModifierBefore(DamageModifier damageModifier, DoubleUnaryOperator function, Set before) { - Objects.requireNonNull(damageModifier, "Damage modifier was null!"); - Objects.requireNonNull(function, "Function was null!"); - int indexToAddBefore = -1; - int index = 0; - for (ModifierFunction tuple : this.modifierFunctions) { - if (tuple.modifier().equals(damageModifier)) { - throw new IllegalArgumentException("Cannot add a duplicate modifier"); - } - if (before.contains(tuple.modifier().type())) { - indexToAddBefore = index; - } - index++; - - } - if (indexToAddBefore == -1) { - this.modifierFunctions.add(new DamageFunction(damageModifier, function)); - } else { - this.modifierFunctions.add(indexToAddBefore, new DamageFunction(damageModifier, function)); - } - this.recalculate(this.baseDamage); - } - - @Override - public void addModifierAfter(DamageModifier damageModifier, DoubleUnaryOperator function, Set after) { - Objects.requireNonNull(damageModifier, "Damage modifier was null!"); - Objects.requireNonNull(function, "Function was null!"); - int indexToAddAfter = -1; - int index = 0; - for (ModifierFunction tuple : this.modifierFunctions) { - if (tuple.modifier().equals(damageModifier)) { - throw new IllegalArgumentException("Cannot add a duplicate modifier"); - } - if (after.contains(tuple.modifier().type())) { - indexToAddAfter = index; - } - index++; - - } - if (indexToAddAfter == -1) { - this.modifierFunctions.add(new DamageFunction(damageModifier, function)); - } else { - this.modifierFunctions.add(indexToAddAfter + 1, new DamageFunction(damageModifier, function)); - } - this.recalculate(this.baseDamage); - } - - @Override - protected DamageFunction convertTuple(DamageModifier obj, DoubleUnaryOperator function) { - return new DamageFunction(obj, function); - } - - @Override - public List modifiers() { - return this.modifierFunctions.stream().map((Function, DamageFunction>) entry -> { - if (entry instanceof DamageFunction) { - return (DamageFunction) entry; - } else { - return new DamageFunction(entry.modifier(), entry.function()); - } - }).collect(Collectors.toUnmodifiableList()); - } - - @Override - public double baseDamage() { - return this.baseDamage; - } - - @Override - public final void setBaseDamage(double baseDamage) { - this.baseDamage = baseDamage; - this.recalculate(this.baseDamage); - } - - @Override - public boolean willCauseDeath() { - final Optional health = this.entity().get(Keys.HEALTH); - return health.isPresent() && health.get() - this.finalDamage() <= 0; - } - -} diff --git a/src/main/java/org/spongepowered/api/event/impl/entity/AbstractModifierEvent.java b/src/main/java/org/spongepowered/api/event/impl/entity/AbstractModifierEvent.java deleted file mode 100644 index f9b99168c97..00000000000 --- a/src/main/java/org/spongepowered/api/event/impl/entity/AbstractModifierEvent.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * This file is part of SpongeAPI, 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.api.event.impl.entity; - -import org.spongepowered.api.event.cause.entity.damage.DamageModifier; -import org.spongepowered.api.event.cause.entity.damage.ModifierFunction; -import org.spongepowered.api.event.entity.DamageEntityEvent; -import org.spongepowered.api.event.impl.AbstractEvent; -import org.spongepowered.api.util.Tuple; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.DoubleUnaryOperator; - -/** - * An abstract base class for implementations of {@link DamageEntityEvent}. - * - * @param The modifier type to use - */ -public abstract class AbstractModifierEvent, M> extends AbstractEvent { - - protected double originalFinalAmount; - protected List>> originalModifiers; - protected Map> originalModifierMap; - protected final LinkedHashMap> modifiers = new LinkedHashMap<>(); - protected final List modifierFunctions = new ArrayList<>(); - - protected List init(double baseAmount, List functions) { - functions.stream().map(entry -> this.convertTuple(entry.modifier(), entry.function())).forEach(this.modifierFunctions::add); - this.originalFinalAmount = this.recalculate(baseAmount); - this.originalModifiers = this.modifiers.entrySet().stream().map(e -> new Tuple<>(e.getKey(), e.getValue())).toList(); - this.originalModifierMap = Map.copyOf(this.modifiers); - return functions.stream().map(entry -> this.convertTuple(entry.modifier(), entry.function())).toList(); - } - - protected abstract T convertTuple(M obj, DoubleUnaryOperator function); - - protected double recalculate(final double baseAmount) { - final var amounts = AbstractModifierEvent.recalculate(this.modifierFunctions, baseAmount, this.modifiers); - return amounts.values().stream().mapToDouble(Double::doubleValue).sum(); - } - - private static , M> Map recalculate(final List functions, final double baseAmount, final Map> into) { - into.clear(); - final var defaultGroup = "default"; - final Map amounts = new HashMap<>(); - for (T func : functions) { - var group = defaultGroup; - if (func.modifier() instanceof DamageModifier damageModifier) { - group = damageModifier.group(); - } - final var oldAmount = amounts.getOrDefault(group, baseAmount); - final var newAmount = func.function().applyAsDouble(oldAmount); - amounts.put(group, newAmount); - into.put(func.modifier(), new Tuple<>(oldAmount, newAmount)); - } - if (amounts.isEmpty()) { - amounts.put(defaultGroup, baseAmount); - } - return amounts; - } - - protected double finalAmount(final double baseAmount) { - final var amounts = AbstractModifierEvent.finalAmounts(baseAmount, this.modifierFunctions); - return amounts.values().stream().mapToDouble(Double::doubleValue).sum(); - } - - public static , M> Map finalAmounts(final double baseAmount, final List modifiers) { - return AbstractModifierEvent.recalculate(modifiers, baseAmount, new LinkedHashMap<>()); - } - - /** - * Gets the modifiers affecting this event. - * - * @return The list of modifiers - */ - public List modifiers() { - return List.copyOf(this.modifierFunctions); - } -} diff --git a/src/main/java/org/spongepowered/api/registry/RegistryTypes.java b/src/main/java/org/spongepowered/api/registry/RegistryTypes.java index 4b1c9a87670..454889f18e6 100644 --- a/src/main/java/org/spongepowered/api/registry/RegistryTypes.java +++ b/src/main/java/org/spongepowered/api/registry/RegistryTypes.java @@ -116,8 +116,8 @@ import org.spongepowered.api.event.cause.entity.MovementType; import org.spongepowered.api.event.cause.entity.SpawnType; import org.spongepowered.api.event.cause.entity.damage.DamageEffect; -import org.spongepowered.api.event.cause.entity.damage.DamageModifierType; import org.spongepowered.api.event.cause.entity.damage.DamageScaling; +import org.spongepowered.api.event.cause.entity.damage.DamageStepType; import org.spongepowered.api.event.cause.entity.damage.DamageType; import org.spongepowered.api.fluid.FluidType; import org.spongepowered.api.item.FireworkShape; @@ -332,7 +332,7 @@ public final class RegistryTypes { public static final DefaultedRegistryType CURRENCY = RegistryTypes.spongeKeyInGame("currency"); - public static final DefaultedRegistryType DAMAGE_MODIFIER_TYPE = RegistryTypes.spongeKeyInGame("damage_modifier_type"); + public static final DefaultedRegistryType DAMAGE_STEP_TYPE = RegistryTypes.spongeKeyInGame("damage_step_type"); public static final DefaultedRegistryType DAMAGE_TYPE = RegistryTypes.minecraftKeyInServer("damage_type"); public static final DefaultedRegistryType DAMAGE_SCALING = RegistryTypes.spongeKeyInGame("damage_scaling"); diff --git a/src/test/java/org/spongepowered/api/event/SpongeAbstractDamageEntityEventTest.java b/src/test/java/org/spongepowered/api/event/SpongeAbstractDamageEntityEventTest.java deleted file mode 100644 index 38ad45d342a..00000000000 --- a/src/test/java/org/spongepowered/api/event/SpongeAbstractDamageEntityEventTest.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * This file is part of SpongeAPI, 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.api.event; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.spongepowered.api.entity.Entity; -import org.spongepowered.api.event.cause.entity.damage.DamageFunction; -import org.spongepowered.api.event.cause.entity.damage.DamageModifier; -import org.spongepowered.api.event.entity.DamageEntityEvent; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.function.DoubleUnaryOperator; - -class SpongeAbstractDamageEntityEventTest { - - private static final double ERROR = 0.03; - - @Test - void testParams() { - final Entity targetEntity = this.mockParam(Entity.class); - final int originalDamage = 5; - - final DamageEntityEvent event = SpongeEventFactory.createDamageEntityEvent(Cause.of(EventContext.empty(), "none"), - targetEntity, new ArrayList<>(), originalDamage); - - Assertions.assertEquals(event.originalDamage(), originalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.originalFinalDamage(), originalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - - Assertions.assertEquals(event.finalDamage(), originalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.baseDamage(), originalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - } - - @Test - void testSetBaseDamage() { - final Entity targetEntity = this.mockParam(Entity.class); - final int originalDamage = 5; - - final DamageEntityEvent event = SpongeEventFactory.createDamageEntityEvent(Cause.of(EventContext.empty(), "none"), - targetEntity, new ArrayList<>(), originalDamage); - - Assertions.assertEquals(event.originalDamage(), originalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.originalFinalDamage(), originalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - - event.setBaseDamage(20); - - Assertions.assertEquals(event.baseDamage(), 20, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.finalDamage(), 20, SpongeAbstractDamageEntityEventTest.ERROR); - - Assertions.assertEquals(event.originalDamage(), originalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.originalFinalDamage(), originalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - } - - @Test - void testUseModifiers() { - final Entity targetEntity = this.mockParam(Entity.class); - - final int originalDamage = 1; - final int originalFinalDamage = 18; - - final int firstModifierDamage = 3; - final int secondModifierDamage = 18; - - final DamageModifier firstModifer = this.mockParam(DamageModifier.class); - final DamageModifier secondModifier = this.mockParam(DamageModifier.class); - - final var firstDamageFunction = DamageFunction.of(firstModifer, p -> p + p * 2); - final var secondDamageFunction = DamageFunction.of(secondModifier, p -> p + p * 5); - - final List originalFunctions = Arrays.asList(firstDamageFunction, secondDamageFunction); - - final DamageEntityEvent event = SpongeEventFactory.createDamageEntityEvent(Cause.of(EventContext.empty(), "none"), - targetEntity, originalFunctions, originalDamage); - - final List originalFunctions1 = event.originalFunctions(); - Assertions.assertEquals(originalFunctions1, originalFunctions); - - Assertions.assertEquals(event.originalDamage(), originalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.originalFinalDamage(), originalFinalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - - final var originalDamages = event.originalDamages(); - - Assertions.assertEquals(originalDamages.size(), originalFunctions.size()); - - Assertions.assertEquals(originalDamages.get(firstModifer).second(), firstModifierDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(originalDamages.get(secondModifier).second(), secondModifierDamage, SpongeAbstractDamageEntityEventTest.ERROR); - - Assertions.assertEquals(event.originalModifierDamage(firstModifer).second(), firstModifierDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.originalModifierDamage(secondModifier).second(), secondModifierDamage, SpongeAbstractDamageEntityEventTest.ERROR); - - Assertions.assertEquals(event.originalFunctions(), originalFunctions); - } - - @Test - void testSetModifiers() { - final Entity targetEntity = this.mockParam(Entity.class); - - final int originalDamage = 1; - final int originalFinalDamage = 18; - - final int firstModifierDamage = 3; - final int secondModifierDamage = 18; - - final int firstChangedDamage = 1; - final int secondChangedDamage = 6; - - final int modifiedFinalDamage = 6; - - final DamageModifier firstModifer = this.mockParam(DamageModifier.class); - final DamageModifier secondModifier = this.mockParam(DamageModifier.class); - - - final var firstDamageFunction = DamageFunction.of(firstModifer, p -> p + p * 2); - final DoubleUnaryOperator newFirstDamageFunction = p -> p; - final var secondDamageFunction = DamageFunction.of(secondModifier, p -> p + p * 5); - final List originalFunctions = Arrays.asList(firstDamageFunction, secondDamageFunction); - - final DamageEntityEvent event = SpongeEventFactory.createDamageEntityEvent(Cause.of(EventContext.empty(), "none"), - targetEntity, originalFunctions, originalDamage); - - Assertions.assertEquals(event.originalFunctions(), originalFunctions); - - event.setDamage(firstModifer, newFirstDamageFunction); - - Assertions.assertEquals(event.damage(firstModifer).second(), firstChangedDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.damage(secondModifier).second(), secondChangedDamage, SpongeAbstractDamageEntityEventTest.ERROR); - - Assertions.assertEquals(event.originalModifierDamage(firstModifer).second(), firstModifierDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.originalModifierDamage(secondModifier).second(), secondModifierDamage, SpongeAbstractDamageEntityEventTest.ERROR); - - Assertions.assertEquals(event.originalDamage(), originalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.originalFinalDamage(), originalFinalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.finalDamage(), modifiedFinalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - - Assertions.assertEquals(event.originalFunctions(), originalFunctions); - - Assertions.assertEquals(event.modifiers(), Arrays.asList(DamageFunction.of(firstModifer, newFirstDamageFunction), originalFunctions.get(1))); - } - - @Test - void testAddModifier() { - final Entity targetEntity = this.mockParam(Entity.class); - - final int originalDamage = 1; - final int originalFinalDamage = 18; - - final int firstModifierDamage = 3; - final int secondModifierDamage = 18; - - final int modifiedFinalDamage = 36; - - final int thirdDamage = 36; - - final DamageModifier firstModifier = this.mockParam(DamageModifier.class); - final DamageModifier secondModifier = this.mockParam(DamageModifier.class); - final DamageModifier thirdModifier = this.mockParam(DamageModifier.class); - - final var firstDamageFunction = DamageFunction.of(firstModifier, p -> p + p * 2); - final var secondDamageFunction = DamageFunction.of(secondModifier, p -> p + p * 5); - final DoubleUnaryOperator thirdDamageFunction = p -> p + p; - - final List originalFunctions = Arrays.asList(firstDamageFunction, secondDamageFunction); - final List newFunctions = new ArrayList<>(originalFunctions); - newFunctions.add(DamageFunction.of(thirdModifier, thirdDamageFunction)); - - final DamageEntityEvent event = SpongeEventFactory.createDamageEntityEvent(Cause.of(EventContext.empty(), "none"), targetEntity, - originalFunctions, originalDamage); - - Assertions.assertEquals(event.originalFunctions(), originalFunctions); - - Assertions.assertFalse(event.isModifierApplicable(thirdModifier)); - - event.setDamage(thirdModifier, thirdDamageFunction); - - Assertions.assertEquals(event.damage(firstModifier).second(), firstModifierDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.damage(secondModifier).second(), secondModifierDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.damage(thirdModifier).second(), thirdDamage, SpongeAbstractDamageEntityEventTest.ERROR); - - Assertions.assertEquals(event.originalModifierDamage(firstModifier).second(), firstModifierDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.originalModifierDamage(secondModifier).second(), secondModifierDamage, SpongeAbstractDamageEntityEventTest.ERROR); - - Assertions.assertEquals(event.originalDamage(), originalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.originalFinalDamage(), originalFinalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - Assertions.assertEquals(event.finalDamage(), modifiedFinalDamage, SpongeAbstractDamageEntityEventTest.ERROR); - - Assertions.assertEquals(event.originalFunctions(), originalFunctions); - - Assertions.assertEquals(event.modifiers(), newFunctions); - } - - @Test - void testModifiersApplicable() { - final Entity targetEntity = this.mockParam(Entity.class); - - final DamageModifier firstModifer = this.mockParam(DamageModifier.class); - final DamageModifier secondModifier = this.mockParam(DamageModifier.class); - - final List - originalFunctions = Arrays.asList(DamageFunction.of(firstModifer, p -> p), DamageFunction.of(secondModifier, p -> p)); - - final DamageEntityEvent event = SpongeEventFactory.createDamageEntityEvent(Cause.of(EventContext.empty(), "none"), targetEntity, - originalFunctions, 0); - - Assertions.assertTrue(event.isModifierApplicable(firstModifer)); - Assertions.assertTrue(event.isModifierApplicable(secondModifier)); - Assertions.assertFalse(event.isModifierApplicable(this.mockParam(DamageModifier.class))); - } - - @Test - void testNotApplicableModifer() { - final DamageEntityEvent event = SpongeEventFactory.createDamageEntityEvent(Cause.of(EventContext.empty(), "none"), this.mockParam(Entity.class), - new ArrayList<>(), 0); - - final DamageModifier modifier = this.mockParam(DamageModifier.class); - - Assertions.assertFalse(event.isModifierApplicable(modifier)); - - Assertions.assertThrows(IllegalArgumentException.class, () -> event.originalModifierDamage(modifier)); - } - - @SuppressWarnings("unchecked") - private T mockParam(Class clazz) { - return (T) Objects.requireNonNull(SpongeEventFactoryTest.mockParam(clazz)); - } - -}