Added magic staffs:

3 sticks on a diagonal = meadowbrooks_staff
3 sticks + gem (optionally with spell) on a diagnoal = magic_staff
This commit is contained in:
Sollace 2023-02-28 16:44:14 +00:00
parent 072f18d6f9
commit 8959c67d53
31 changed files with 667 additions and 127 deletions

View file

@ -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<Hit> {
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);
}

View file

@ -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);

View file

@ -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<ItemStack> {
@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<ItemStack> {
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;

View file

@ -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;
}

View file

@ -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);

View file

@ -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

View file

@ -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));
}
}
}

View file

@ -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<T extends Spell> 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() {

View file

@ -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) {

View file

@ -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));

View file

@ -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<CustomisedSpellType<?>> 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());
}

View file

@ -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<PlayerEntity> 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);
}

View file

@ -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");

View file

@ -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;
}
}

View file

@ -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<CustomisedSpellType<?>> consumeSpell(ItemStack stack, PlayerEntity player, @Nullable Predicate<CustomisedSpellType<?>> filter) {
if (!isEnchanted(stack)) {
return TypedActionResult.pass(null);
}
SpellType<Spell> 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 <T extends Spell> SpellType<T> getSpellKey(ItemStack stack) {
return SpellType.getKey(isEnchanted(stack) ? new Identifier(stack.getNbt().getString("spell")) : SpellType.EMPTY_ID);
}
}

View file

@ -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<EntityType<?>, SpellType<?>> ENTITY_TYPE_TO_SPELL = new HashMap<>();
public static <T extends Spell> SpellType<T> register(EntityType<?> entityType, SpellType<T> 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<Text> 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<ItemStack> 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);
}
}
}

View file

@ -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<CustomisedSpellType<?>> spell = consumeSpell(stack, user, ((Predicate<CustomisedSpellType<?>>)charms.getEquippedSpell(hand)::equals).negate());
TypedActionResult<CustomisedSpellType<?>> spell = EnchantableItem.consumeSpell(stack, user, ((Predicate<CustomisedSpellType<?>>)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<Text> 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<CustomisedSpellType<?>> consumeSpell(ItemStack stack, PlayerEntity player, @Nullable Predicate<CustomisedSpellType<?>> filter) {
if (!isEnchanted(stack)) {
return TypedActionResult.pass(null);
}
SpellType<Spell> 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 <T extends Spell> SpellType<T> getSpellKey(ItemStack stack) {
return SpellType.getKey(isEnchanted(stack) ? new Identifier(stack.getNbt().getString("spell")) : SpellType.EMPTY_ID);
}
}

View file

@ -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<Text> 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<EntityAttribute, EntityAttributeModifier> getAttributeModifiers(EquipmentSlot slot) {
Multimap<EntityAttribute, EntityAttributeModifier> 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;
}
}

View file

@ -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);

View file

@ -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<ShapelessRecipe> ZAP_APPLE_SERIALIZER = RecipeSerializer.register("unicopia:crafting_zap_apple", new ZapAppleRecipe.Serializer());
RecipeSerializer<GlowingRecipe> GLOWING_SERIALIZER = RecipeSerializer.register("unicopia:crafting_glowing", new SpecialRecipeSerializer<>(GlowingRecipe::new));
RecipeSerializer<JarInsertRecipe> JAR_INSERT_SERIALIZER = RecipeSerializer.register("unicopia:jar_insert", new SpecialRecipeSerializer<>(JarInsertRecipe::new));
RecipeSerializer<ShapedRecipe> CRAFTING_MAGICAL_SERIALIZER = RecipeSerializer.register("unicopia:crafting_magical", new SpellShapedCraftingRecipe.Serializer());
RecipeSerializer<SpellCraftingRecipe> TRAIT_REQUIREMENT = RecipeSerializer.register("unicopia:spellbook/crafting", new SpellCraftingRecipe.Serializer());
RecipeSerializer<SpellEnhancingRecipe> TRAIT_COMBINING = RecipeSerializer.register("unicopia:spellbook/combining", new SpellEnhancingRecipe.Serializer());
RecipeSerializer<SpellDuplicatingRecipe> SPELL_DUPLICATING = RecipeSerializer.register("unicopia:spellbook/duplicating", new SpellDuplicatingRecipe.Serializer());

View file

@ -93,6 +93,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",
"item.unicopia.iron_polearm": "Iron Polearm",

View file

@ -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 ]
}
}
}

View file

@ -0,0 +1,7 @@
{
"parent": "unicopia:item/handheld_staff",
"textures": {
"layer0": "unicopia:item/magic_staff_base",
"layer1": "unicopia:item/magic_staff_magic"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "unicopia:item/handheld_staff",
"textures": {
"layer0": "unicopia:item/meadowbrooks_staff"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -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"
]
]
}

View file

@ -0,0 +1,19 @@
{
"type": "unicopia:crafting_magical",
"pattern": [
" o",
" # ",
"# "
],
"key": {
"#": {
"item": "minecraft:stick"
},
"o": {
"item": "unicopia:gemstone"
}
},
"result": {
"item": "unicopia:magic_staff"
}
}

View file

@ -0,0 +1,16 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
" #",
" # ",
"# "
],
"key": {
"#": {
"item": "minecraft:stick"
}
},
"result": {
"item": "unicopia:meadowbrooks_staff"
}
}

View file

@ -0,0 +1,7 @@
{
"replace": false,
"values": [
"unicopia:gemstone",
"unicopia:magic_staff"
]
}