Implement enchantment effects

This commit is contained in:
Sollace 2024-10-01 16:22:48 +01:00
parent b033d1dc7a
commit 1601c14d95
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
30 changed files with 409 additions and 350 deletions

View file

@ -6,8 +6,7 @@ import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.entity.Equine; import com.minelittlepony.unicopia.entity.Equine;
import com.minelittlepony.unicopia.entity.MagicImmune; import com.minelittlepony.unicopia.entity.MagicImmune;
import com.minelittlepony.unicopia.item.enchantment.WantItNeedItEnchantment; import com.minelittlepony.unicopia.item.enchantment.EnchantmentUtil;
import net.minecraft.entity.*; import net.minecraft.entity.*;
import net.minecraft.entity.decoration.AbstractDecorationEntity; import net.minecraft.entity.decoration.AbstractDecorationEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
@ -44,7 +43,7 @@ public interface EquinePredicates {
Predicate<Entity> EXCEPT_MAGIC_IMMUNE = IS_MAGIC_IMMUNE.negate(); Predicate<Entity> EXCEPT_MAGIC_IMMUNE = IS_MAGIC_IMMUNE.negate();
Predicate<Entity> VALID_LIVING_AND_NOT_MAGIC_IMMUNE = EntityPredicates.VALID_LIVING_ENTITY.and(EXCEPT_MAGIC_IMMUNE); Predicate<Entity> VALID_LIVING_AND_NOT_MAGIC_IMMUNE = EntityPredicates.VALID_LIVING_ENTITY.and(EXCEPT_MAGIC_IMMUNE);
Predicate<LivingEntity> LIVING_HAS_WANT_IT_NEED_IT = e -> WantItNeedItEnchantment.getLevel(e) > 0; Predicate<LivingEntity> LIVING_HAS_WANT_IT_NEED_IT = e -> EnchantmentUtil.getWantItNeedItLevel(e) > 0;
Predicate<Entity> VALID_FOR_DISGUISE = EntityPredicates.EXCEPT_SPECTATOR.and(e -> !(e instanceof LightningEntity || e instanceof AbstractDecorationEntity)); Predicate<Entity> VALID_FOR_DISGUISE = EntityPredicates.EXCEPT_SPECTATOR.and(e -> !(e instanceof LightningEntity || e instanceof AbstractDecorationEntity));
static Predicate<Entity> ofRace(Race race) { static Predicate<Entity> ofRace(Race race) {

View file

@ -29,6 +29,7 @@ public class InteractionManager {
public static final int SOUND_MAGIC_BEAM = 5; public static final int SOUND_MAGIC_BEAM = 5;
public static final int SOUND_HEART_BEAT = 6; public static final int SOUND_HEART_BEAT = 6;
public static final int SOUND_KIRIN_RAGE = 7; public static final int SOUND_KIRIN_RAGE = 7;
public static final int SOUND_GEM_FINDING_MAGIC_HUM = 8;
public static final int SCREEN_DISPELL_ABILITY = 0; public static final int SCREEN_DISPELL_ABILITY = 0;

View file

@ -65,6 +65,7 @@ public enum Trait implements CommandArgumentEnum<Trait> {
private static final Trait[] VALUES = values(); private static final Trait[] VALUES = values();
private static final Map<Identifier, Trait> IDS = Arrays.stream(values()).collect(Collectors.toMap(Trait::getId, Function.identity())); private static final Map<Identifier, Trait> IDS = Arrays.stream(values()).collect(Collectors.toMap(Trait::getId, Function.identity()));
@SuppressWarnings("deprecation")
private static final EnumCodec<Trait> NAME_CODEC = StringIdentifiable.createCodec(Trait::values, n -> n.toLowerCase(Locale.ROOT)); private static final EnumCodec<Trait> NAME_CODEC = StringIdentifiable.createCodec(Trait::values, n -> n.toLowerCase(Locale.ROOT));
public static final Codec<Trait> CODEC = Identifier.CODEC.xmap(id -> IDS.get(id), Trait::getId); public static final Codec<Trait> CODEC = Identifier.CODEC.xmap(id -> IDS.get(id), Trait::getId);
public static final Codec<Set<Trait>> SET_CODEC = CODEC.listOf().xmap( public static final Codec<Set<Trait>> SET_CODEC = CODEC.listOf().xmap(

View file

@ -101,7 +101,7 @@ public class HiveBlock extends ConnectingBlock implements BlockEntityProvider {
} }
@Override @Override
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) { protected BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) {
if (state.get(CONSUMING)) { if (state.get(CONSUMING)) {
return state; return state;
} }
@ -116,7 +116,7 @@ public class HiveBlock extends ConnectingBlock implements BlockEntityProvider {
} }
@Override @Override
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) { protected void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
if (state.get(CONSUMING) || !state.get(AWAKE)) { if (state.get(CONSUMING) || !state.get(AWAKE)) {
return; return;
} }
@ -142,7 +142,7 @@ public class HiveBlock extends ConnectingBlock implements BlockEntityProvider {
} }
@Override @Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) { protected ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
if (EquineContext.of(player).getCompositeRace().includes(Race.CHANGELING)) { if (EquineContext.of(player).getCompositeRace().includes(Race.CHANGELING)) {
world.setBlockState(pos, state.with(CONSUMING, true)); world.setBlockState(pos, state.with(CONSUMING, true));
if (!world.isClient) { if (!world.isClient) {
@ -171,15 +171,13 @@ public class HiveBlock extends ConnectingBlock implements BlockEntityProvider {
super.neighborUpdate(state, world, pos, sourceBlock, sourcePos, notify); super.neighborUpdate(state, world, pos, sourceBlock, sourcePos, notify);
} }
@Deprecated
@Override @Override
public VoxelShape getCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { protected VoxelShape getCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return EquineContext.of(context).getSpecies() == Race.CHANGELING ? VoxelShapes.empty() : super.getCollisionShape(state, world, pos, context); return EquineContext.of(context).getSpecies() == Race.CHANGELING ? VoxelShapes.empty() : super.getCollisionShape(state, world, pos, context);
} }
@Deprecated
@Override @Override
public float calcBlockBreakingDelta(BlockState state, PlayerEntity player, BlockView world, BlockPos pos) { protected float calcBlockBreakingDelta(BlockState state, PlayerEntity player, BlockView world, BlockPos pos) {
float delta = super.calcBlockBreakingDelta(state, player, world, pos); float delta = super.calcBlockBreakingDelta(state, player, world, pos);
delta *= Pony.of(player).getSpecies() == Race.CHANGELING ? 2 : 1; delta *= Pony.of(player).getSpecies() == Race.CHANGELING ? 2 : 1;
return delta; return delta;
@ -213,6 +211,7 @@ public class HiveBlock extends ConnectingBlock implements BlockEntityProvider {
listener = new Listener(pos); listener = new Listener(pos);
} }
@SuppressWarnings("deprecation")
@Override @Override
public void readNbt(NbtCompound nbt, WrapperLookup lookup) { public void readNbt(NbtCompound nbt, WrapperLookup lookup) {
opening = nbt.getBoolean("opening"); opening = nbt.getBoolean("opening");
@ -225,6 +224,7 @@ public class HiveBlock extends ConnectingBlock implements BlockEntityProvider {
} }
} }
@SuppressWarnings("deprecation")
@Override @Override
protected void writeNbt(NbtCompound nbt, WrapperLookup lookup) { protected void writeNbt(NbtCompound nbt, WrapperLookup lookup) {
nbt.putBoolean("opening", opening); nbt.putBoolean("opening", opening);
@ -312,14 +312,15 @@ public class HiveBlock extends ConnectingBlock implements BlockEntityProvider {
} }
record Entry (BlockPos pos, BlockState state, @Nullable BlockEntity data) { record Entry (BlockPos pos, BlockState state, @Nullable BlockEntity data) {
@SuppressWarnings("deprecation")
public static final Serializer<NbtCompound, Entry> SERIALIZER = Serializer.of((compound, lookup) -> new Entry( public static final Serializer<NbtCompound, Entry> SERIALIZER = Serializer.of((compound, lookup) -> new Entry(
NbtSerialisable.BLOCK_POS.read(compound.getCompound("pos"), lookup), NbtSerialisable.decode(BlockPos.CODEC, compound.getCompound("pos")).orElse(BlockPos.ORIGIN),
NbtSerialisable.decode(BlockState.CODEC, compound.get("state")).orElse(Blocks.AIR.getDefaultState()), NbtSerialisable.decode(BlockState.CODEC, compound.get("state")).orElse(Blocks.AIR.getDefaultState()),
compound.getCompound("data"), compound.getCompound("data"),
lookup lookup
), (entry, lookup) -> { ), (entry, lookup) -> {
NbtCompound compound = new NbtCompound(); NbtCompound compound = new NbtCompound();
compound.put("pos", NbtSerialisable.BLOCK_POS.write(entry.pos(), lookup)); compound.put("pos", NbtSerialisable.encode(BlockPos.CODEC, entry.pos()));
compound.put("state", NbtSerialisable.encode(BlockState.CODEC, entry.state())); compound.put("state", NbtSerialisable.encode(BlockState.CODEC, entry.state()));
if (entry.data() != null) { if (entry.data() != null) {
compound.put("data", entry.data().createNbtWithId(lookup)); compound.put("data", entry.data().createNbtWithId(lookup));

View file

@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.block.jar;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.USounds;
@ -12,6 +11,7 @@ import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.mixin.MixinEntityBucketItem; import com.minelittlepony.unicopia.mixin.MixinEntityBucketItem;
import com.minelittlepony.unicopia.util.FluidHelper; import com.minelittlepony.unicopia.util.FluidHelper;
import com.minelittlepony.unicopia.util.NbtSerialisable; import com.minelittlepony.unicopia.util.NbtSerialisable;
import com.mojang.serialization.Codec;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
import net.minecraft.block.Block; import net.minecraft.block.Block;
@ -20,7 +20,6 @@ import net.minecraft.inventory.SidedInventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.Items; import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.registry.RegistryWrapper.WrapperLookup; import net.minecraft.registry.RegistryWrapper.WrapperLookup;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.Pair; import net.minecraft.util.Pair;
@ -33,15 +32,14 @@ public record ItemsJarContents (
) implements JarContents, SidedInventory { ) implements JarContents, SidedInventory {
private static final int MAX_SIZE = 16; private static final int MAX_SIZE = 16;
private static final int[] SLOTS = IntStream.range(0, MAX_SIZE).toArray(); private static final int[] SLOTS = IntStream.range(0, MAX_SIZE).toArray();
private static final Codec<List<ItemStack>> STACKS_CODEC = ItemStack.CODEC.listOf(0, MAX_SIZE);
public ItemsJarContents(TileData tile) { public ItemsJarContents(TileData tile) {
this(tile, new ArrayList<>(MAX_SIZE)); this(tile, new ArrayList<>(MAX_SIZE));
} }
public ItemsJarContents(TileData tile, NbtCompound compound, WrapperLookup lookup) { public ItemsJarContents(TileData tile, NbtCompound compound, WrapperLookup lookup) {
this(tile, NbtSerialisable.ITEM_STACK.readAll(compound.getList("items", NbtElement.COMPOUND_TYPE), lookup) this(tile, new ArrayList<>(NbtSerialisable.decode(STACKS_CODEC, compound.get("items")).orElse(List.of())));
.limit(MAX_SIZE)
.collect(Collectors.toList()));
} }
@Override @Override
@ -199,7 +197,7 @@ public record ItemsJarContents (
@Override @Override
public NbtCompound toNBT(NbtCompound compound, WrapperLookup lookup) { public NbtCompound toNBT(NbtCompound compound, WrapperLookup lookup) {
compound.put("items", NbtSerialisable.ITEM_STACK.writeAll(stacks, lookup)); compound.put("items", NbtSerialisable.encode(STACKS_CODEC, stacks));
return compound; return compound;
} }

View file

@ -1,6 +1,8 @@
package com.minelittlepony.unicopia.client; package com.minelittlepony.unicopia.client;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -18,6 +20,7 @@ import com.minelittlepony.unicopia.client.gui.spellbook.ClientChapters;
import com.minelittlepony.unicopia.client.particle.ClientBoundParticleSpawner; import com.minelittlepony.unicopia.client.particle.ClientBoundParticleSpawner;
import com.minelittlepony.unicopia.client.sound.*; import com.minelittlepony.unicopia.client.sound.*;
import com.minelittlepony.unicopia.container.SpellbookChapter; import com.minelittlepony.unicopia.container.SpellbookChapter;
import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.player.PlayerPhysics; import com.minelittlepony.unicopia.entity.player.PlayerPhysics;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.entity.player.dummy.DummyClientPlayerEntity; import com.minelittlepony.unicopia.entity.player.dummy.DummyClientPlayerEntity;
@ -47,10 +50,10 @@ import net.minecraft.util.math.random.Random;
import net.minecraft.world.World; import net.minecraft.world.World;
public class ClientInteractionManager extends InteractionManager { public class ClientInteractionManager extends InteractionManager {
private final MinecraftClient client = MinecraftClient.getInstance(); private final MinecraftClient client = MinecraftClient.getInstance();
private final Int2ObjectMap<WeakReference<TickableSoundInstance>> playingSounds = new Int2ObjectOpenHashMap<>(); private final Int2ObjectMap<WeakReference<TickableSoundInstance>> playingSounds = new Int2ObjectOpenHashMap<>();
private final Map<UUID, Int2ObjectMap<WeakReference<TickableSoundInstance>>> entitySounds = new HashMap<>();
@Override @Override
public SpellbookChapter readChapter(PacketByteBuf buffer) { public SpellbookChapter readChapter(PacketByteBuf buffer) {
@ -99,11 +102,23 @@ public class ClientInteractionManager extends InteractionManager {
return EquinePredicates.RAGING.test(source); return EquinePredicates.RAGING.test(source);
} }
}); });
} else if (type == SOUND_GEM_FINDING_MAGIC_HUM) {
play(source.getUuid(), type, () -> {
return new MagicAuraSoundInstance(source.getSoundCategory(), Living.living(source), source.getWorld().getRandom());
});
} }
}); });
} }
private void play(UUID sourceId, int type, Supplier<TickableSoundInstance> soundSupplier) {
play(entitySounds.computeIfAbsent(sourceId, id -> new Int2ObjectOpenHashMap<>()), type, soundSupplier);
}
private void play(int type, Supplier<TickableSoundInstance> soundSupplier) { private void play(int type, Supplier<TickableSoundInstance> soundSupplier) {
play(playingSounds, type, soundSupplier);
}
private void play(Int2ObjectMap<WeakReference<TickableSoundInstance>> playingSounds, int type, Supplier<TickableSoundInstance> soundSupplier) {
WeakReference<TickableSoundInstance> activeSound = playingSounds.get(type); WeakReference<TickableSoundInstance> activeSound = playingSounds.get(type);
TickableSoundInstance existing; TickableSoundInstance existing;
if (activeSound == null || (existing = activeSound.get()) == null || existing.isDone()) { if (activeSound == null || (existing = activeSound.get()) == null || existing.isDone()) {

View file

@ -2,8 +2,7 @@ package com.minelittlepony.unicopia.client.render;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.entity.Creature; import com.minelittlepony.unicopia.entity.Creature;
import com.minelittlepony.unicopia.item.enchantment.WantItNeedItEnchantment; import com.minelittlepony.unicopia.item.enchantment.EnchantmentUtil;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.model.Dilation; import net.minecraft.client.model.Dilation;
import net.minecraft.client.model.ModelData; import net.minecraft.client.model.ModelData;
@ -57,6 +56,6 @@ public class SmittenEyesRenderer {
} }
public boolean isSmitten(Creature pony) { public boolean isSmitten(Creature pony) {
return pony.isSmitten() || WantItNeedItEnchantment.getLevel(pony.asEntity()) > 0; return pony.isSmitten() || EnchantmentUtil.getWantItNeedItLevel(pony.asEntity()) > 0;
} }
} }

View file

@ -1,10 +1,13 @@
package com.minelittlepony.unicopia.client.sound; package com.minelittlepony.unicopia.client.sound;
import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.item.enchantment.EnchantmentUtil;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.random.Random; import net.minecraft.util.math.random.Random;
public class MagicAuraSoundInstance extends FadeOutSoundInstance { public class MagicAuraSoundInstance extends FadeOutSoundInstance {
@ -12,7 +15,7 @@ public class MagicAuraSoundInstance extends FadeOutSoundInstance {
private final Living<?> living; private final Living<?> living;
public MagicAuraSoundInstance(SoundCategory category, Living<?> living, Random random) { public MagicAuraSoundInstance(SoundCategory category, Living<?> living, Random random) {
super(USounds.ITEM_MAGIC_AURA, category, 1, random); super(USounds.ITEM_MAGIC_AURA, category, 0.1F, random);
this.relative = false; this.relative = false;
this.living = living; this.living = living;
setPosition(living.getOriginVector()); setPosition(living.getOriginVector());
@ -20,18 +23,32 @@ public class MagicAuraSoundInstance extends FadeOutSoundInstance {
@Override @Override
protected boolean shouldKeepPlaying() { protected boolean shouldKeepPlaying() {
var data = living.getEnchants().getOrEmpty(UEnchantments.GEM_FINDER); int level = EnchantmentUtil.getLevel(UEnchantments.GEM_FINDER, living.asEntity());
setPosition(living.getOriginVector()); setPosition(living.getOriginVector());
if (level <= 0 || living.asEntity().isRemoved()) {
if (!living.asEntity().isRemoved() && data.isPresent()) { return false;
float level = data.get().level;
if (level != targetVolume) {
setTargetVolume(level);
}
return true;
} }
return false; float volume = computeTargetVolume(level);
if (volume != targetVolume) {
setTargetVolume(volume);
}
return true;
}
private float computeTargetVolume(int level) {
int radius = 2 + (level * 2);
BlockPos origin = living.getOrigin();
double volume = BlockPos.findClosest(origin, radius, radius, p -> living.asWorld().getBlockState(p).isIn(UTags.Blocks.INTERESTING))
.map(p -> living.getOriginVector().squaredDistanceTo(p.getX(), p.getY(), p.getZ()))
.map(find -> (1 - (Math.sqrt(find) / radius)))
.orElse(-1D);
volume = Math.max(volume, 0.04F);
return (float)volume * (1.3F + level);
} }
} }

View file

@ -1,104 +0,0 @@
package com.minelittlepony.unicopia.entity;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import com.minelittlepony.unicopia.util.Tickable;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtList;
import net.minecraft.nbt.NbtString;
import net.minecraft.util.Identifier;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
import net.minecraft.server.world.ServerWorld;
// TODO: Use a EnchantmentLocationBasedEffect for this instead
@Deprecated
public class Enchantments implements NbtSerialisable, Tickable {
private final Living<?> entity;
private final Set<RegistryKey<Enchantment>> equippedEnchantments = new HashSet<>();
private final Map<RegistryKey<Enchantment>, Data> data = new HashMap<>();
Enchantments(Living<?> entity) {
this.entity = entity;
}
@SuppressWarnings("unchecked")
public <T extends Data> Optional<T> getOrEmpty(RegistryKey<Enchantment> enchantment) {
return Optional.ofNullable((T)data.get(enchantment));
}
@SuppressWarnings("unchecked")
public <T extends Data> T computeIfAbsent(RegistryKey<Enchantment> enchantment, Supplier<T> factory) {
return (T)data.computeIfAbsent(enchantment, e -> factory.get());
}
@Nullable
@SuppressWarnings("unchecked")
public <T extends Data> T remove(RegistryKey<Enchantment> enchantment) {
return (T)data.remove(enchantment);
}
@Override
public void tick() {
UEnchantments.REGISTRY.forEach(key -> {
var ench = entity.entryFor(key);
int level = EnchantmentHelper.getEquipmentLevel(ench, entity.asEntity());
boolean active = level > 0;
if (active != equippedEnchantments.contains(key)) {
if (active) {
equippedEnchantments.add(key);
ench.value().applyLocationBasedEffects((ServerWorld)entity.asWorld(), level, null, entity.asEntity());
} else {
equippedEnchantments.remove(key);
ench.value().removeLocationBasedEffects(level, null, entity.asEntity());
}
}
if (active) {
ench.value().onTick((ServerWorld)entity.asWorld(), level, null, entity.asEntity());
}
});
}
@Override
public void toNBT(NbtCompound compound, WrapperLookup lookup) {
NbtList list = new NbtList();
equippedEnchantments.forEach(key -> {
list.add(NbtString.of(key.getValue().toString()));
});
compound.put("enchants", list);
}
@Override
public void fromNBT(NbtCompound compound, WrapperLookup lookup) {
equippedEnchantments.clear();
if (compound.contains("enchants")) {
compound.getList("enchants", NbtElement.STRING_TYPE).forEach(tag -> {
equippedEnchantments.add(RegistryKey.of(RegistryKeys.ENCHANTMENT, Identifier.of(tag.asString())));
});
}
}
public static class Data {
public float level;
}
}

View file

@ -5,7 +5,6 @@ import java.util.List;
import com.minelittlepony.unicopia.*; import com.minelittlepony.unicopia.*;
import com.minelittlepony.unicopia.item.enchantment.EnchantmentUtil; import com.minelittlepony.unicopia.item.enchantment.EnchantmentUtil;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
import com.minelittlepony.unicopia.item.enchantment.WantItNeedItEnchantment;
import com.minelittlepony.unicopia.network.track.DataTracker; import com.minelittlepony.unicopia.network.track.DataTracker;
import com.minelittlepony.unicopia.network.track.DataTrackerManager; import com.minelittlepony.unicopia.network.track.DataTrackerManager;
import com.minelittlepony.unicopia.network.track.Trackable; import com.minelittlepony.unicopia.network.track.Trackable;
@ -56,7 +55,7 @@ public class ItemImpl implements Equine<ItemEntity> {
public boolean beforeUpdate() { public boolean beforeUpdate() {
if (!entity.getWorld().isClient) { if (!entity.getWorld().isClient) {
if (WantItNeedItEnchantment.getLevel(entity) > 0) { if (EnchantmentUtil.getWantItNeedItLevel(entity) > 0) {
var random = entity.getWorld().random; var random = entity.getWorld().random;
if (random.nextInt(15) == 0) { if (random.nextInt(15) == 0) {

View file

@ -6,6 +6,7 @@ import java.util.stream.StreamSupport;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
@ -40,6 +41,8 @@ import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.projectile.ProjectileImpactListener; import com.minelittlepony.unicopia.projectile.ProjectileImpactListener;
import com.minelittlepony.unicopia.server.world.DragonBreathStore; import com.minelittlepony.unicopia.server.world.DragonBreathStore;
import com.minelittlepony.unicopia.util.*; import com.minelittlepony.unicopia.util.*;
import com.minelittlepony.unicopia.util.serialization.NbtMap;
import com.mojang.serialization.Codec;
import net.fabricmc.fabric.api.util.TriState; import net.fabricmc.fabric.api.util.TriState;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -67,6 +70,7 @@ import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.BlockSoundGroup; import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundCategory;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util; import net.minecraft.util.Util;
import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@ -97,7 +101,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
private final List<Tickable> tickers = new ArrayList<>(); private final List<Tickable> tickers = new ArrayList<>();
private final LandingEventHandler landEvent = addTicker(new LandingEventHandler(this)); private final LandingEventHandler landEvent = addTicker(new LandingEventHandler(this));
private final Enchantments enchants = addTicker(new Enchantments(this)); private final NbtMap<Identifier, Float> enchants = NbtMap.of(Identifier.CODEC, Codec.FLOAT);
private final ItemTracker armour = addTicker(new ItemTracker(this)); private final ItemTracker armour = addTicker(new ItemTracker(this));
private final Transportation<T> transportation = new Transportation<>(this); private final Transportation<T> transportation = new Transportation<>(this);
@ -152,7 +156,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
return spells.getSlots(); return spells.getSlots();
} }
public Enchantments getEnchants() { public NbtMap<Identifier, Float> getEnchants() {
return enchants; return enchants;
} }
@ -251,6 +255,10 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
updateDragonBreath(); updateDragonBreath();
if (EnchantmentUtil.getLevel(UEnchantments.GEM_FINDER, entity) > 0) {
InteractionManager.getInstance().playLoopingSound(entity, InteractionManager.SOUND_GEM_FINDING_MAGIC_HUM, 0);
}
transportation.tick(); transportation.tick();
} }

View file

@ -5,7 +5,6 @@ import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.entity.Creature; import com.minelittlepony.unicopia.entity.Creature;
import com.minelittlepony.unicopia.item.enchantment.EnchantmentUtil; import com.minelittlepony.unicopia.item.enchantment.EnchantmentUtil;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
import com.minelittlepony.unicopia.item.enchantment.WantItNeedItEnchantment;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect; import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.particle.UParticles;
@ -40,7 +39,7 @@ public class WantItTakeItGoal extends BreakHeartGoal {
protected boolean canTarget(Entity e) { protected boolean canTarget(Entity e) {
return e != null && !e.isRemoved() && ( return e != null && !e.isRemoved() && (
(e instanceof LivingEntity l && predicate.test(mob, l) (e instanceof LivingEntity l && predicate.test(mob, l)
|| (e instanceof ItemEntity i && WantItNeedItEnchantment.getLevel(i) > 0) || (e instanceof ItemEntity i && EnchantmentUtil.getWantItNeedItLevel(i) > 0)
) )
); );
} }

View file

@ -0,0 +1,62 @@
package com.minelittlepony.unicopia.item.enchantment;
import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.util.RegistryUtils;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.enchantment.EnchantmentEffectContext;
import net.minecraft.enchantment.effect.EnchantmentEntityEffect;
import net.minecraft.entity.Entity;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvent;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.random.Random;
public record AmbientSoundsEnchantmentEffect (
Identifier id,
TagKey<SoundEvent> sounds
) implements EnchantmentEntityEffect {
public static final MapCodec<AmbientSoundsEnchantmentEffect> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
Identifier.CODEC.fieldOf("id").forGetter(AmbientSoundsEnchantmentEffect::id),
TagKey.codec(RegistryKeys.SOUND_EVENT).fieldOf("sounds").forGetter(AmbientSoundsEnchantmentEffect::sounds)
).apply(instance, AmbientSoundsEnchantmentEffect::new));
@Override
public MapCodec<? extends EnchantmentEntityEffect> getCodec() {
return CODEC;
}
@Override
public void apply(ServerWorld world, int level, EnchantmentEffectContext context, Entity entity, Vec3d pos) {
Living.getOrEmpty(entity).ifPresent(user -> {
user.getEnchants().compute(id, (id, data) -> {
if (data == null) {
data = 0F;
}
Random rng = world.getRandom();
data -= rng.nextFloat() * 0.8F;
int light = world.getLightLevel(entity.getRootVehicle().getBlockPos());
if (rng.nextInt(Math.max(1, (light * 9) + data.intValue())) == 0) {
data = (float)rng.nextInt(5000);
RegistryUtils.pickRandom(world, sounds).ifPresent(event -> {
user.asWorld().playSoundFromEntity(
null,
user.asEntity(),
event, SoundCategory.HOSTILE,
0.5F + rng.nextFloat() * 0.5F,
0.5F + rng.nextFloat() * 0.5F
);
});
}
return data;
});
});
}
}

View file

@ -1,33 +0,0 @@
package com.minelittlepony.unicopia.item.enchantment;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.entity.Enchantments;
import com.minelittlepony.unicopia.entity.Living;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.util.Identifier;
import net.minecraft.entity.attribute.EntityAttributeModifier.Operation;
public class CollaboratorEnchantment {
private static final Identifier TEAM_STRENGTH_ID = Unicopia.id("team_strength");
protected CollaboratorEnchantment() {
//addModifier(EntityAttributes.GENERIC_ATTACK_DAMAGE, this::getModifier);
}
protected boolean shouldChangeModifiers(Living<?> user, int level) {
return false;//super.shouldChangeModifiers(user, getTeamCollectiveLevel(user, 2 + (level * 2)));
}
private EntityAttributeModifier getModifier(Living<?> user, int level) {
return new EntityAttributeModifier(TEAM_STRENGTH_ID, user.getEnchants().computeIfAbsent(UEnchantments.HERDS, Enchantments.Data::new).level / 2, Operation.ADD_VALUE);
}
private static int getTeamCollectiveLevel(Living<?> user, int radius) {
return user.findAllEntitiesInRange(radius, e -> e instanceof LivingEntity)
.mapToInt(e -> EnchantmentUtil.getLevel(UEnchantments.HERDS, (LivingEntity)e))
.reduce((a, b) -> a + b)
.orElse(0);
}
}

View file

@ -20,7 +20,7 @@ import net.minecraft.sound.SoundCategory;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
public class ConsumptionEnchantment { public class ConsumptionEnchantmentUtil {
public static boolean applyConsumption(World w, BlockState state, BlockPos pos, @Nullable BlockEntity blockEntity, Entity entity, ItemStack tool) { public static boolean applyConsumption(World w, BlockState state, BlockPos pos, @Nullable BlockEntity blockEntity, Entity entity, ItemStack tool) {
if (!(w instanceof ServerWorld world)) { if (!(w instanceof ServerWorld world)) {

View file

@ -0,0 +1,47 @@
package com.minelittlepony.unicopia.item.enchantment;
import com.minelittlepony.unicopia.entity.player.MagicReserves.Bar;
import com.mojang.serialization.MapCodec;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.enchantment.EnchantmentEffectContext;
import net.minecraft.enchantment.effect.EnchantmentEntityEffect;
import net.minecraft.entity.Entity;
import net.minecraft.entity.mob.HostileEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.Vec3d;
public class DangerSensingEnchantmentEffect implements EnchantmentEntityEffect {
public static final DangerSensingEnchantmentEffect INSTANCE = new DangerSensingEnchantmentEffect();
public static final MapCodec<DangerSensingEnchantmentEffect> CODEC = MapCodec.unit(INSTANCE);
@Override
public MapCodec<DangerSensingEnchantmentEffect> getCodec() {
return CODEC;
}
@Override
public void apply(ServerWorld world, int level, EnchantmentEffectContext context, Entity entity, Vec3d pos) {
Pony.of(entity).ifPresent(pony -> {
if (pony.asEntity().age % 10 == 0) {
int range = (level + 1) * 3;
if (pony.asWorld().getEntitiesByClass(HostileEntity.class, pony.asEntity().getBoundingBox().expand(range, 0, range), enemy -> {
return enemy != null
&& enemy.canTarget(pony.asEntity())
&& enemy.canSee(pony.asEntity())
&& enemy.getTarget() == pony.asEntity();
}).isEmpty()) {
return;
}
Bar bar = pony.getMagicalReserves().getEnergy();
float targetPercent = (level / (float)pony.entryFor(UEnchantments.STRESSED).value().definition().maxLevel()) * 0.05125F;
float increase = 1F + (level * level)/100F;
if (bar.getPercentFill() < targetPercent) {
bar.add(increase);
}
}
});
}
}

View file

@ -8,6 +8,8 @@ import net.minecraft.component.type.ItemEnchantmentsComponent;
import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.enchantment.Enchantments; import net.minecraft.enchantment.Enchantments;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.effect.StatusEffect; import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.entity.effect.StatusEffects; import net.minecraft.entity.effect.StatusEffects;
@ -40,6 +42,27 @@ public interface EnchantmentUtil {
return true; return true;
} }
static boolean prefersEquipment(ItemStack newStack, ItemStack oldStack) {
int newLevel = EnchantmentUtil.getLevel(UEnchantments.WANT_IT_NEED_IT, newStack);
int oldLevel = EnchantmentUtil.getLevel(UEnchantments.WANT_IT_NEED_IT, oldStack);
return newLevel > oldLevel;
}
static int getWantItNeedItLevel(Entity entity) {
return entity instanceof LivingEntity l ? getWantItNeedItLevel(l)
: entity instanceof ItemEntity i ? getWantItNeedItLevel(i)
: 0;
}
static int getWantItNeedItLevel(ItemEntity entity) {
return getLevel(UEnchantments.WANT_IT_NEED_IT, entity.getStack());
}
static int getWantItNeedItLevel(LivingEntity entity) {
return getLevel(UEnchantments.WANT_IT_NEED_IT, entity);
}
static int getLuck(int baseline, LivingEntity entity) { static int getLuck(int baseline, LivingEntity entity) {
boolean naturallyLucky = Living.getOrEmpty(entity).filter(c -> c.getCompositeRace().canUseEarth()).isPresent(); boolean naturallyLucky = Living.getOrEmpty(entity).filter(c -> c.getCompositeRace().canUseEarth()).isPresent();
if (naturallyLucky) { if (naturallyLucky) {

View file

@ -1,37 +0,0 @@
package com.minelittlepony.unicopia.item.enchantment;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.client.sound.MagicAuraSoundInstance;
import com.minelittlepony.unicopia.entity.Enchantments;
import com.minelittlepony.unicopia.entity.Living;
import net.minecraft.client.MinecraftClient;
import net.minecraft.util.math.BlockPos;
public class GemFindingEnchantment {
public void onUserTick(Living<?> user, int level) {
int radius = 2 + (level * 2);
BlockPos origin = user.getOrigin();
double volume = BlockPos.findClosest(origin, radius, radius, pos -> user.asWorld().getBlockState(pos).isIn(UTags.Blocks.INTERESTING))
.map(p -> user.getOriginVector().squaredDistanceTo(p.getX(), p.getY(), p.getZ()))
.map(find -> (1 - (Math.sqrt(find) / radius)))
.orElse(-1D);
volume = Math.max(volume, 0.04F);
user.getEnchants().computeIfAbsent(UEnchantments.GEM_FINDER, Enchantments.Data::new).level = (float)volume * (1.3F + level);
}
public void onEquipped(Living<?> user) {
if (user.isClient()) {
MinecraftClient.getInstance().getSoundManager().play(new MagicAuraSoundInstance(user.asEntity().getSoundCategory(), user, user.asWorld().getRandom()));
}
}
public void onUnequipped(Living<?> user) {
user.getEnchants().remove(UEnchantments.GEM_FINDER).level = 0;
}
}

View file

@ -0,0 +1,53 @@
package com.minelittlepony.unicopia.item.enchantment;
import com.minelittlepony.unicopia.entity.Living;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.enchantment.EnchantmentEffectContext;
import net.minecraft.enchantment.EnchantmentLevelBasedValue;
import net.minecraft.enchantment.effect.AttributeEnchantmentEffect;
import net.minecraft.enchantment.effect.EnchantmentEntityEffect;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.util.math.Vec3d;
import net.minecraft.server.world.ServerWorld;
public record GroupBasedAttributeEnchantmentEffect (
AttributeEnchantmentEffect attribute,
EnchantmentLevelBasedValue range
) implements EnchantmentEntityEffect {
public static final MapCodec<GroupBasedAttributeEnchantmentEffect> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
AttributeEnchantmentEffect.CODEC.fieldOf("attribute").forGetter(GroupBasedAttributeEnchantmentEffect::attribute),
EnchantmentLevelBasedValue.CODEC.fieldOf("range").forGetter(GroupBasedAttributeEnchantmentEffect::range)
).apply(instance, GroupBasedAttributeEnchantmentEffect::new));
@Override
public MapCodec<GroupBasedAttributeEnchantmentEffect> getCodec() {
return CODEC;
}
@Override
public void apply(ServerWorld world, int level, EnchantmentEffectContext context, Entity user, Vec3d pos) {
Living.getOrEmpty(user).ifPresent(living -> {
int collectiveLevels = living.findAllEntitiesInRange(range.getValue(level), e -> e instanceof LivingEntity)
.mapToInt(e -> EnchantmentUtil.getLevel(UEnchantments.HERDS, (LivingEntity)e))
.reduce(Integer::sum)
.orElse(0);
living.updateAttributeModifier(attribute.id(), attribute.attribute(), attribute.amount().getValue(collectiveLevels), value -> {
return new EntityAttributeModifier(attribute.id(), value, attribute.operation());
}, false);
});
}
@Override
public void remove(EnchantmentEffectContext context, Entity user, Vec3d pos, int level) {
Living.getOrEmpty(user).ifPresent(living -> {
living.updateAttributeModifier(attribute.id(), attribute.attribute(), 0, value -> {
return new EntityAttributeModifier(attribute.id(), value, attribute.operation());
}, false);
});
}
}

View file

@ -0,0 +1,48 @@
package com.minelittlepony.unicopia.item.enchantment;
import java.util.Optional;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.particle.WeakTarget;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.enchantment.EnchantmentEffectContext;
import net.minecraft.enchantment.effect.EnchantmentEntityEffect;
import net.minecraft.entity.Entity;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.Vec3d;
public record ParticleTrailEnchantmentEntityEffect(
Optional<ParticleEffect> particle,
float followSpeed,
int density,
int probability
) implements EnchantmentEntityEffect {
public static final MapCodec<ParticleTrailEnchantmentEntityEffect> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
ParticleTypes.TYPE_CODEC.optionalFieldOf("particle").forGetter(ParticleTrailEnchantmentEntityEffect::particle),
Codec.FLOAT.fieldOf("followSpeed").forGetter(ParticleTrailEnchantmentEntityEffect::followSpeed),
Codec.INT.fieldOf("density").forGetter(ParticleTrailEnchantmentEntityEffect::density),
Codec.INT.fieldOf("probability").forGetter(ParticleTrailEnchantmentEntityEffect::probability)
).apply(instance, ParticleTrailEnchantmentEntityEffect::new));
@Override
public MapCodec<ParticleTrailEnchantmentEntityEffect> getCodec() {
return CODEC;
}
@Override
public void apply(ServerWorld world, int level, EnchantmentEffectContext context, Entity entity, Vec3d pos) {
if (probability <= 0 || world.random.nextInt(probability) == 0) {
ParticleUtils.spawnParticles(new FollowingParticleEffect(
UParticles.HEALTH_DRAIN,
new WeakTarget(entity.getCameraPosVec(1), entity), followSpeed, particle
), entity, density);
}
}
}

View file

@ -1,36 +0,0 @@
package com.minelittlepony.unicopia.item.enchantment;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.entity.Enchantments;
import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.util.RegistryUtils;
import net.minecraft.sound.SoundCategory;
import net.minecraft.util.math.random.Random;
public class PoisonedJokeEnchantment {
public void onUserTick(Living<?> user, int level) {
if (user.asWorld().isClient) {
return;
}
int light = user.asWorld().getLightLevel(user.asEntity().getRootVehicle().getBlockPos());
Random rng = user.asWorld().random;
Enchantments.Data data = user.getEnchants().computeIfAbsent(UEnchantments.POISONED_JOKE, Enchantments.Data::new);
data.level -= rng.nextFloat() * 0.8F;
if (rng.nextInt(Math.max(1, (light * 9) + (int)data.level)) == 0) {
data.level = rng.nextInt(5000);
RegistryUtils.pickRandom(user.asWorld(), UTags.Sounds.POISON_JOKE_EVENTS).ifPresent(event -> {
user.asWorld().playSoundFromEntity(
null,
user.asEntity(),
event, SoundCategory.HOSTILE,
0.5F + rng.nextFloat() * 0.5F,
0.5F + rng.nextFloat() * 0.5F
);
});
}
}
}

View file

@ -1,32 +0,0 @@
package com.minelittlepony.unicopia.item.enchantment;
import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.player.MagicReserves.Bar;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.entity.mob.HostileEntity;
public class StressfulEnchantment {
public void onUserTick(Living<?> user, int level) {
if (user instanceof Pony pony && pony.asEntity().age % 10 == 0) {
int range = (level + 1) * 3;
if (pony.asWorld().getEntitiesByClass(HostileEntity.class, user.asEntity().getBoundingBox().expand(range, 0, range), enemy -> {
return enemy != null
&& enemy.canTarget(user.asEntity())
&& enemy.canSee(user.asEntity())
&& enemy.getTarget() == user.asEntity();
}).isEmpty()) {
return;
}
Bar bar = pony.getMagicalReserves().getEnergy();
float targetPercent = (level / (float)user.entryFor(UEnchantments.STRESSED).value().definition().maxLevel()) * 0.05125F;
float increase = 1F + (level * level)/100F;
if (bar.getPercentFill() < targetPercent) {
bar.add(increase);
}
}
}
}

View file

@ -0,0 +1,21 @@
package com.minelittlepony.unicopia.item.enchantment;
import com.minelittlepony.unicopia.Unicopia;
import com.mojang.serialization.MapCodec;
import net.minecraft.enchantment.effect.EnchantmentEntityEffect;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
public interface UEnchantmentEffects {
static void bootstrap() {
register("group_based_attribute", GroupBasedAttributeEnchantmentEffect.CODEC);
register("poisoned_joke_sound", AmbientSoundsEnchantmentEffect.CODEC);
register("danger_sensor", DangerSensingEnchantmentEffect.CODEC);
register("particle_trail", ParticleTrailEnchantmentEntityEffect.CODEC);
}
private static void register(String name, MapCodec<? extends EnchantmentEntityEffect> codec) {
Registry.register(Registries.ENCHANTMENT_ENTITY_EFFECT_TYPE, Unicopia.id("name"), codec);
}
}

View file

@ -1,9 +0,0 @@
package com.minelittlepony.unicopia.item.enchantment;
import net.minecraft.entity.EquipmentSlot;
public interface UEnchantmentValidSlots {
EquipmentSlot[] ANY = EquipmentSlot.values();
EquipmentSlot[] ARMOR = { EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET };
EquipmentSlot[] HANDS = { EquipmentSlot.MAINHAND, EquipmentSlot.OFFHAND };
}

View file

@ -2,6 +2,9 @@ package com.minelittlepony.unicopia.item.enchantment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.entity.mob.UEntityAttributes; import com.minelittlepony.unicopia.entity.mob.UEntityAttributes;
import net.fabricmc.fabric.api.event.registry.DynamicRegistrySetupCallback; import net.fabricmc.fabric.api.event.registry.DynamicRegistrySetupCallback;
@ -12,6 +15,7 @@ import net.minecraft.enchantment.EnchantmentLevelBasedValue;
import net.minecraft.enchantment.effect.AttributeEnchantmentEffect; import net.minecraft.enchantment.effect.AttributeEnchantmentEffect;
import net.minecraft.entity.attribute.EntityAttributeModifier; import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.attribute.EntityAttributes; import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.attribute.EntityAttributeModifier.Operation;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.registry.Registry; import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryKey;
@ -137,6 +141,7 @@ public interface UEnchantments {
} }
static void bootstrap() { static void bootstrap() {
UEnchantmentEffects.bootstrap();
// Options.table -> EnchantmentTags.IN_ENCHANTING_TABLE // Options.table -> EnchantmentTags.IN_ENCHANTING_TABLE
// Optiona.curse -> EnchantmentTags.CURSE // Optiona.curse -> EnchantmentTags.CURSE
// Options.traded -> EnchantmentTags.TRADEABLE // Options.traded -> EnchantmentTags.TRADEABLE
@ -153,7 +158,6 @@ public interface UEnchantments {
4, 4,
AttributeModifierSlot.HAND AttributeModifierSlot.HAND
))); )));
// TODO: gem finder effect
register(registry, PADDED, Enchantment.builder(Enchantment.definition( register(registry, PADDED, Enchantment.builder(Enchantment.definition(
items.getEntryList(ItemTags.HEAD_ARMOR_ENCHANTABLE).orElseThrow(), items.getEntryList(ItemTags.HEAD_ARMOR_ENCHANTABLE).orElseThrow(),
@ -197,8 +201,12 @@ public interface UEnchantments {
1, 1,
AttributeModifierSlot.MAINHAND AttributeModifierSlot.MAINHAND
) )
)); ).addEffect(EnchantmentEffectComponentTypes.TICK, new GroupBasedAttributeEnchantmentEffect(new AttributeEnchantmentEffect(
// TODO: Herding effect Unicopia.id("team_strength"),
EntityAttributes.GENERIC_ATTACK_DAMAGE,
EnchantmentLevelBasedValue.linear(0, 1),
Operation.ADD_VALUE
), EnchantmentLevelBasedValue.linear(2, 2))));
register(registry, REPULSION, Enchantment.builder( register(registry, REPULSION, Enchantment.builder(
Enchantment.definition( Enchantment.definition(
@ -225,8 +233,8 @@ public interface UEnchantments {
Enchantment.constantCost(2), Enchantment.constantCost(10), Enchantment.constantCost(2), Enchantment.constantCost(10),
4, 4,
AttributeModifierSlot.ANY AttributeModifierSlot.ANY
))); )).addEffect(EnchantmentEffectComponentTypes.TICK, new ParticleTrailEnchantmentEntityEffect(Optional.empty(), 0.2F, 1, 10)));
// TODO: Want it need it effect
register(registry, POISONED_JOKE, Enchantment.builder(Enchantment.definition( register(registry, POISONED_JOKE, Enchantment.builder(Enchantment.definition(
items.getEntryList(ItemTags.VANISHING_ENCHANTABLE).orElseThrow(), items.getEntryList(ItemTags.VANISHING_ENCHANTABLE).orElseThrow(),
Rarity.VERY_RARE, Rarity.VERY_RARE,
@ -234,8 +242,7 @@ public interface UEnchantments {
Enchantment.constantCost(2), Enchantment.constantCost(10), Enchantment.constantCost(2), Enchantment.constantCost(10),
4, 4,
AttributeModifierSlot.ANY AttributeModifierSlot.ANY
))); )).addEffect(EnchantmentEffectComponentTypes.TICK, new AmbientSoundsEnchantmentEffect(Unicopia.id("poisoned_joke_level"), UTags.Sounds.POISON_JOKE_EVENTS)));
// TODO: Poisoned joke effect
register(registry, CLINGY, Enchantment.builder(Enchantment.definition( register(registry, CLINGY, Enchantment.builder(Enchantment.definition(
items.getEntryList(ItemTags.VANISHING_ENCHANTABLE).orElseThrow(), items.getEntryList(ItemTags.VANISHING_ENCHANTABLE).orElseThrow(),
@ -244,8 +251,7 @@ public interface UEnchantments {
Enchantment.constantCost(2), Enchantment.constantCost(12), Enchantment.constantCost(2), Enchantment.constantCost(12),
4, 4,
AttributeModifierSlot.ANY AttributeModifierSlot.ANY
))); )).addEffect(EnchantmentEffectComponentTypes.TICK, DangerSensingEnchantmentEffect.INSTANCE));
// TODO: Stressful effect
register(registry, CLINGY, Enchantment.builder(Enchantment.definition( register(registry, CLINGY, Enchantment.builder(Enchantment.definition(
items.getEntryList(ItemTags.EQUIPPABLE_ENCHANTABLE).orElseThrow(), items.getEntryList(ItemTags.EQUIPPABLE_ENCHANTABLE).orElseThrow(),

View file

@ -1,43 +0,0 @@
package com.minelittlepony.unicopia.item.enchantment;
import com.minelittlepony.unicopia.entity.Creature;
import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.particle.UParticles;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack;
public class WantItNeedItEnchantment {
public void onUserTick(Living<?> user, int level) {
if (user instanceof Creature && user.asWorld().random.nextInt(10) == 0) {
ParticleUtils.spawnParticles(new FollowingParticleEffect(UParticles.HEALTH_DRAIN, user.asEntity(), 0.2F), user.asEntity(), 1);
}
}
public static boolean prefersEquipment(ItemStack newStack, ItemStack oldStack) {
int newLevel = EnchantmentUtil.getLevel(UEnchantments.WANT_IT_NEED_IT, newStack);
int oldLevel = EnchantmentUtil.getLevel(UEnchantments.WANT_IT_NEED_IT, oldStack);
return newLevel > oldLevel;
}
public static int getLevel(Entity entity) {
return entity instanceof LivingEntity l ? getLevel(l)
: entity instanceof ItemEntity i ? getLevel(i)
: 0;
}
public static int getLevel(ItemEntity entity) {
return EnchantmentUtil.getLevel(UEnchantments.WANT_IT_NEED_IT, entity.getStack());
}
public static int getLevel(LivingEntity entity) {
return EnchantmentUtil.getLevel(UEnchantments.WANT_IT_NEED_IT, entity)
+ EnchantmentUtil.getLevel(UEnchantments.WANT_IT_NEED_IT, entity.getOffHandStack())
+ EnchantmentUtil.getLevel(UEnchantments.WANT_IT_NEED_IT, entity.getMainHandStack());
}
}

View file

@ -6,7 +6,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.minelittlepony.unicopia.item.enchantment.ConsumptionEnchantment; import com.minelittlepony.unicopia.item.enchantment.ConsumptionEnchantmentUtil;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -24,7 +24,7 @@ abstract class MixinBlock {
cancellable = true cancellable = true
) )
private static void dropStacks(BlockState state, World world, BlockPos pos, @Nullable BlockEntity blockEntity, Entity entity, ItemStack stack, CallbackInfo info) { private static void dropStacks(BlockState state, World world, BlockPos pos, @Nullable BlockEntity blockEntity, Entity entity, ItemStack stack, CallbackInfo info) {
if (ConsumptionEnchantment.applyConsumption(world, state, pos, blockEntity, entity, stack)) { if (ConsumptionEnchantmentUtil.applyConsumption(world, state, pos, blockEntity, entity, stack)) {
info.cancel(); info.cancel();
} }
} }

View file

@ -9,8 +9,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.entity.*; import com.minelittlepony.unicopia.entity.*;
import com.minelittlepony.unicopia.item.enchantment.WantItNeedItEnchantment; import com.minelittlepony.unicopia.item.enchantment.EnchantmentUtil;
import net.minecraft.entity.EntityType; import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.goal.GoalSelector; import net.minecraft.entity.ai.goal.GoalSelector;
@ -35,7 +34,7 @@ abstract class MixinMobEntity extends LivingEntity implements Equine.Container<C
@Inject(method = "prefersNewEquipment(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemStack;)Z", @Inject(method = "prefersNewEquipment(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemStack;)Z",
at = @At("HEAD"), cancellable = true) at = @At("HEAD"), cancellable = true)
private void onPrefersNewEquipment(ItemStack newStack, ItemStack oldStack, CallbackInfoReturnable<Boolean> info) { private void onPrefersNewEquipment(ItemStack newStack, ItemStack oldStack, CallbackInfoReturnable<Boolean> info) {
if (WantItNeedItEnchantment.prefersEquipment(newStack, oldStack)) { if (EnchantmentUtil.prefersEquipment(newStack, oldStack)) {
info.setReturnValue(true); info.setReturnValue(true);
} }
} }

View file

@ -8,7 +8,6 @@ import java.util.stream.Stream;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
import net.minecraft.registry.RegistryWrapper.WrapperLookup; import net.minecraft.registry.RegistryWrapper.WrapperLookup;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@ -17,8 +16,6 @@ import net.minecraft.util.math.Vec3d;
public interface NbtSerialisable { public interface NbtSerialisable {
@Deprecated @Deprecated
Serializer<NbtElement, BlockPos> BLOCK_POS = Serializer.ofCodec(BlockPos.CODEC); Serializer<NbtElement, BlockPos> BLOCK_POS = Serializer.ofCodec(BlockPos.CODEC);
@Deprecated
Serializer<NbtElement, ItemStack> ITEM_STACK = Serializer.ofCodec(ItemStack.CODEC);
/** /**
* Called to save this to nbt to persist state on file or to transmit over the network * Called to save this to nbt to persist state on file or to transmit over the network

View file

@ -0,0 +1,60 @@
package com.minelittlepony.unicopia.util.serialization;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import com.mojang.serialization.Codec;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
public class NbtMap<K, V> implements NbtSerialisable {
public static <K, V> NbtMap<K, V> of(Codec<K> keyCodec, Codec<V> valueCodec) {
return new NbtMap<>(Codec.unboundedMap(keyCodec, valueCodec));
}
private final Codec<Map<K, V>> codec;
private final Map<K, V> data = new HashMap<>();
public NbtMap(Codec<Map<K, V>> codec) {
this.codec = codec;
}
public Optional<V> getOrEmpty(K key) {
return Optional.ofNullable(data.get(key));
}
public V computeIfAbsent(K key, Supplier<V> factory) {
return data.computeIfAbsent(key, e -> factory.get());
}
public V put(K key, V value) {
return data.put(key, value);
}
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> function) {
return data.compute(key, function);
}
@Nullable
public V remove(K key) {
return data.remove(key);
}
@Override
public void toNBT(NbtCompound compound, WrapperLookup lookup) {
compound.put("data", NbtSerialisable.encode(codec, data));
}
@Override
public void fromNBT(NbtCompound compound, WrapperLookup lookup) {
data.clear();
NbtSerialisable.decode(codec, compound.get("data")).ifPresent(data::putAll);
}
}