diff --git a/SpongeAPI b/SpongeAPI index 9b0d6786155..214a97c9174 160000 --- a/SpongeAPI +++ b/SpongeAPI @@ -1 +1 @@ -Subproject commit 9b0d678615595bec00cf099af576c931cc044d48 +Subproject commit 214a97c9174a315b226bc716c77d7a4d7c7091d2 diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/ItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/ItemStackData.java index a9d1d4212fd..e4d0d005a1c 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/ItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/ItemStackData.java @@ -47,6 +47,7 @@ import net.minecraft.world.item.component.ItemLore; import net.minecraft.world.item.component.UseCooldown; import net.minecraft.world.item.component.UseRemainder; +import net.minecraft.world.item.component.Weapon; import net.minecraft.world.item.consume_effects.ConsumeEffect; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.Platform; @@ -65,6 +66,7 @@ import org.spongepowered.common.adventure.SpongeAdventure; import org.spongepowered.common.data.provider.DataProviderRegistrator; import org.spongepowered.common.item.util.ItemStackUtil; +import org.spongepowered.common.util.Constants; import java.util.List; import java.util.Optional; @@ -370,6 +372,57 @@ public static void register(final DataProviderRegistrator registrator) { .get(h -> (ResourceKey) (Object) h.get(DataComponents.TOOLTIP_STYLE)) .set((h, v) -> h.set(DataComponents.TOOLTIP_STYLE, (ResourceLocation) (Object) v)) .delete(h -> h.remove(DataComponents.TOOLTIP_STYLE)) + .create(Keys.WEAPON_DAMAGE_PER_ATTACK) + .get(h -> { + final @Nullable Weapon weapon = h.get(DataComponents.WEAPON); + if (weapon == null) { + return null; + } + return weapon.itemDamagePerAttack(); + }) + .set((h, v) -> { + final @Nullable Weapon weapon = h.get(DataComponents.WEAPON); + h.set(DataComponents.WEAPON, new Weapon(v, weapon == null ? 0 : weapon.disableBlockingForSeconds())); + }) + .delete(h -> { + final @Nullable Weapon weapon = h.get(DataComponents.WEAPON); + if (weapon == null) { + return; + } + if (weapon.disableBlockingForSeconds() == 0) { + h.remove(DataComponents.WEAPON); + } else { + h.set(DataComponents.WEAPON, new Weapon(0, weapon.disableBlockingForSeconds())); + } + }) + .create(Keys.DISABLE_SHIELD_TICKS) + .get(h -> { + final @Nullable Weapon weapon = h.get(DataComponents.WEAPON); + if (weapon == null) { + return null; + } + return Ticks.of(Math.round( + Constants.TickConversions.TICKS_PER_SECOND * weapon.disableBlockingForSeconds() + )); + }) + .set((h, v) -> { + final @Nullable Weapon weapon = h.get(DataComponents.WEAPON); + h.set(DataComponents.WEAPON, new Weapon( + weapon == null ? 0 : weapon.itemDamagePerAttack(), + v.ticks() / (float) Constants.TickConversions.TICKS_PER_SECOND + )); + }) + .delete(h -> { + final @Nullable Weapon weapon = h.get(DataComponents.WEAPON); + if (weapon == null) { + return; + } + if (weapon.itemDamagePerAttack() == 0) { + h.remove(DataComponents.WEAPON); + } else { + h.set(DataComponents.WEAPON, new Weapon(weapon.itemDamagePerAttack())); + } + }) ; } // @formatter:on diff --git a/src/main/java/org/spongepowered/common/data/provider/item/stack/ShieldItemStackData.java b/src/main/java/org/spongepowered/common/data/provider/item/stack/ShieldItemStackData.java index 9b499f8084f..d0438441f6e 100644 --- a/src/main/java/org/spongepowered/common/data/provider/item/stack/ShieldItemStackData.java +++ b/src/main/java/org/spongepowered/common/data/provider/item/stack/ShieldItemStackData.java @@ -24,15 +24,28 @@ */ package org.spongepowered.common.data.provider.item.stack; +import net.minecraft.core.Holder; import net.minecraft.core.component.DataComponents; -import net.minecraft.world.item.BannerItem; +import net.minecraft.sounds.SoundEvent; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.ShieldItem; +import net.minecraft.world.item.component.BlocksAttacks; import net.minecraft.world.level.block.entity.BannerPatternLayers; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.meta.BannerPatternLayer; import org.spongepowered.api.data.type.DyeColor; +import org.spongepowered.api.data.type.ShieldDamageReduction; +import org.spongepowered.api.data.type.ShieldItemDamageFunction; +import org.spongepowered.api.effect.sound.SoundType; +import org.spongepowered.api.event.cause.entity.damage.DamageType; +import org.spongepowered.api.tag.Tag; +import org.spongepowered.api.util.Ticks; +import org.spongepowered.common.bridge.tags.TagBridge; import org.spongepowered.common.data.provider.DataProviderRegistrator; +import org.spongepowered.common.util.Constants; + +import java.util.List; +import java.util.Optional; public final class ShieldItemStackData { @@ -46,7 +59,6 @@ public static void register(final DataProviderRegistrator registrator) { .create(Keys.DYE_COLOR) .get(h -> (DyeColor) (Object) h.getOrDefault(DataComponents.BASE_COLOR, net.minecraft.world.item.DyeColor.WHITE)) .set((h, v) -> h.set(DataComponents.BASE_COLOR, (net.minecraft.world.item.DyeColor) (Object) v)) - .supports(h -> h.getItem() instanceof ShieldItem) .create(Keys.BANNER_PATTERN_LAYERS) .get(h -> h.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY).layers() .stream().map(BannerPatternLayer.class::cast).toList()) @@ -54,8 +66,194 @@ public static void register(final DataProviderRegistrator registrator) { h.set(DataComponents.BANNER_PATTERNS, new BannerPatternLayers(v.stream().map(BannerPatternLayers.Layer.class::cast).toList())); // TODO check setting banner base? Constants.TileEntity.Banner.BANNER_BASE / BannerPatternShapes.BASE }) - .supports(h -> h.getItem() instanceof ShieldItem || h.getItem() instanceof BannerItem); + .create(Keys.SHIELD_DEPLOY_TICKS) + .get(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.get(DataComponents.BLOCKS_ATTACKS); + if (blocksAttacks == null) { + return null; + } + return Ticks.of((long) (Constants.TickConversions.TICKS_PER_SECOND * blocksAttacks.blockDelaySeconds())); + }) + .set((h, v) -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + v.ticks() / (float) Constants.TickConversions.TICKS_PER_SECOND, + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + blocksAttacks.bypassedBy(), + blocksAttacks.blockSound(), + blocksAttacks.disableSound() + )); + }) + .create(Keys.DISABLE_SHIELD_TICKS_SCALE) + .get(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.get(DataComponents.BLOCKS_ATTACKS); + if (blocksAttacks == null) { + return null; + } + return (double) blocksAttacks.disableCooldownScale(); + }) + .set((h, v) -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + v.floatValue(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + blocksAttacks.bypassedBy(), + blocksAttacks.blockSound(), + blocksAttacks.disableSound() + )); + }) + .create(Keys.SHIELD_DAMAGE_REDUCTIONS) + .get(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.get(DataComponents.BLOCKS_ATTACKS); + if (blocksAttacks == null) { + return null; + } + return (List>) (Object) List.copyOf(blocksAttacks.damageReductions()); + }) + .set((h, v) -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + (List) (Object) List.copyOf(v), + blocksAttacks.itemDamage(), + blocksAttacks.bypassedBy(), + blocksAttacks.blockSound(), + blocksAttacks.disableSound() + )); + }) + .create(Keys.SHIELD_ITEM_DAMAGE_FUNCTION) + .get(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.get(DataComponents.BLOCKS_ATTACKS); + if (blocksAttacks == null) { + return null; + } + return (ShieldItemDamageFunction) (Object) blocksAttacks.itemDamage(); + }) + .set((h, v) -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + (BlocksAttacks.ItemDamageFunction) (Object) v, + blocksAttacks.bypassedBy(), + blocksAttacks.blockSound(), + blocksAttacks.disableSound() + )); + }) + .create(Keys.BYPASS_DAMAGE_TAG) + .get(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.get(DataComponents.BLOCKS_ATTACKS); + if (blocksAttacks == null || blocksAttacks.bypassedBy().isEmpty()) { + return null; + } + return (Tag) (Object) blocksAttacks.bypassedBy().get(); + }) + .set((h, v) -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + Optional.of(((TagBridge) v).bridge$asVanillaTag()), + blocksAttacks.blockSound(), + blocksAttacks.disableSound() + )); + }) + .delete(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + Optional.empty(), + blocksAttacks.blockSound(), + blocksAttacks.disableSound() + )); + }) + .create(Keys.SHIELD_BLOCK_SOUND) + .get(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.get(DataComponents.BLOCKS_ATTACKS); + if (blocksAttacks == null || blocksAttacks.blockSound().isEmpty()) { + return null; + } + return (SoundType) (Object) blocksAttacks.blockSound().get().value(); + }) + .set((h, v) -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + blocksAttacks.bypassedBy(), + Optional.of(Holder.direct((SoundEvent) (Object) v)), + blocksAttacks.disableSound() + )); + }) + .delete(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + blocksAttacks.bypassedBy(), + Optional.empty(), + blocksAttacks.disableSound() + )); + }) + .create(Keys.SHIELD_DISABLE_SOUND) + .get(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.get(DataComponents.BLOCKS_ATTACKS); + if (blocksAttacks == null || blocksAttacks.disableSound().isEmpty()) { + return null; + } + return (SoundType) (Object) blocksAttacks.disableSound().get().value(); + }) + .set((h, v) -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + blocksAttacks.bypassedBy(), + blocksAttacks.blockSound(), + Optional.of(Holder.direct((SoundEvent) (Object) v)) + )); + }) + .delete(h -> { + final @Nullable BlocksAttacks blocksAttacks = h.getOrDefault(DataComponents.BLOCKS_ATTACKS, BLOCKS_ATTACKS_DEFAULTS); + h.set(DataComponents.BLOCKS_ATTACKS, new BlocksAttacks( + blocksAttacks.blockDelaySeconds(), + blocksAttacks.disableCooldownScale(), + blocksAttacks.damageReductions(), + blocksAttacks.itemDamage(), + blocksAttacks.bypassedBy(), + blocksAttacks.blockSound(), + Optional.empty() + )); + }) + ; } // @formatter:on + private static final BlocksAttacks BLOCKS_ATTACKS_DEFAULTS = new BlocksAttacks( + 0, + 1, + List.of(), + BlocksAttacks.ItemDamageFunction.DEFAULT, + Optional.empty(), + Optional.empty(), + Optional.empty() + ); + } diff --git a/src/main/java/org/spongepowered/common/item/shield/SpongeShieldDamageReductionFactory.java b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldDamageReductionFactory.java new file mode 100644 index 00000000000..719ab097f17 --- /dev/null +++ b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldDamageReductionFactory.java @@ -0,0 +1,36 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.item.shield; + +import org.spongepowered.api.data.type.ShieldDamageReduction; + +public final class SpongeShieldDamageReductionFactory implements ShieldDamageReduction.Factory { + + @Override + public ShieldDamageReduction create(final ShieldDamageReduction.MultiplyAdd config) { + return (ShieldDamageReduction) config; + } + +} diff --git a/src/main/java/org/spongepowered/common/item/shield/SpongeShieldDamageReductionMultiplyAddBuilder.java b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldDamageReductionMultiplyAddBuilder.java new file mode 100644 index 00000000000..194978f163d --- /dev/null +++ b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldDamageReductionMultiplyAddBuilder.java @@ -0,0 +1,109 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.item.shield; + +import net.minecraft.core.HolderSet; +import net.minecraft.core.Registry; +import net.minecraft.world.item.component.BlocksAttacks; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.data.type.ShieldDamageReduction; +import org.spongepowered.api.event.cause.entity.damage.DamageType; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.api.tag.Tag; +import org.spongepowered.common.bridge.tags.TagBridge; +import org.spongepowered.common.util.Preconditions; + +import java.util.Optional; +import java.util.Set; + +public final class SpongeShieldDamageReductionMultiplyAddBuilder implements ShieldDamageReduction.MultiplyAdd.Builder { + private HolderSet damageTypes; + private Double horizontalBlockingAngle; + private double base = 0; + private double factor = 0; + + @Override + public ShieldDamageReduction.MultiplyAdd.Builder damageTypes(final Set damageTypes) { + final Registry registry = (Registry) Sponge.server().registry(RegistryTypes.DAMAGE_TYPE); + + this.damageTypes = HolderSet.direct(damageTypes.stream() + .map(dt -> registry.wrapAsHolder((net.minecraft.world.damagesource.DamageType) (Object) dt)) + .toList()); + + return this; + } + + @Override + public ShieldDamageReduction.MultiplyAdd.Builder damageTypes(final Tag tag) { + final Registry registry = (Registry) Sponge.server().registry(RegistryTypes.DAMAGE_TYPE); + final var vanillaTag = ((TagBridge) tag).bridge$asVanillaTag(); + this.damageTypes = registry.getOrThrow(vanillaTag); + + return this; + } + + @Override + public ShieldDamageReduction.MultiplyAdd.Builder horizontalBlockingAngle(final double angle) { + Preconditions.checkArgument(angle > 0, "angle must be positive"); + this.horizontalBlockingAngle = angle; + + return this; + } + + @Override + public ShieldDamageReduction.MultiplyAdd.Builder constantReduction(final double constant) { + this.base = constant; + + return this; + } + + @Override + public ShieldDamageReduction.MultiplyAdd.Builder fractionalReduction(final double fraction) { + this.factor = fraction; + + return this; + } + + @Override + public ShieldDamageReduction.MultiplyAdd build() { + return (ShieldDamageReduction.MultiplyAdd) (Object) new BlocksAttacks.DamageReduction( + this.horizontalBlockingAngle != null ? this.horizontalBlockingAngle.floatValue() : 90, + Optional.ofNullable(this.damageTypes), + (float) this.base, + (float) this.factor + ); + } + + @Override + public ShieldDamageReduction.MultiplyAdd.Builder reset() { + this.damageTypes = null; + this.horizontalBlockingAngle = null; + this.base = 0; + this.factor = 0; + + return this; + } + +} diff --git a/src/main/java/org/spongepowered/common/item/shield/SpongeShieldItemDamageFunctionFactory.java b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldItemDamageFunctionFactory.java new file mode 100644 index 00000000000..6174a5cb8c1 --- /dev/null +++ b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldItemDamageFunctionFactory.java @@ -0,0 +1,36 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.item.shield; + +import org.spongepowered.api.data.type.ShieldItemDamageFunction; + +public final class SpongeShieldItemDamageFunctionFactory implements ShieldItemDamageFunction.Factory { + + @Override + public ShieldItemDamageFunction create(final ShieldItemDamageFunction.MultiplyAdd config) { + return (ShieldItemDamageFunction) config; + } + +} diff --git a/src/main/java/org/spongepowered/common/item/shield/SpongeShieldItemDamageFunctionMultiplyAddBuilder.java b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldItemDamageFunctionMultiplyAddBuilder.java new file mode 100644 index 00000000000..cb241ad33f6 --- /dev/null +++ b/src/main/java/org/spongepowered/common/item/shield/SpongeShieldItemDamageFunctionMultiplyAddBuilder.java @@ -0,0 +1,76 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.item.shield; + +import net.minecraft.world.item.component.BlocksAttacks; +import org.spongepowered.api.data.type.ShieldItemDamageFunction; +import org.spongepowered.common.util.Preconditions; + +public final class SpongeShieldItemDamageFunctionMultiplyAddBuilder implements ShieldItemDamageFunction.MultiplyAdd.Builder { + private double minAttackDamage = 0; + private double constantDamage = 0; + private double fractionalDamage = 0; + + @Override + public ShieldItemDamageFunction.MultiplyAdd.Builder minAttackDamage(final double minDamage) { + Preconditions.checkArgument(minDamage >= 0, "minAttackDamage must not be negative"); + this.minAttackDamage = minDamage; + + return this; + } + + @Override + public ShieldItemDamageFunction.MultiplyAdd.Builder constantDamage(final double constantDamage) { + this.constantDamage = constantDamage; + + return this; + } + + @Override + public ShieldItemDamageFunction.MultiplyAdd.Builder fractionalDamage(final double fractionalDamage) { + this.fractionalDamage = fractionalDamage; + + return this; + } + + @Override + public ShieldItemDamageFunction.MultiplyAdd build() { + return (ShieldItemDamageFunction.MultiplyAdd) (Object) new BlocksAttacks.ItemDamageFunction( + (float) this.minAttackDamage, + (float) this.constantDamage, + (float) this.fractionalDamage + ); + } + + @Override + public ShieldItemDamageFunction.MultiplyAdd.Builder reset() { + this.minAttackDamage = 0; + this.constantDamage = 0; + this.fractionalDamage = 0; + + return this; + } + +} diff --git a/src/main/java/org/spongepowered/common/registry/SpongeBuilderProvider.java b/src/main/java/org/spongepowered/common/registry/SpongeBuilderProvider.java index 821ca0dcb09..3ddcd3873f3 100644 --- a/src/main/java/org/spongepowered/common/registry/SpongeBuilderProvider.java +++ b/src/main/java/org/spongepowered/common/registry/SpongeBuilderProvider.java @@ -51,6 +51,8 @@ import org.spongepowered.api.data.meta.BannerPatternLayer; import org.spongepowered.api.data.persistence.DataStore; import org.spongepowered.api.data.type.ArtType; +import org.spongepowered.api.data.type.ShieldDamageReduction; +import org.spongepowered.api.data.type.ShieldItemDamageFunction; import org.spongepowered.api.effect.particle.ParticleEffect; import org.spongepowered.api.effect.potion.PotionEffect; import org.spongepowered.api.effect.sound.SoundType; @@ -204,6 +206,8 @@ import org.spongepowered.common.item.recipe.ingredient.SpongeIngredientBuilder; import org.spongepowered.common.item.recipe.smithing.SpongeSmithingRecipeBuilder; import org.spongepowered.common.item.recipe.stonecutting.SpongeStoneCutterRecipeBuilder; +import org.spongepowered.common.item.shield.SpongeShieldDamageReductionMultiplyAddBuilder; +import org.spongepowered.common.item.shield.SpongeShieldItemDamageFunctionMultiplyAddBuilder; import org.spongepowered.common.map.canvas.SpongeMapCanvasBuilder; import org.spongepowered.common.map.color.SpongeMapColorBuilder; import org.spongepowered.common.map.decoration.SpongeMapDecorationBuilder; @@ -395,6 +399,8 @@ public void registerDefaultBuilders() { .register(PortalLogic.Builder.class, SpongePortalLogicBuilder::new) .register(ServerWorldProperties.LoadOptions.Builder.class, SpongeServerWorldPropertiesLoadOptions.BuilderImpl::new) .register(WorldArchetype.Builder.class, SpongeWorldArchetype.BuilderImpl::new) + .register(ShieldDamageReduction.MultiplyAdd.Builder.class, SpongeShieldDamageReductionMultiplyAddBuilder::new) + .register(ShieldItemDamageFunction.MultiplyAdd.Builder.class, SpongeShieldItemDamageFunctionMultiplyAddBuilder::new) ; } } diff --git a/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java b/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java index b39affb0bce..bdebce0d637 100644 --- a/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java +++ b/src/main/java/org/spongepowered/common/registry/SpongeFactoryProvider.java @@ -42,6 +42,8 @@ import org.spongepowered.api.command.selector.Selector; import org.spongepowered.api.data.DataManipulator; import org.spongepowered.api.data.type.ItemAction; +import org.spongepowered.api.data.type.ShieldDamageReduction; +import org.spongepowered.api.data.type.ShieldItemDamageFunction; import org.spongepowered.api.data.type.ToolRule; import org.spongepowered.api.data.value.Value; import org.spongepowered.api.effect.ForwardingViewer; @@ -133,6 +135,8 @@ import org.spongepowered.common.item.SpongeToolRuleFactory; import org.spongepowered.common.item.recipe.SpongeRecipeInputFactory; import org.spongepowered.common.item.recipe.smithing.SpongeArmorTrimFactory; +import org.spongepowered.common.item.shield.SpongeShieldDamageReductionFactory; +import org.spongepowered.common.item.shield.SpongeShieldItemDamageFunctionFactory; import org.spongepowered.common.item.util.SpongeItemStackComparatorFactory; import org.spongepowered.common.network.channel.SpongeChannelExceptionHandlerFactory; import org.spongepowered.common.network.status.SpongeFavicon; @@ -292,6 +296,8 @@ public void registerDefaultFactories() { .registerFactory(RecipeInput.Factory.class, new SpongeRecipeInputFactory()) .registerFactory(ArmorTrim.Factory.class, new SpongeArmorTrimFactory()) .registerFactory(RegistryRegistrationSet.Factory.class, new SpongeRegistryRegistrationSet.FactoryImpl()) + .registerFactory(ShieldDamageReduction.Factory.class, new SpongeShieldDamageReductionFactory()) + .registerFactory(ShieldItemDamageFunction.Factory.class, new SpongeShieldItemDamageFunctionFactory()) ; } } diff --git a/src/main/java/org/spongepowered/common/util/Constants.java b/src/main/java/org/spongepowered/common/util/Constants.java index db3dbb7b1f8..eb2601246ba 100644 --- a/src/main/java/org/spongepowered/common/util/Constants.java +++ b/src/main/java/org/spongepowered/common/util/Constants.java @@ -1539,6 +1539,7 @@ public static final class KeyValueMatcher { public static final class TickConversions { public static final int TICK_DURATION_MS = 50; + public static final int TICKS_PER_SECOND = 1000 / TICK_DURATION_MS; public static final Duration EFFECTIVE_MINIMUM_DURATION = Duration.ofMillis(TickConversions.TICK_DURATION_MS); public static final int MINECRAFT_DAY_TICKS = 24000; diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/component/BlocksAttacks_DamageReductionMixin_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/component/BlocksAttacks_DamageReductionMixin_API.java new file mode 100644 index 00000000000..248bc1f1910 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/component/BlocksAttacks_DamageReductionMixin_API.java @@ -0,0 +1,84 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.item.component; + +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.world.item.component.BlocksAttacks; +import org.spongepowered.api.data.type.ShieldDamageReduction; +import org.spongepowered.api.event.cause.entity.damage.DamageType; +import org.spongepowered.api.event.cause.entity.damage.source.DamageSource; +import org.spongepowered.asm.mixin.*; + +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +@Mixin(BlocksAttacks.DamageReduction.class) +@Implements({ + @Interface(iface = ShieldDamageReduction.class, prefix = "shielddamagereduction$"), + @Interface(iface = ShieldDamageReduction.MultiplyAdd.class, prefix = "shielddamagereductionmultiplyadd$") +}) +public abstract class BlocksAttacks_DamageReductionMixin_API implements ShieldDamageReduction, ShieldDamageReduction.MultiplyAdd { + + @Shadow @Final private Optional> type; + @Shadow @Final private float base; + @Shadow @Final private float factor; + @Shadow @Final private float horizontalBlockingAngle; + + @Shadow public abstract float shadow$resolve(net.minecraft.world.damagesource.DamageSource $$0, float $$1, double $$2); + + @Override + public MultiplyAdd configuration() { + return this; + } + + public double shielddamagereduction$resolve(final DamageSource source, final double damage, final double angle) { + return this.shadow$resolve((net.minecraft.world.damagesource.DamageSource) source, (float) damage, angle); + } + + @Override + public Optional> damageTypes() { + return this.type.map(set -> set.stream() + .map(Holder::value) + .map(DamageType.class::cast) + .collect(Collectors.toSet())); + } + + public double shielddamagereductionmultiplyadd$horizontalBlockingAngle() { + return this.horizontalBlockingAngle; + } + + @Override + public double constantReduction() { + return this.base; + } + + @Override + public double fractionalReduction() { + return this.factor; + } + +} diff --git a/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/component/BlocksAttacks_ItemDamageFunction_API.java b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/component/BlocksAttacks_ItemDamageFunction_API.java new file mode 100644 index 00000000000..558458c2460 --- /dev/null +++ b/src/mixins/java/org/spongepowered/common/mixin/api/minecraft/world/item/component/BlocksAttacks_ItemDamageFunction_API.java @@ -0,0 +1,67 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.mixin.api.minecraft.world.item.component; + +import net.minecraft.world.item.component.BlocksAttacks; +import org.spongepowered.api.data.type.ShieldItemDamageFunction; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(BlocksAttacks.ItemDamageFunction.class) +public abstract class BlocksAttacks_ItemDamageFunction_API implements ShieldItemDamageFunction.MultiplyAdd, ShieldItemDamageFunction { + + @Shadow @Final private float threshold; + @Shadow @Final private float base; + @Shadow @Final private float factor; + + @Shadow public abstract int shadow$apply(float $$0); + + @Override + public MultiplyAdd configuration() { + return this; + } + + @Override + public double resolve(final double damage) { + return this.shadow$apply((float) damage); + } + + @Override + public double minAttackDamage() { + return this.threshold; + } + + @Override + public double constantDamage() { + return this.base; + } + + @Override + public double fractionalDamage() { + return this.factor; + } + +} diff --git a/src/mixins/resources/mixins.sponge.api.json b/src/mixins/resources/mixins.sponge.api.json index c0189a5990e..840a6f902da 100644 --- a/src/mixins/resources/mixins.sponge.api.json +++ b/src/mixins/resources/mixins.sponge.api.json @@ -333,6 +333,8 @@ "minecraft.world.item.RarityMixin_API", "minecraft.world.item.ToolMaterialMixin_API", "minecraft.world.item.alchemy.PotionMixin_API", + "minecraft.world.item.component.BlocksAttacks_DamageReductionMixin_API", + "minecraft.world.item.component.BlocksAttacks_ItemDamageFunction_API", "minecraft.world.item.component.FireworkExplosionMixin_API", "minecraft.world.item.component.Tool_RuleMixin_API", "minecraft.world.item.consume_effects.ApplyStatusEffectsConsumeEffectMixin_API", diff --git a/src/test/java/org/spongepowered/common/data/key/KeysTest.java b/src/test/java/org/spongepowered/common/data/key/KeysTest.java new file mode 100644 index 00000000000..5a7f8a4ac9f --- /dev/null +++ b/src/test/java/org/spongepowered/common/data/key/KeysTest.java @@ -0,0 +1,125 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.common.data.key; + +import io.leangen.geantyref.GenericTypeReflector; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.spongepowered.api.ResourceKeyed; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.Keys; +import org.spongepowered.api.data.type.ShieldDamageReduction; +import org.spongepowered.api.data.type.ShieldItemDamageFunction; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.effect.sound.SoundTypes; +import org.spongepowered.api.event.cause.entity.damage.DamageTypes; +import org.spongepowered.api.item.ItemTypes; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.tag.DamageTypeTags; +import org.spongepowered.api.util.Ticks; + +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Stream; + +public class KeysTest { + + private static Stream testSingleKeys() { + return Stream.of( + Arguments.of(Keys.WEAPON_DAMAGE_PER_ATTACK, 5), + Arguments.of(Keys.DISABLE_SHIELD_TICKS, Ticks.of(10)), + Arguments.of(Keys.SHIELD_DEPLOY_TICKS, Ticks.of(15)), + Arguments.of(Keys.DISABLE_SHIELD_TICKS_SCALE, 2.5), + Arguments.of(Keys.SHIELD_DAMAGE_REDUCTIONS, List.of(ShieldDamageReduction.of(ShieldDamageReduction.MultiplyAdd.builder() + .horizontalBlockingAngle(45) + .constantReduction(2) + .fractionalReduction(0.5) + .damageTypes(Set.of(DamageTypes.ARROW.get(), DamageTypes.PLAYER_ATTACK.get())) + .build()))), + Arguments.of(Keys.SHIELD_ITEM_DAMAGE_FUNCTION, ShieldItemDamageFunction.of(ShieldItemDamageFunction.MultiplyAdd.builder() + .constantDamage(5) + .fractionalDamage(2) + .minAttackDamage(2.5) + .build())), + Arguments.of(Keys.SHIELD_BLOCK_SOUND, SoundTypes.ENTITY_SHULKER_HURT.get()), + Arguments.of(Keys.SHIELD_DISABLE_SOUND, SoundTypes.ENTITY_ENDER_DRAGON_DEATH.get()) + ); + } + + @MethodSource + @ParameterizedTest + void testSingleKeys(Key k, Object value) { + testSingleKeyUnchecked(k, value); + } + + @Test + void testBypassDamageTag() { + testSingleKey(Keys.BYPASS_DAMAGE_TAG, DamageTypeTags.BYPASSES_ARMOR, ResourceKeyed::key); + } + + @Test + void testAllWeaponKeys() { + final var stack = ItemStack.builder() + .itemType(ItemTypes.DIAMOND_SWORD) + .add(Keys.WEAPON_DAMAGE_PER_ATTACK, 5) + .add(Keys.DISABLE_SHIELD_TICKS, Ticks.of(5)) + .build(); + + Assertions.assertEquals(5, stack.require(Keys.WEAPON_DAMAGE_PER_ATTACK)); + Assertions.assertEquals(Ticks.of(5), stack.require(Keys.DISABLE_SHIELD_TICKS)); + + stack.remove(Keys.DISABLE_SHIELD_TICKS); + Assertions.assertEquals(5, stack.require(Keys.WEAPON_DAMAGE_PER_ATTACK)); + + stack.remove(Keys.WEAPON_DAMAGE_PER_ATTACK); + + Assertions.assertNull(stack.getOrNull(Keys.WEAPON_DAMAGE_PER_ATTACK)); + Assertions.assertNull(stack.getOrNull(Keys.DISABLE_SHIELD_TICKS)); + } + + @SuppressWarnings("unchecked") + private static > void testSingleKeyUnchecked(Key k, Object value) { + if (!GenericTypeReflector.isSuperType(k.elementType(), value.getClass())) { + throw new IllegalArgumentException("Invalid value type for key " + k.key() + ": " + value.getClass().getName()); + } + + testSingleKey((Key) k, (T) value, a -> a); + } + + private static > void testSingleKey(Key k, T value, Function equalityExtractor) { + final var stack = ItemStack.builder() + .itemType(ItemTypes.DIAMOND_SWORD) + .add(k, value) + .build(); + + Assertions.assertEquals(equalityExtractor.apply(value), equalityExtractor.apply(stack.require(k)), () -> "retrieved value is not equal " + + "to the original for " + k.key().asString()); + } + +}