diff --git a/src/main/java/com/minelittlepony/unicopia/ability/UnicornCastingAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/UnicornCastingAbility.java index 12f702d9..0b7b5a06 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/UnicornCastingAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/UnicornCastingAbility.java @@ -11,6 +11,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.item.AmuletItem; +import com.minelittlepony.unicopia.item.ChargeableItem; import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.util.TraceHelper; import com.minelittlepony.unicopia.util.VecHelper; @@ -89,13 +90,13 @@ public class UnicornCastingAbility implements Ability { if (amulet.getResult().isAccepted()) { ItemStack stack = amulet.getValue(); - AmuletItem item = (AmuletItem)stack.getItem(); + ChargeableItem item = (ChargeableItem)stack.getItem(); if (item.canCharge(stack)) { float amount = -Math.min(player.getMagicalReserves().getMana().get(), item.getChargeRemainder(stack)); if (amount < 0) { - AmuletItem.consumeEnergy(stack, amount); + ChargeableItem.consumeEnergy(stack, amount); player.getMagicalReserves().getMana().add(amount * player.getMagicalReserves().getMana().getMax()); player.asWorld().playSoundFromEntity(null, player.asEntity(), USounds.ITEM_AMULET_RECHARGE, SoundCategory.PLAYERS, 1, 1); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/ThrowableSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/ThrowableSpell.java index 0ba42ac1..f0f8bc15 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/ThrowableSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/ThrowableSpell.java @@ -8,7 +8,6 @@ import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; import com.minelittlepony.unicopia.entity.UEntities; -import com.minelittlepony.unicopia.item.GemstoneItem; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; @@ -59,7 +58,7 @@ public final class ThrowableSpell extends AbstractDelegatingSpell { MagicProjectileEntity projectile = UEntities.MAGIC_BEAM.create(world); projectile.setPosition(entity.getX(), entity.getEyeY() - 0.1F, entity.getZ()); projectile.setOwner(entity); - projectile.setItem(GemstoneItem.enchant(UItems.GEMSTONE.getDefaultStack(), spell.getType())); + projectile.setItem(UItems.GEMSTONE.getDefaultStack(spell.getType())); projectile.getSpellSlot().put(spell); projectile.setVelocity(entity, entity.getPitch(), entity.getYaw(), 0, 1.5F, divergance); projectile.setNoGravity(true); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/IngredientWithSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/IngredientWithSpell.java index 797cbe6e..c2494f83 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/IngredientWithSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/IngredientWithSpell.java @@ -9,7 +9,7 @@ import org.jetbrains.annotations.Nullable; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; -import com.minelittlepony.unicopia.item.GemstoneItem; +import com.minelittlepony.unicopia.item.EnchantableItem; import net.minecraft.item.ItemStack; import net.minecraft.network.PacketByteBuf; @@ -32,7 +32,7 @@ public class IngredientWithSpell implements Predicate { @Override public boolean test(ItemStack t) { boolean stackMatch = stack.map(m -> m.test(t)).orElse(true); - boolean spellMatch = spell.map(m -> GemstoneItem.getSpellKey(t).equals(m)).orElse(true); + boolean spellMatch = spell.map(m -> EnchantableItem.getSpellKey(t).equals(m)).orElse(true); return stackMatch && spellMatch; } @@ -41,7 +41,7 @@ public class IngredientWithSpell implements Predicate { stacks = stack.stream() .map(Ingredient::getMatchingStacks) .flatMap(Arrays::stream) - .map(stack -> spell.map(spell -> GemstoneItem.enchant(stack, spell)).orElse(stack)) + .map(stack -> spell.map(spell -> EnchantableItem.enchant(stack, spell)).orElse(stack)) .toArray(ItemStack[]::new); } return stacks; diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellCraftingRecipe.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellCraftingRecipe.java index b6d68ed1..52c27f78 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellCraftingRecipe.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellCraftingRecipe.java @@ -8,7 +8,7 @@ import com.google.gson.JsonObject; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.container.inventory.SpellbookInventory; -import com.minelittlepony.unicopia.item.GemstoneItem; +import com.minelittlepony.unicopia.item.EnchantableItem; import com.minelittlepony.unicopia.item.URecipes; import com.minelittlepony.unicopia.util.InventoryUtil; import com.mojang.datafixers.util.Pair; @@ -137,7 +137,7 @@ public class SpellCraftingRecipe implements SpellbookRecipe { SpellType spell = SpellType.getKey(Identifier.tryParse(JsonHelper.getString(json, "spell", ""))); if (spell != SpellType.EMPTY_KEY) { - return GemstoneItem.enchant(stack, spell); + return EnchantableItem.enchant(stack, spell); } return stack; } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellDuplicatingRecipe.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellDuplicatingRecipe.java index 89635d45..3c77049c 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellDuplicatingRecipe.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellDuplicatingRecipe.java @@ -29,7 +29,7 @@ public class SpellDuplicatingRecipe implements SpellbookRecipe { public void buildCraftingTree(CraftingTreeBuilder builder) { ItemStack[] spells = SpellType.REGISTRY.stream() .filter(SpellType::isObtainable) - .map(type -> GemstoneItem.enchant(UItems.GEMSTONE.getDefaultStack(), type)) + .map(UItems.GEMSTONE::getDefaultStack) .toArray(ItemStack[]::new); builder.input(UItems.BOTCHED_GEM.getDefaultStack()); builder.input(spells); @@ -47,16 +47,16 @@ public class SpellDuplicatingRecipe implements SpellbookRecipe { return InventoryUtil.stream(inventory) .limit(inventory.size() - 1) .filter(i -> !i.isEmpty()) - .noneMatch(i -> !i.isOf(UItems.GEMSTONE) || !GemstoneItem.isEnchanted(i)) + .noneMatch(i -> !i.isOf(UItems.GEMSTONE) || !EnchantableItem.isEnchanted(i)) && material.test(stack) - && !GemstoneItem.isEnchanted(stack); + && !EnchantableItem.isEnchanted(stack); } @Override public ItemStack craft(SpellbookInventory inventory) { return InventoryUtil.stream(inventory) .filter(i -> i.isOf(UItems.GEMSTONE)) - .filter(GemstoneItem::isEnchanted) + .filter(EnchantableItem::isEnchanted) .map(stack -> stack.copy()) .map(stack -> { stack.setCount(2); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellEnhancingRecipe.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellEnhancingRecipe.java index 13070456..4e9879cf 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellEnhancingRecipe.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellEnhancingRecipe.java @@ -37,7 +37,7 @@ public class SpellEnhancingRecipe implements SpellbookRecipe { @Override public boolean matches(SpellbookInventory inventory, World world) { ItemStack stack = inventory.getItemToModify(); - return material.test(stack) && GemstoneItem.isEnchanted(stack); + return material.test(stack) && EnchantableItem.isEnchanted(stack); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellShapedCraftingRecipe.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellShapedCraftingRecipe.java new file mode 100644 index 00000000..cbc2f641 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellShapedCraftingRecipe.java @@ -0,0 +1,48 @@ +package com.minelittlepony.unicopia.ability.magic.spell.crafting; + +import com.google.gson.JsonObject; +import com.minelittlepony.unicopia.item.EnchantableItem; +import com.minelittlepony.unicopia.item.URecipes; +import com.minelittlepony.unicopia.util.InventoryUtil; + +import net.minecraft.inventory.CraftingInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.recipe.RecipeSerializer; +import net.minecraft.recipe.ShapedRecipe; +import net.minecraft.util.Identifier; + +public class SpellShapedCraftingRecipe extends ShapedRecipe { + + public SpellShapedCraftingRecipe(ShapedRecipe recipe) { + super(recipe.getId(), recipe.getGroup(), recipe.getCategory(), recipe.getWidth(), recipe.getHeight(), recipe.getIngredients(), recipe.getOutput()); + } + + @Override + public ItemStack craft(CraftingInventory inventory) { + return InventoryUtil.stream(inventory) + .filter(stack -> stack.getItem() instanceof EnchantableItem) + .filter(EnchantableItem::isEnchanted) + .map(stack -> ((EnchantableItem)stack.getItem()).getSpellEffect(stack)) + .findFirst() + .map(spell -> spell.traits().applyTo(EnchantableItem.enchant(super.craft(inventory), spell.type()))) + .orElseGet(() -> super.craft(inventory)); + } + + @Override + public RecipeSerializer getSerializer() { + return URecipes.CRAFTING_MAGICAL_SERIALIZER; + } + + public static class Serializer extends ShapedRecipe.Serializer { + @Override + public ShapedRecipe read(Identifier id, JsonObject json) { + return new SpellShapedCraftingRecipe(super.read(id, json)); + } + + @Override + public ShapedRecipe read(Identifier id, PacketByteBuf buffer) { + return new SpellShapedCraftingRecipe(super.read(id, buffer)); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java index 93e1823a..add82ba7 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java @@ -17,7 +17,6 @@ import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell; import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.ThrowableSpell; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; -import com.minelittlepony.unicopia.item.GemstoneItem; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.util.RegistryUtils; @@ -92,7 +91,7 @@ public final class SpellType implements Affine, SpellPredicate< this.factory = factory; this.traits = traits; traited = new CustomisedSpellType<>(this, traits); - defaultStack = GemstoneItem.enchant(UItems.GEMSTONE.getDefaultStack(), this); + defaultStack = UItems.GEMSTONE.getDefaultStack(this); } public boolean isObtainable() { diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index a43009db..c23f0271 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -17,7 +17,7 @@ import com.minelittlepony.unicopia.client.render.*; import com.minelittlepony.unicopia.client.render.entity.*; import com.minelittlepony.unicopia.entity.UEntities; import com.minelittlepony.unicopia.item.ChameleonItem; -import com.minelittlepony.unicopia.item.GemstoneItem; +import com.minelittlepony.unicopia.item.EnchantableItem; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.particle.UParticles; @@ -117,11 +117,17 @@ public interface URenderers { PolearmRenderer.register(UItems.DIAMOND_POLEARM); PolearmRenderer.register(UItems.NETHERITE_POLEARM); ModelPredicateProviderRegistry.register(UItems.GEMSTONE, new Identifier("affinity"), (stack, world, entity, seed) -> { - return GemstoneItem.isEnchanted(stack) ? 1 + GemstoneItem.getSpellKey(stack).getAffinity().ordinal() : 0; + return EnchantableItem.isEnchanted(stack) ? 1 + EnchantableItem.getSpellKey(stack).getAffinity().ordinal() : 0; }); ColorProviderRegistry.ITEM.register((stack, i) -> { - return i > 0 || !GemstoneItem.isEnchanted(stack) ? -1 : GemstoneItem.getSpellKey(stack).getColor(); + return i > 0 || !EnchantableItem.isEnchanted(stack) ? -1 : EnchantableItem.getSpellKey(stack).getColor(); }, UItems.GEMSTONE); + ColorProviderRegistry.ITEM.register((stack, i) -> { + if (i == 1 && EnchantableItem.isEnchanted(stack)) { + return EnchantableItem.getSpellKey(stack).getColor(); + } + return -1; + }, UItems.MAGIC_STAFF); BlockColorProvider tintedProvider = (state, view, pos, color) -> { if (view == null || pos == null) { diff --git a/src/main/java/com/minelittlepony/unicopia/entity/UTradeOffers.java b/src/main/java/com/minelittlepony/unicopia/entity/UTradeOffers.java index f723c414..1b541108 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/UTradeOffers.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/UTradeOffers.java @@ -5,7 +5,7 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.effect.UPotions; -import com.minelittlepony.unicopia.item.GemstoneItem; +import com.minelittlepony.unicopia.item.EnchantableItem; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.util.RegistryUtils; @@ -43,7 +43,7 @@ public interface UTradeOffers { TradeOfferHelper.registerWanderingTraderOffers(1, factories -> { factories.add(buyTiered(UItems.GEMSTONE, 30, UItems.GOLDEN_FEATHER, 1, UItems.GOLDEN_WING, 1, 30, 2, 0.05F)); - factories.add((e, rng) -> new TradeOffer(new ItemStack(UItems.GEMSTONE, 3), GemstoneItem.enchant(UItems.GEMSTONE.getDefaultStack(), SpellType.REGISTRY.getRandom(rng).get().value()), 20, 1, 0.05F)); + factories.add((e, rng) -> new TradeOffer(new ItemStack(UItems.GEMSTONE, 3), EnchantableItem.enchant(UItems.GEMSTONE.getDefaultStack(), SpellType.REGISTRY.getRandom(rng).get().value()), 20, 1, 0.05F)); factories.add(buy(UItems.GEMSTONE, 20, UItems.HAY_FRIES, 5, 50, 3, 0.06F)); factories.add(buy(Items.WHEAT, 17, UItems.HAY_BURGER, 1, 10, 6, 0.08F)); factories.add(buy(ItemTags.SMALL_FLOWERS, 2, UItems.DAFFODIL_DAISY_SANDWICH, 1, 10, 6, 0.08F)); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java index d53a20ed..2bdd554a 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java @@ -3,7 +3,7 @@ package com.minelittlepony.unicopia.entity.player; import com.google.common.collect.Streams; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; -import com.minelittlepony.unicopia.item.GemstoneItem; +import com.minelittlepony.unicopia.item.EnchantableItem; import com.minelittlepony.unicopia.util.NbtSerialisable; import net.minecraft.nbt.NbtCompound; @@ -36,8 +36,8 @@ public class PlayerCharmTracker implements NbtSerialisable { public TypedActionResult> getSpellInHand(Hand hand) { return Streams.stream(pony.asEntity().getHandItems()) - .filter(GemstoneItem::isEnchanted) - .map(stack -> GemstoneItem.consumeSpell(stack, pony.asEntity(), null)) + .filter(EnchantableItem::isEnchanted) + .map(stack -> EnchantableItem.consumeSpell(stack, pony.asEntity(), null)) .findFirst() .orElse(getEquippedSpell(hand).toAction()); } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java index 31a933f3..0f819cbf 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -12,6 +12,7 @@ import com.minelittlepony.unicopia.entity.*; import com.minelittlepony.unicopia.entity.duck.LivingEntityDuck; import com.minelittlepony.unicopia.entity.player.MagicReserves.Bar; import com.minelittlepony.unicopia.item.AmuletItem; +import com.minelittlepony.unicopia.item.ChargeableItem; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.particle.*; @@ -389,9 +390,9 @@ public class PlayerPhysics extends EntityPhysics implements Tickab minDamage *= 3; } - AmuletItem.consumeEnergy(stack, energyConsumed); + ChargeableItem.consumeEnergy(stack, energyConsumed); - if (AmuletItem.getEnergy(stack) < 9) { + if (ChargeableItem.getEnergy(stack) < 9) { entity.playSound(USounds.ITEM_ICARUS_WINGS_WARN, 0.13F, 0.5F); } diff --git a/src/main/java/com/minelittlepony/unicopia/item/AmuletItem.java b/src/main/java/com/minelittlepony/unicopia/item/AmuletItem.java index 1defd4d5..110f60b1 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/AmuletItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/AmuletItem.java @@ -27,7 +27,7 @@ import net.minecraft.text.Text; import net.minecraft.util.Formatting; import net.minecraft.world.World; -public class AmuletItem extends WearableItem { +public class AmuletItem extends WearableItem implements ChargeableItem { private final int maxEnergy; @@ -57,7 +57,7 @@ public class AmuletItem extends WearableItem { } if (isChargable()) { - list.add(Text.translatable("item.unicopia.amulet.energy", (int)Math.floor(getEnergy(stack)), maxEnergy)); + list.add(Text.translatable("item.unicopia.amulet.energy", (int)Math.floor(ChargeableItem.getEnergy(stack)), getMaxCharge())); } } @@ -73,7 +73,7 @@ public class AmuletItem extends WearableItem { @Override public boolean hasGlint(ItemStack stack) { - return !isChargable() || stack.hasEnchantments() || getEnergy(stack) > 0; + return !isChargable() || stack.hasEnchantments() || ChargeableItem.getEnergy(stack) > 0; } @Override @@ -82,13 +82,18 @@ public class AmuletItem extends WearableItem { } public boolean isApplicable(ItemStack stack) { - return stack.getItem() == this && (!isChargable() || getEnergy(stack) > 0); + return stack.getItem() == this && (!isChargable() || ChargeableItem.getEnergy(stack) > 0); } public boolean isApplicable(LivingEntity entity) { return isApplicable(getForEntity(entity)); } + @Override + public int getMaxCharge() { + return maxEnergy; + } + public static ItemStack getForEntity(LivingEntity entity) { return TrinketsDelegate.getInstance().getEquipped(entity, TrinketsDelegate.NECKLACE) .filter(stack -> stack.getItem() instanceof AmuletItem) @@ -96,34 +101,6 @@ public class AmuletItem extends WearableItem { .orElse(ItemStack.EMPTY); } - public boolean isChargable() { - return maxEnergy > 0; - } - - public boolean canCharge(ItemStack stack) { - return isChargable() && getEnergy(stack) < maxEnergy; - } - - public float getChargeRemainder(ItemStack stack) { - return Math.max(0, maxEnergy - getEnergy(stack)); - } - - public static void consumeEnergy(ItemStack stack, float amount) { - setEnergy(stack, getEnergy(stack) - amount); - } - - public static float getEnergy(ItemStack stack) { - return stack.hasNbt() && stack.getNbt().contains("energy") ? stack.getNbt().getFloat("energy") : 0; - } - - public static void setEnergy(ItemStack stack, float energy) { - if (energy <= 0) { - stack.removeSubNbt("energy"); - } else { - stack.getOrCreateNbt().putFloat("energy", energy); - } - } - public static class ModifiersBuilder { private static final UUID SLOT_UUID = UUID.fromString("9F3D476D-C118-4544-8365-64846904B48E"); diff --git a/src/main/java/com/minelittlepony/unicopia/item/ChargeableItem.java b/src/main/java/com/minelittlepony/unicopia/item/ChargeableItem.java new file mode 100644 index 00000000..b5edcce2 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/ChargeableItem.java @@ -0,0 +1,56 @@ +package com.minelittlepony.unicopia.item; + +import net.minecraft.item.ItemStack; + +public interface ChargeableItem { + + int getMaxCharge(); + + default int getDefaultCharge() { + return 0; + } + + default boolean isChargable() { + return getMaxCharge() > 0; + } + + default boolean hasCharge(ItemStack stack) { + return getEnergy(stack) > 0; + } + + default ItemStack recharge(ItemStack stack) { + return setEnergy(stack, getMaxCharge()); + } + + default boolean canCharge(ItemStack stack) { + return isChargable() && getEnergy(stack) < getMaxCharge(); + } + + default float getChargeRemainder(ItemStack stack) { + return Math.max(0, getMaxCharge() - getEnergy(stack)); + } + + default void onDischarge(ItemStack stack) { + + } + + static void consumeEnergy(ItemStack stack, float amount) { + setEnergy(stack, getEnergy(stack) - amount); + if (stack.getItem() instanceof ChargeableItem c) { + c.onDischarge(stack); + } + } + + static float getEnergy(ItemStack stack) { + return stack.hasNbt() && stack.getNbt().contains("energy") ? stack.getNbt().getFloat("energy") : (stack.getItem() instanceof ChargeableItem c) ? c.getDefaultCharge() : 0; + } + + static ItemStack setEnergy(ItemStack stack, float energy) { + if (energy <= 0) { + stack.removeSubNbt("energy"); + } else { + stack.getOrCreateNbt().putFloat("energy", energy); + } + return stack; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/EnchantableItem.java b/src/main/java/com/minelittlepony/unicopia/item/EnchantableItem.java new file mode 100644 index 00000000..b068fa0d --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/EnchantableItem.java @@ -0,0 +1,86 @@ +package com.minelittlepony.unicopia.item; + +import java.util.function.Predicate; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.Affinity; +import com.minelittlepony.unicopia.ability.magic.spell.Spell; +import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; +import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; +import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemConvertible; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; +import net.minecraft.util.TypedActionResult; + +public interface EnchantableItem extends ItemConvertible { + + default ItemStack getDefaultStack(SpellType spell) { + return enchant(asItem().getDefaultStack(), spell); + } + + default CustomisedSpellType getSpellEffect(ItemStack stack) { + return EnchantableItem.getSpellKey(stack).withTraits(SpellTraits.of(stack)); + } + + static TypedActionResult> consumeSpell(ItemStack stack, PlayerEntity player, @Nullable Predicate> filter) { + + if (!isEnchanted(stack)) { + return TypedActionResult.pass(null); + } + + SpellType key = EnchantableItem.getSpellKey(stack); + + if (key.isEmpty()) { + return TypedActionResult.fail(null); + } + + CustomisedSpellType result = key.withTraits(SpellTraits.of(stack)); + + if (filter != null && !filter.test(result)) { + return TypedActionResult.fail(null); + } + + if (!player.world.isClient) { + player.swingHand(player.getStackInHand(Hand.OFF_HAND) == stack ? Hand.OFF_HAND : Hand.MAIN_HAND); + + if (stack.getCount() == 1) { + unenchant(stack); + } else { + player.giveItemStack(unenchant(stack.split(1))); + } + } + + return TypedActionResult.consume(result); + } + + static boolean isEnchanted(ItemStack stack) { + return !stack.isEmpty() && stack.hasNbt() && stack.getNbt().contains("spell"); + } + + static ItemStack enchant(ItemStack stack, SpellType type) { + return enchant(stack, type, type.getAffinity()); + } + + static ItemStack enchant(ItemStack stack, SpellType type, Affinity affinity) { + if (type.isEmpty()) { + return unenchant(stack); + } + stack.getOrCreateNbt().putString("spell", type.getId().toString()); + return type.getTraits().applyTo(stack); + } + + static ItemStack unenchant(ItemStack stack) { + stack.removeSubNbt("spell"); + stack.removeSubNbt("spell_traits"); + return stack; + } + + static SpellType getSpellKey(ItemStack stack) { + return SpellType.getKey(isEnchanted(stack) ? new Identifier(stack.getNbt().getString("spell")) : SpellType.EMPTY_ID); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/EnchantedStaffItem.java b/src/main/java/com/minelittlepony/unicopia/item/EnchantedStaffItem.java new file mode 100644 index 00000000..67ac4723 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/EnchantedStaffItem.java @@ -0,0 +1,206 @@ +package com.minelittlepony.unicopia.item; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nullable; + +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.SpellPredicate; +import com.minelittlepony.unicopia.ability.magic.spell.Spell; +import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; +import com.minelittlepony.unicopia.entity.CastSpellEntity; +import com.minelittlepony.unicopia.entity.UEntities; +import com.minelittlepony.unicopia.entity.player.Pony; + +import net.minecraft.client.item.TooltipContext; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.particle.ParticleTypes; +import net.minecraft.sound.SoundCategory; +import net.minecraft.sound.SoundEvents; +import net.minecraft.text.Text; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.TypedActionResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class EnchantedStaffItem extends StaffItem implements EnchantableItem, ChargeableItem { + + private static final Map, SpellType> ENTITY_TYPE_TO_SPELL = new HashMap<>(); + public static SpellType register(EntityType entityType, SpellType spellType) { + ENTITY_TYPE_TO_SPELL.put(entityType, spellType); + return spellType; + } + + public static SpellType getSpellType(Entity entity) { + if (entity instanceof CastSpellEntity cast) { + return cast.getSpellSlot().get(c -> !SpellPredicate.IS_PLACED.test(c), true).map(Spell::getType).orElse(SpellType.empty()); + } + if (entity instanceof PlayerEntity player) { + return Pony.of(player).getCharms().getEquippedSpell(Hand.MAIN_HAND).type(); + } + return ENTITY_TYPE_TO_SPELL.getOrDefault(entity.getType(), SpellType.empty()); + } + + static { + register(EntityType.DROWNED, SpellType.BUBBLE); + register(EntityType.DOLPHIN, SpellType.BUBBLE); + register(EntityType.BLAZE, SpellType.FIRE_BOLT); + register(EntityType.CHICKEN, SpellType.FEATHER_FALL); + register(EntityType.CREEPER, SpellType.CATAPULT); + register(EntityType.HUSK, SpellType.HYDROPHOBIC); + register(EntityType.SNOW_GOLEM, SpellType.FROST); + register(EntityType.FIREBALL, SpellType.FLAME); + register(EntityType.SMALL_FIREBALL, SpellType.FLAME); + register(EntityType.ENDER_DRAGON, SpellType.DISPLACEMENT); + register(EntityType.GUARDIAN, SpellType.AWKWARD); + register(EntityType.ELDER_GUARDIAN, SpellType.AWKWARD); + register(EntityType.DRAGON_FIREBALL, SpellType.INFERNAL); + register(EntityType.CAVE_SPIDER, SpellType.REVEALING); + register(EntityType.ZOMBIE, SpellType.NECROMANCY); + register(EntityType.VEX, SpellType.NECROMANCY); + register(EntityType.SKELETON, SpellType.CATAPULT); + register(EntityType.WITHER_SKELETON, SpellType.CATAPULT); + register(EntityType.SKELETON_HORSE, SpellType.CATAPULT); + register(UEntities.TWITTERMITE, SpellType.LIGHT); + } + + public EnchantedStaffItem(Settings settings) { + super(settings.maxDamage(500)); + } + + @Override + public ItemStack getDefaultStack() { + return EnchantableItem.enchant(super.getDefaultStack(), SpellType.FIRE_BOLT); + } + + @Override + public void appendTooltip(ItemStack stack, @Nullable World world, List lines, TooltipContext context) { + + if (EnchantableItem.isEnchanted(stack)) { + SpellType key = EnchantableItem.getSpellKey(stack); + lines.add(Text.translatable(key.getTranslationKey()).formatted(key.getAffinity().getColor())); + lines.add(Text.translatable(getTranslationKey(stack) + ".charges", (int)Math.floor(ChargeableItem.getEnergy(stack)), getMaxCharge())); + } + } + + @Override + public ActionResult useOnEntity(ItemStack stack, PlayerEntity player, LivingEntity target, Hand hand) { + if (EnchantableItem.isEnchanted(stack)) { + return ActionResult.PASS; + } + + super.useOnEntity(stack, player, target, hand); + + SpellType type = getSpellType(target); + if (!type.isEmpty()) { + target.setHealth(1); + target.setFrozenTicks(9000); + player.setStackInHand(hand, recharge(EnchantableItem.enchant(stack, type))); + } + return ActionResult.SUCCESS; + } + + @Override + public TypedActionResult use(World world, PlayerEntity player, Hand hand) { + ItemStack itemstack = player.getStackInHand(hand); + player.setCurrentHand(hand); + return TypedActionResult.consume(itemstack); + } + + @Override + public void onStoppedUsing(ItemStack stack, World world, LivingEntity entity, int timeLeft) { + int i = getMaxUseTime(stack) - timeLeft; + + if (EnchantableItem.isEnchanted(stack) && hasCharge(stack)) { + if (i > 20) { + Pony.of(entity).ifPresent(pony -> { + pony.subtractEnergyCost(4); + stack.damage(1, pony.asEntity(), p -> p.sendEquipmentBreakStatus(EquipmentSlot.MAINHAND)); + getSpellEffect(stack).create().toThrowable().throwProjectile(pony); + }); + ChargeableItem.consumeEnergy(stack, 1); + } else if (i > 5) { + Pony.of(entity).ifPresent(pony -> { + pony.subtractEnergyCost(4); + stack.damage(1, pony.asEntity(), p -> p.sendEquipmentBreakStatus(EquipmentSlot.MAINHAND)); + getSpellEffect(stack).create().toThrowable().throwProjectile(pony); + }); + ChargeableItem.consumeEnergy(stack, 1); + } + } + } + + @Override + protected boolean castContainedEffect(ItemStack stack, LivingEntity target, LivingEntity attacker) { + if (attacker.isSneaking() && hasCharge(stack)) { + stack.damage(50, attacker, p -> p.sendEquipmentBreakStatus(EquipmentSlot.MAINHAND)); + Caster.of(attacker).ifPresent(c -> c.subtractEnergyCost(4)); + Caster.of(target).ifPresent(c -> getSpellEffect(stack).create().apply(c)); + ChargeableItem.consumeEnergy(stack, 1); + + return true; + } + + return false; + } + + @Override + public void usageTick(World world, LivingEntity entity, ItemStack stack, int ticksRemaining) { + if (entity instanceof LivingEntity) { + LivingEntity living = entity; + + if (living.getActiveItem().getItem() == this) { + Vec3d eyes = entity.getCameraPosVec(1); + + float i = getMaxUseTime(stack) - ticksRemaining; + + world.addParticle(i > 150 ? ParticleTypes.LARGE_SMOKE : ParticleTypes.CLOUD, eyes.x, eyes.y, eyes.z, + (world.random.nextGaussian() - 0.5) / 10, + (world.random.nextGaussian() - 0.5) / 10, + (world.random.nextGaussian() - 0.5) / 10 + ); + world.playSound(null, entity.getBlockPos(), SoundEvents.ENTITY_GUARDIAN_ATTACK, SoundCategory.PLAYERS, 1, i / 20); + + if (i > 200) { + living.clearActiveItem(); + living.damage(DamageSource.MAGIC, 1); + if (EnchantableItem.isEnchanted(stack) && hasCharge(stack)) { + Caster.of(entity).ifPresent(c -> getSpellEffect(stack).create().apply(c)); + ChargeableItem.consumeEnergy(stack, 1); + } + } + } + } + } + + @Override + public int getMaxUseTime(ItemStack stack) { + return 72000; + } + + @Override + public int getMaxCharge() { + return 3; + } + + @Override + public int getDefaultCharge() { + return 3; + } + + @Override + public void onDischarge(ItemStack stack) { + if ((stack.hasNbt() && stack.getNbt().contains("energy") ? stack.getNbt().getFloat("energy") : 0) == 0) { + EnchantableItem.unenchant(stack); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/GemstoneItem.java b/src/main/java/com/minelittlepony/unicopia/item/GemstoneItem.java index 4d04759e..aa0d6fde 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/GemstoneItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/GemstoneItem.java @@ -8,10 +8,8 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.Unicopia; -import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; -import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.client.FlowingText; import com.minelittlepony.unicopia.entity.player.PlayerCharmTracker; import com.minelittlepony.unicopia.entity.player.Pony; @@ -25,11 +23,10 @@ import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; import net.minecraft.util.Hand; -import net.minecraft.util.Identifier; import net.minecraft.util.TypedActionResult; import net.minecraft.world.World; -public class GemstoneItem extends Item implements MultiItem { +public class GemstoneItem extends Item implements MultiItem, EnchantableItem { public GemstoneItem(Settings settings) { super(settings); @@ -43,16 +40,16 @@ public class GemstoneItem extends Item implements MultiItem { ItemStack stack = user.getStackInHand(hand); PlayerCharmTracker charms = Pony.of(user).getCharms(); - TypedActionResult> spell = consumeSpell(stack, user, ((Predicate>)charms.getEquippedSpell(hand)::equals).negate()); + TypedActionResult> spell = EnchantableItem.consumeSpell(stack, user, ((Predicate>)charms.getEquippedSpell(hand)::equals).negate()); CustomisedSpellType existing = charms.getEquippedSpell(hand); if (!existing.isEmpty()) { if (stack.getCount() == 1) { - stack = existing.traits().applyTo(enchant(stack, existing.type())); + stack = existing.traits().applyTo(EnchantableItem.enchant(stack, existing.type())); } else { - user.giveItemStack(existing.traits().applyTo(enchant(stack.split(1), existing.type()))); + user.giveItemStack(existing.traits().applyTo(EnchantableItem.enchant(stack.split(1), existing.type()))); } } @@ -76,8 +73,8 @@ public class GemstoneItem extends Item implements MultiItem { public void appendTooltip(ItemStack stack, @Nullable World world, List lines, TooltipContext tooltipContext) { super.appendTooltip(stack, world, lines, tooltipContext); - if (isEnchanted(stack)) { - SpellType key = getSpellKey(stack); + if (EnchantableItem.isEnchanted(stack)) { + SpellType key = EnchantableItem.getSpellKey(stack); MutableText line = Text.translatable(key.getTranslationKey() + ".lore").formatted(key.getAffinity().getColor()); @@ -94,81 +91,26 @@ public class GemstoneItem extends Item implements MultiItem { return Arrays.stream(Affinity.VALUES) .flatMap(i -> SpellType.byAffinity(i).stream() .filter(type -> type.isObtainable()) - .map(type -> enchant(getDefaultStack(), type, i)) + .map(type -> EnchantableItem.enchant(getDefaultStack(), type, i)) ) .toList(); } @Override public boolean hasGlint(ItemStack stack) { - return super.hasGlint(stack) || (Unicopia.SIDE.getPlayerSpecies().canCast() && isEnchanted(stack)); + return super.hasGlint(stack) || (Unicopia.SIDE.getPlayerSpecies().canCast() && EnchantableItem.isEnchanted(stack)); } @Override public Text getName(ItemStack stack) { - if (isEnchanted(stack)) { + if (EnchantableItem.isEnchanted(stack)) { if (!Unicopia.SIDE.getPlayerSpecies().canCast()) { return Text.translatable(getTranslationKey(stack) + ".obfuscated"); } - return Text.translatable(getTranslationKey(stack) + ".enchanted", getSpellKey(stack).getName()); + return Text.translatable(getTranslationKey(stack) + ".enchanted", EnchantableItem.getSpellKey(stack).getName()); } return super.getName(); } - public static TypedActionResult> consumeSpell(ItemStack stack, PlayerEntity player, @Nullable Predicate> filter) { - - if (!isEnchanted(stack)) { - return TypedActionResult.pass(null); - } - - SpellType key = getSpellKey(stack); - - if (key.isEmpty()) { - return TypedActionResult.fail(null); - } - - CustomisedSpellType result = key.withTraits(SpellTraits.of(stack)); - - if (filter != null && !filter.test(result)) { - return TypedActionResult.fail(null); - } - - if (!player.world.isClient) { - player.swingHand(player.getStackInHand(Hand.OFF_HAND) == stack ? Hand.OFF_HAND : Hand.MAIN_HAND); - - if (stack.getCount() == 1) { - unenchant(stack); - } else { - player.giveItemStack(unenchant(stack.split(1))); - } - } - - return TypedActionResult.consume(result); - } - - public static boolean isEnchanted(ItemStack stack) { - return !stack.isEmpty() && stack.hasNbt() && stack.getNbt().contains("spell"); - } - - public static ItemStack enchant(ItemStack stack, SpellType type) { - return enchant(stack, type, type.getAffinity()); - } - - public static ItemStack enchant(ItemStack stack, SpellType type, Affinity affinity) { - if (type.isEmpty()) { - return unenchant(stack); - } - stack.getOrCreateNbt().putString("spell", type.getId().toString()); - return type.getTraits().applyTo(stack); - } - - public static ItemStack unenchant(ItemStack stack) { - stack.removeSubNbt("spell"); - return stack; - } - - public static SpellType getSpellKey(ItemStack stack) { - return SpellType.getKey(isEnchanted(stack) ? new Identifier(stack.getNbt().getString("spell")) : SpellType.EMPTY_ID); - } } diff --git a/src/main/java/com/minelittlepony/unicopia/item/StaffItem.java b/src/main/java/com/minelittlepony/unicopia/item/StaffItem.java new file mode 100644 index 00000000..07f46a74 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/StaffItem.java @@ -0,0 +1,99 @@ +package com.minelittlepony.unicopia.item; + +import java.util.List; +import java.util.UUID; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.minelittlepony.unicopia.entity.UEntityAttributes; + +import net.minecraft.block.Blocks; +import net.minecraft.client.item.TooltipContext; +import net.minecraft.entity.EntityDimensions; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.attribute.EntityAttribute; +import net.minecraft.entity.attribute.EntityAttributeModifier; +import net.minecraft.entity.attribute.EntityAttributeModifier.Operation; +import net.minecraft.entity.attribute.EntityAttributes; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.SwordItem; +import net.minecraft.item.ToolMaterials; +import net.minecraft.particle.BlockStateParticleEffect; +import net.minecraft.particle.ParticleTypes; +import net.minecraft.sound.SoundEvents; +import net.minecraft.text.Text; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Formatting; +import net.minecraft.util.Hand; +import net.minecraft.util.UseAction; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.World; + +public class StaffItem extends SwordItem { + + protected static final UUID ATTACK_REACH_MODIFIER = UUID.fromString("FA235E1C-4280-A865-B01B-CBAE9985ACA3"); + + public StaffItem(Settings settings) { + super(ToolMaterials.WOOD, 2, 4, settings); + } + + @Override + public ActionResult useOnEntity(ItemStack stack, PlayerEntity player, LivingEntity target, Hand hand) { + World w = player.getEntityWorld(); + + EntityDimensions dims = target.getDimensions(target.getPose()); + + for (int i = 0; i < 130; i++) { + w.addParticle(new BlockStateParticleEffect(ParticleTypes.BLOCK, Blocks.OAK_LOG.getDefaultState()), + target.getX() + (target.world.random.nextFloat() - 0.5F) * (dims.width + 1), + (target.getY() + dims.height / 2) + (target.world.random.nextFloat() - 0.5F) * dims.height, + target.getZ() + (target.world.random.nextFloat() - 0.5F) * (dims.width + 1), + 0, 0, 0 + ); + } + + return ActionResult.SUCCESS; + } + + @Override + public void appendTooltip(ItemStack stack, World world, List tooltip, TooltipContext context) { + tooltip.add(Text.translatable(getTranslationKey(stack) + ".lore").formatted(Formatting.GRAY)); + } + + @Override + public boolean postHit(ItemStack stack, LivingEntity entity, LivingEntity attacker) { + super.postHit(stack, entity, attacker); + + return castContainedEffect(stack, entity, attacker); + } + + protected boolean castContainedEffect(ItemStack stack, LivingEntity target, LivingEntity attacker) { + target.getEntityWorld().playSound(null, target.getBlockPos(), SoundEvents.ENTITY_PLAYER_ATTACK_CRIT, attacker.getSoundCategory(), 1, 1); + + target.takeKnockback(attacker.getVelocity().subtract(target.getVelocity()).horizontalLength(), + MathHelper.sin(attacker.getYaw() * 0.017453292F), + -MathHelper.cos(attacker.getYaw() * 0.017453292F) + ); + + return true; + } + + @Override + public UseAction getUseAction(ItemStack stack) { + return UseAction.BOW; + } + + @Override + public Multimap getAttributeModifiers(EquipmentSlot slot) { + Multimap multimap = HashMultimap.create(); + + if (slot == EquipmentSlot.MAINHAND) { + multimap.put(EntityAttributes.GENERIC_ATTACK_DAMAGE, new EntityAttributeModifier(ATTACK_DAMAGE_MODIFIER_ID, "Weapon modifier", getAttackDamage(), Operation.ADDITION)); + multimap.put(UEntityAttributes.EXTENDED_ATTACK_DISTANCE, new EntityAttributeModifier(ATTACK_REACH_MODIFIER, "Weapon modifier", 3, Operation.ADDITION)); + } + + return multimap; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java index 99272021..896ae225 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java +++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java @@ -56,7 +56,7 @@ public interface UItems { Item CRYSTAL_HEART = register("crystal_heart", new CrystalHeartItem(new Item.Settings().maxCount(1)), ItemGroups.TOOLS); Item CRYSTAL_SHARD = register("crystal_shard", new Item(new Item.Settings()), ItemGroups.NATURAL); - Item GEMSTONE = register("gemstone", new GemstoneItem(new Item.Settings()), ItemGroups.NATURAL); + GemstoneItem GEMSTONE = register("gemstone", new GemstoneItem(new Item.Settings()), ItemGroups.NATURAL); Item BOTCHED_GEM = register("botched_gem", new Item(new Item.Settings()), ItemGroups.NATURAL); Item PEGASUS_FEATHER = register("pegasus_feather", new Item(new Item.Settings()), ItemGroups.NATURAL); @@ -99,6 +99,9 @@ public interface UItems { Item GOLDEN_WING = register("golden_wing", new Item(new Item.Settings().rarity(Rarity.UNCOMMON)), ItemGroups.NATURAL); Item DRAGON_BREATH_SCROLL = register("dragon_breath_scroll", new DragonBreathScrollItem(new Item.Settings().rarity(Rarity.UNCOMMON)), ItemGroups.TOOLS); + Item GROGARS_BELL = register("grogars_bell", new Item(new Item.Settings().rarity(Rarity.RARE)), ItemGroups.TOOLS); + Item MEADOWBROOKS_STAFF = register("meadowbrooks_staff", new StaffItem(new Settings().rarity(Rarity.UNCOMMON).maxCount(1).maxDamage(120)), ItemGroups.TOOLS); + Item MAGIC_STAFF = register("magic_staff", new EnchantedStaffItem(new Settings().rarity(Rarity.UNCOMMON).maxCount(1).maxDamage(120)), ItemGroups.TOOLS); Item WOODEN_POLEARM = register("wooden_polearm", new PolearmItem(ToolMaterials.WOOD, 2, -3.6F, 2, new Item.Settings()), ItemGroups.COMBAT); Item STONE_POLEARM = register("stone_polearm", new PolearmItem(ToolMaterials.STONE, 2, -3.6F, 2, new Item.Settings()), ItemGroups.COMBAT); diff --git a/src/main/java/com/minelittlepony/unicopia/item/URecipes.java b/src/main/java/com/minelittlepony/unicopia/item/URecipes.java index 7c1a72c1..21ef9f4c 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/URecipes.java +++ b/src/main/java/com/minelittlepony/unicopia/item/URecipes.java @@ -10,6 +10,7 @@ import net.minecraft.loot.LootTable; import net.minecraft.recipe.Ingredient; import net.minecraft.recipe.RecipeSerializer; import net.minecraft.recipe.RecipeType; +import net.minecraft.recipe.ShapedRecipe; import net.minecraft.recipe.ShapelessRecipe; import net.minecraft.recipe.SpecialRecipeSerializer; import net.minecraft.util.Identifier; @@ -21,6 +22,7 @@ public interface URecipes { RecipeSerializer ZAP_APPLE_SERIALIZER = RecipeSerializer.register("unicopia:crafting_zap_apple", new ZapAppleRecipe.Serializer()); RecipeSerializer GLOWING_SERIALIZER = RecipeSerializer.register("unicopia:crafting_glowing", new SpecialRecipeSerializer<>(GlowingRecipe::new)); RecipeSerializer JAR_INSERT_SERIALIZER = RecipeSerializer.register("unicopia:jar_insert", new SpecialRecipeSerializer<>(JarInsertRecipe::new)); + RecipeSerializer CRAFTING_MAGICAL_SERIALIZER = RecipeSerializer.register("unicopia:crafting_magical", new SpellShapedCraftingRecipe.Serializer()); RecipeSerializer TRAIT_REQUIREMENT = RecipeSerializer.register("unicopia:spellbook/crafting", new SpellCraftingRecipe.Serializer()); RecipeSerializer TRAIT_COMBINING = RecipeSerializer.register("unicopia:spellbook/combining", new SpellEnhancingRecipe.Serializer()); RecipeSerializer SPELL_DUPLICATING = RecipeSerializer.register("unicopia:spellbook/duplicating", new SpellDuplicatingRecipe.Serializer()); diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index ccd79266..ccac1db6 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -92,6 +92,11 @@ "item.unicopia.alicorn_amulet": "Alicorn Amulet", "item.unicopia.alicorn_amulet.lore": "Time worn: %d", + + "item.unicopia.magic_staff": "Magic Staff", + "item.unicopia.magic_staff.charges": "Charges: %d / %d", + "item.unicopia.meadowbrooks_staff": "Meadowbrook's Staff", + "item.unicopia.meadowbrooks_staff.lore": "A Heavy Stick", "item.unicopia.wooden_polearm": "Wooden Polearm", "item.unicopia.stone_polearm": "Stone Polearm", diff --git a/src/main/resources/assets/unicopia/models/item/handheld_staff.json b/src/main/resources/assets/unicopia/models/item/handheld_staff.json new file mode 100644 index 00000000..6a51bd98 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/handheld_staff.json @@ -0,0 +1,25 @@ +{ + "parent": "item/handheld", + "display": { + "thirdperson_righthand": { + "rotation": [ 0, 90, 55 ], + "translation": [ 0, 4.0, 2.5 ], + "scale": [ 1.25, 1.25, 1.25 ] + }, + "thirdperson_lefthand": { + "rotation": [ 0, -90, -55 ], + "translation": [ 0, 4.0, 2.5 ], + "scale": [ 1.25, 1.25, 1.25 ] + }, + "firstperson_righthand": { + "rotation": [ 0, 90, 25 ], + "translation": [ 0, 1.9, 0.8 ], + "scale": [ 0.88, 0.88, 0.88 ] + }, + "firstperson_lefthand": { + "rotation": [ 0, -90, -25 ], + "translation": [ 0, 1.9, 0.8 ], + "scale": [ 0.88, 0.88, 0.88 ] + } + } +} diff --git a/src/main/resources/assets/unicopia/models/item/magic_staff.json b/src/main/resources/assets/unicopia/models/item/magic_staff.json new file mode 100644 index 00000000..c484178a --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/magic_staff.json @@ -0,0 +1,7 @@ +{ + "parent": "unicopia:item/handheld_staff", + "textures": { + "layer0": "unicopia:item/magic_staff_base", + "layer1": "unicopia:item/magic_staff_magic" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/item/meadowbrooks_staff.json b/src/main/resources/assets/unicopia/models/item/meadowbrooks_staff.json new file mode 100644 index 00000000..1a3489bb --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/meadowbrooks_staff.json @@ -0,0 +1,6 @@ +{ + "parent": "unicopia:item/handheld_staff", + "textures": { + "layer0": "unicopia:item/meadowbrooks_staff" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/textures/item/magic_staff_base.png b/src/main/resources/assets/unicopia/textures/item/magic_staff_base.png new file mode 100644 index 00000000..9e8318d8 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/magic_staff_base.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/magic_staff_magic.png b/src/main/resources/assets/unicopia/textures/item/magic_staff_magic.png new file mode 100644 index 00000000..63f9240c Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/magic_staff_magic.png differ diff --git a/src/main/resources/assets/unicopia/textures/item/meadowbrooks_staff.png b/src/main/resources/assets/unicopia/textures/item/meadowbrooks_staff.png new file mode 100644 index 00000000..230eedf5 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/meadowbrooks_staff.png differ diff --git a/src/main/resources/data/unicopia/advancements/recipes/meadowbrooks_staff.json b/src/main/resources/data/unicopia/advancements/recipes/meadowbrooks_staff.json new file mode 100644 index 00000000..26462f67 --- /dev/null +++ b/src/main/resources/data/unicopia/advancements/recipes/meadowbrooks_staff.json @@ -0,0 +1,30 @@ +{ + "parent": "minecraft:recipes/root", + "rewards": { + "recipes": [ + "unicopia:meadowbrooks_staff" + ] + }, + "criteria": { + "has_ingredients": { + "trigger": "minecraft:inventory_changed", + "conditions": { + "items": [ + { "item": "minecraft:stick" } + ] + } + }, + "has_the_recipe": { + "trigger": "minecraft:recipe_unlocked", + "conditions": { + "recipe": "unicopia:meadowbrooks_staff" + } + } + }, + "requirements": [ + [ + "has_ingredients", + "has_the_recipe" + ] + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/recipes/magic_staff.json b/src/main/resources/data/unicopia/recipes/magic_staff.json new file mode 100644 index 00000000..9ce441a0 --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/magic_staff.json @@ -0,0 +1,19 @@ +{ + "type": "unicopia:crafting_magical", + "pattern": [ + " o", + " # ", + "# " + ], + "key": { + "#": { + "item": "minecraft:stick" + }, + "o": { + "item": "unicopia:gemstone" + } + }, + "result": { + "item": "unicopia:magic_staff" + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/recipes/meadowbrooks_staff.json b/src/main/resources/data/unicopia/recipes/meadowbrooks_staff.json new file mode 100644 index 00000000..5382d9ce --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/meadowbrooks_staff.json @@ -0,0 +1,16 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + " #", + " # ", + "# " + ], + "key": { + "#": { + "item": "minecraft:stick" + } + }, + "result": { + "item": "unicopia:meadowbrooks_staff" + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/tags/items/magic_spell_base.json b/src/main/resources/data/unicopia/tags/items/magic_spell_base.json new file mode 100644 index 00000000..d3e5b431 --- /dev/null +++ b/src/main/resources/data/unicopia/tags/items/magic_spell_base.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "unicopia:gemstone", + "unicopia:magic_staff" + ] +}