diff --git a/src/main/java/com/minelittlepony/unicopia/USounds.java b/src/main/java/com/minelittlepony/unicopia/USounds.java index 19ba00e1..6c0d0d33 100644 --- a/src/main/java/com/minelittlepony/unicopia/USounds.java +++ b/src/main/java/com/minelittlepony/unicopia/USounds.java @@ -12,6 +12,8 @@ public interface USounds { SoundEvent BATPONY_EEEE = register("batpony_eeee"); SoundEvent CHANGELING_BUZZ = register("changeling_buzz"); + SoundEvent AURA = register("aura"); + SoundEvent RECORD_CRUSADE = register("record.crusade"); SoundEvent RECORD_PET = register("record.pet"); SoundEvent RECORD_POPULAR = register("record.popular"); diff --git a/src/main/java/com/minelittlepony/unicopia/UTags.java b/src/main/java/com/minelittlepony/unicopia/UTags.java index beae7d94..fefccd37 100644 --- a/src/main/java/com/minelittlepony/unicopia/UTags.java +++ b/src/main/java/com/minelittlepony/unicopia/UTags.java @@ -7,18 +7,23 @@ import net.minecraft.tag.Tag; import net.minecraft.util.Identifier; public interface UTags { - Tag APPLES = register("apples"); - Tag FESH_APPLES = register("fresh_apples"); + Tag APPLES = item("apples"); + Tag FESH_APPLES = item("fresh_apples"); - Tag NON_TOXIC = register("non_toxic"); - Tag FAIRLY_TOXIC = register("fairly_toxic"); - Tag SEVERELY_TOXIC = register("severely_toxic"); + Tag NON_TOXIC = item("non_toxic"); + Tag FAIRLY_TOXIC = item("fairly_toxic"); + Tag SEVERELY_TOXIC = item("severely_toxic"); - Tag FRAGILE = TagRegistry.block(new Identifier("unicopia", "fragile")); + Tag FRAGILE = block("fragile"); + Tag INTERESTING = block("interesting"); - static Tag register(String name) { + static Tag item(String name) { return TagRegistry.item(new Identifier("unicopia", name)); } + static Tag block(String name) { + return TagRegistry.block(new Identifier("unicopia", name)); + } + static void bootstrap() { } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java index e96b397b..d2112852 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java @@ -6,12 +6,15 @@ import com.minelittlepony.unicopia.BlockDestructionManager; import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.entity.player.Pony; +import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.util.MagicalDamageSource; import com.minelittlepony.unicopia.util.PosHelper; import com.minelittlepony.unicopia.util.WorldEvent; import net.minecraft.block.BlockState; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.LivingEntity; import net.minecraft.entity.attribute.EntityAttributes; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.player.PlayerEntity; @@ -80,19 +83,27 @@ public class EarthPonyStompAbility implements Ability { iplayer.waitForFall(() -> { BlockPos center = PosHelper.findSolidGroundAt(player.getEntityWorld(), player.getBlockPos()); + float heavyness = 1 + EnchantmentHelper.getEquipmentLevel(UEnchantments.HEAVY, player); + iplayer.getWorld().getOtherEntities(player, areaOfEffect.offset(iplayer.getOriginVector())).forEach(i -> { double dist = Math.sqrt(center.getSquaredDistance(i.getBlockPos())); if (dist <= rad + 3) { - double force = dist / 5; + double inertia = dist / 5; + + if (i instanceof LivingEntity) { + inertia *= 1 + EnchantmentHelper.getEquipmentLevel(UEnchantments.HEAVY, (LivingEntity)i); + } + inertia /= heavyness; + i.addVelocity( - -(player.getX() - i.getX()) / force, - -(player.getY() - i.getY() - 2) / force + (dist < 1 ? dist : 0), - -(player.getZ() - i.getZ()) / force); + -(player.getX() - i.getX()) / inertia, + -(player.getY() - i.getY() - 2) / inertia + (dist < 1 ? dist : 0), + -(player.getZ() - i.getZ()) / inertia); DamageSource damage = MagicalDamageSource.create("smash", player); - double amount = (4 * player.getAttributeInstance(EntityAttributes.GENERIC_ATTACK_DAMAGE).getValue()) / (float)dist; + double amount = (4 * player.getAttributeInstance(EntityAttributes.GENERIC_ATTACK_DAMAGE).getValue() + heavyness * 0.4) / (float)dist; if (i instanceof PlayerEntity) { Race race = Pony.of((PlayerEntity)i).getSpecies(); @@ -109,10 +120,12 @@ public class EarthPonyStompAbility implements Ability { } }); - BlockPos.iterate(center.add(-rad, -rad, -rad), center.add(rad, rad, rad)).forEach(i -> { + double radius = rad + heavyness * 0.3; + + BlockPos.iterate(center.add(-radius, -radius, -radius), center.add(radius, radius, radius)).forEach(i -> { double dist = Math.sqrt(i.getSquaredDistance(player.getX(), player.getY(), player.getZ(), true)); - if (dist <= rad) { + if (dist <= radius) { spawnEffect(player.world, i, dist); } }); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/ShieldSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/ShieldSpell.java index 119af6c0..2d35c5e8 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/ShieldSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/ShieldSpell.java @@ -13,12 +13,14 @@ import com.minelittlepony.unicopia.ability.magic.Attached; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.item.FriendshipBraceletItem; +import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.ParticleHandle; import com.minelittlepony.unicopia.particle.SphereParticleEffect; import com.minelittlepony.unicopia.projectile.ProjectileUtil; import com.minelittlepony.unicopia.util.shape.Sphere; +import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.Entity; import net.minecraft.entity.EyeOfEnderEntity; import net.minecraft.entity.FallingBlockEntity; @@ -182,6 +184,10 @@ public class ShieldSpell extends AbstractRangedAreaSpell implements Attached { protected void applyForce(Vec3d pos, Entity target, double force, double distance) { pos = target.getPos().subtract(pos).normalize().multiply(force); + if (target instanceof LivingEntity) { + pos = pos.multiply(1 / (1 + EnchantmentHelper.getEquipmentLevel(UEnchantments.HEAVY, (LivingEntity)target))); + } + target.addVelocity( pos.x, pos.y + (distance < 1 ? distance : 0), diff --git a/src/main/java/com/minelittlepony/unicopia/client/sound/MagicAuraSoundInstance.java b/src/main/java/com/minelittlepony/unicopia/client/sound/MagicAuraSoundInstance.java new file mode 100644 index 00000000..c406cd23 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/sound/MagicAuraSoundInstance.java @@ -0,0 +1,73 @@ +package com.minelittlepony.unicopia.client.sound; + +import java.util.Optional; + +import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.entity.Living; +import com.minelittlepony.unicopia.item.enchantment.SimpleEnchantment; +import com.minelittlepony.unicopia.item.enchantment.UEnchantments; + +import net.minecraft.client.sound.MovingSoundInstance; +import net.minecraft.sound.SoundCategory; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; + +public class MagicAuraSoundInstance extends MovingSoundInstance { + + private final Living living; + + private float sourceVolume; + private float targetVolume; + private float interpolate; + + private boolean fadingOut; + + public MagicAuraSoundInstance(SoundCategory category, Living living) { + super(USounds.AURA, category); + this.looping = true; + this.repeat = true; + this.living = living; + this.volume = 0; + setTargetVolume(1); + } + + @Override + public boolean shouldAlwaysPlay() { + return true; + } + + @Override + public void tick() { + Optional data = living.getEnchants().getOrEmpty(UEnchantments.GEM_LOCATION); + + Vec3d pos = living.getOriginVector(); + x = pos.x; + y = pos.y; + z = pos.z; + + if (!living.getEntity().removed && data.isPresent()) { + float level = data.get().level; + if (level != targetVolume) { + setTargetVolume(level); + } + } else { + fadingOut = true; + setTargetVolume(0); + } + + if (interpolate < 1) { + interpolate = Math.min(interpolate + 0.4F, 1); + volume = MathHelper.lerp(interpolate, sourceVolume, targetVolume); + } + + if (fadingOut && volume < 0.01F) { + setDone(); + } + } + + private void setTargetVolume(float target) { + sourceVolume = volume; + targetVolume = target; + interpolate = 0; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Creature.java b/src/main/java/com/minelittlepony/unicopia/entity/Creature.java index db076d6b..14d8f601 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/Creature.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/Creature.java @@ -6,10 +6,17 @@ import com.minelittlepony.unicopia.ability.magic.Affine; import com.minelittlepony.unicopia.ability.magic.Levelled; import com.minelittlepony.unicopia.ability.magic.Spell; import com.minelittlepony.unicopia.ability.magic.spell.SpellRegistry; +import com.minelittlepony.unicopia.entity.ai.WantItNeedItTargetGoal; +import com.minelittlepony.unicopia.entity.ai.WantItTakeItGoal; + import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.goal.GoalSelector; +import net.minecraft.entity.attribute.DefaultAttributeContainer; +import net.minecraft.entity.attribute.EntityAttributes; import net.minecraft.entity.data.DataTracker; import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.data.TrackedDataHandlerRegistry; +import net.minecraft.entity.mob.MobEntity; import net.minecraft.nbt.CompoundTag; public class Creature extends Living { @@ -26,6 +33,16 @@ public class Creature extends Living { super(entity, EFFECT); } + public void initAi(GoalSelector goals, GoalSelector targets) { + targets.add(1, new WantItNeedItTargetGoal((MobEntity)entity)); + goals.add(1, new WantItTakeItGoal((MobEntity)entity)); + } + + public static void registerAttributes(DefaultAttributeContainer.Builder builder) { + builder.add(EntityAttributes.GENERIC_ATTACK_DAMAGE); + builder.add(EntityAttributes.GENERIC_ATTACK_KNOCKBACK); + } + @Override public Race getSpecies() { return Race.HUMAN; @@ -55,6 +72,7 @@ public class Creature extends Living { @Override public void toNBT(CompoundTag compound) { + super.toNBT(compound); Spell effect = getSpell(true); if (effect != null) { @@ -65,6 +83,7 @@ public class Creature extends Living { @Override public void fromNBT(CompoundTag compound) { + super.fromNBT(compound); if (compound.contains("effect")) { setSpell(SpellRegistry.instance().createEffectFromNBT(compound.getCompound("effect"))); } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Enchantments.java b/src/main/java/com/minelittlepony/unicopia/entity/Enchantments.java new file mode 100644 index 00000000..a43d1514 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/Enchantments.java @@ -0,0 +1,97 @@ +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 javax.annotation.Nullable; + +import com.minelittlepony.unicopia.item.enchantment.SimpleEnchantment; +import com.minelittlepony.unicopia.item.enchantment.UEnchantments; +import com.minelittlepony.unicopia.util.NbtSerialisable; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.util.Identifier; +import net.minecraft.util.Tickable; +import net.minecraft.util.registry.Registry; + +public class Enchantments implements NbtSerialisable, Tickable { + + private final Living entity; + + private final Set equippedEnchantments = new HashSet<>(); + + private final Map data = new HashMap<>(); + + Enchantments(Living entity) { + this.entity = entity; + } + + @SuppressWarnings("unchecked") + public Optional getOrEmpty(Enchantment enchantment) { + return Optional.ofNullable((T)data.get(enchantment)); + } + + @SuppressWarnings("unchecked") + public T computeIfAbsent(Enchantment enchantment, Supplier factory) { + return (T)data.computeIfAbsent(enchantment, e -> factory.get()); + } + + @Nullable + @SuppressWarnings("unchecked") + public T remove(Enchantment enchantment) { + return (T)data.remove(enchantment); + } + + @Override + public void tick() { + UEnchantments.REGISTRY.forEach(ench -> { + int level = EnchantmentHelper.getEquipmentLevel(ench, entity.getMaster()); + + boolean active = level > 0; + + if (active != equippedEnchantments.contains(ench)) { + if (active) { + equippedEnchantments.add(ench); + ench.onEquipped(entity); + } else { + equippedEnchantments.remove(ench); + ench.onUnequipped(entity); + } + } + + if (active) { + ench.onUserTick(entity, level); + } + }); + } + + @Override + public void toNBT(CompoundTag compound) { + ListTag list = new ListTag(); + equippedEnchantments.forEach(enchant -> { + Identifier id = Registry.ENCHANTMENT.getId(enchant); + if (id != null) { + list.add(StringTag.of(id.toString())); + } + }); + compound.put("enchants", list); + } + + @Override + public void fromNBT(CompoundTag compound) { + equippedEnchantments.clear(); + if (compound.contains("enchants")) { + compound.getList("enchants", 8).forEach(tag -> { + Registry.ENCHANTMENT.getOrEmpty(new Identifier(tag.asString())).ifPresent(equippedEnchantments::add); + }); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Living.java b/src/main/java/com/minelittlepony/unicopia/entity/Living.java index e9c890c8..6f7c1da3 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/Living.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/Living.java @@ -34,6 +34,8 @@ public abstract class Living implements Equine, Caste private int invinsibilityTicks; + private final Enchantments enchants = new Enchantments(this); + protected Living(T entity, TrackedData effect) { this.entity = entity; this.effectDelegate = new EffectSync(this, effect); @@ -58,6 +60,10 @@ public abstract class Living implements Equine, Caste return effectDelegate; } + public Enchantments getEnchants() { + return enchants; + } + @Override public void setMaster(T owner) { } @@ -93,6 +99,8 @@ public abstract class Living implements Equine, Caste if (getPhysics().isGravityNegative() && entity.getY() > entity.world.getHeight() + 64) { entity.damage(DamageSource.OUT_OF_WORLD, 4.0F); } + + enchants.tick(); } @Override @@ -152,4 +160,14 @@ public abstract class Living implements Equine, Caste spell.getDisguise().onImpact(this, distance, damageMultiplier); }); } + + @Override + public void toNBT(CompoundTag compound) { + enchants.toNBT(compound); + } + + @Override + public void fromNBT(CompoundTag compound) { + enchants.fromNBT(compound); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/ai/WantItNeedItSensor.java b/src/main/java/com/minelittlepony/unicopia/entity/ai/WantItNeedItSensor.java new file mode 100644 index 00000000..8ade9ffd --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/ai/WantItNeedItSensor.java @@ -0,0 +1,34 @@ +package com.minelittlepony.unicopia.entity.ai; + +import java.util.Optional; +import java.util.Set; + +import com.google.common.collect.ImmutableSet; +import com.minelittlepony.unicopia.item.enchantment.UEnchantments; + +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.brain.MemoryModuleType; +import net.minecraft.entity.ai.brain.sensor.Sensor; +import net.minecraft.server.world.ServerWorld; + +public class WantItNeedItSensor extends Sensor { + + @Override + public Set> getOutputMemoryModules() { + return ImmutableSet.of(MemoryModuleType.ATTACK_TARGET, MemoryModuleType.VISIBLE_MOBS); + } + + @Override + protected void sense(ServerWorld world, LivingEntity entity) { + entity.getBrain().getOptionalMemory(MemoryModuleType.VISIBLE_MOBS).ifPresent(targets -> { + + Optional target = targets.stream() + .filter(e -> (EnchantmentHelper.getEquipmentLevel(UEnchantments.DESIRED, e) * 10) >= entity.distanceTo(e)) + .findFirst(); + + entity.getBrain().remember(MemoryModuleType.ATTACK_TARGET, target); + }); + } + +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/ai/WantItNeedItTargetGoal.java b/src/main/java/com/minelittlepony/unicopia/entity/ai/WantItNeedItTargetGoal.java new file mode 100644 index 00000000..59ee3c86 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/ai/WantItNeedItTargetGoal.java @@ -0,0 +1,59 @@ +package com.minelittlepony.unicopia.entity.ai; + +import java.util.Comparator; +import java.util.Optional; + +import com.minelittlepony.unicopia.item.enchantment.UEnchantments; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.TargetPredicate; +import net.minecraft.entity.ai.goal.Goal; +import net.minecraft.entity.mob.MobEntity; + +public class WantItNeedItTargetGoal extends Goal { + + private final TargetPredicate predicate; + + private final MobEntity mob; + + private int interval; + + public WantItNeedItTargetGoal(MobEntity mob) { + this.predicate = new TargetPredicate() + .setBaseMaxDistance(64) + .setPredicate(WantItNeedItTargetGoal::canTarget); + this.mob = mob; + } + + @Override + public boolean canStart() { + if (interval-- <= 0) { + interval = 20; + Optional target = mob.world.getOtherEntities(mob, mob.getBoundingBox().expand(16, 16, 16), + e -> e instanceof LivingEntity && predicate.test(mob, (LivingEntity)e)) + .stream() + .map(e -> (LivingEntity)e) + .sorted(Comparator.comparing((Entity e) -> mob.distanceTo(e))) + .findFirst(); + + if (target.isPresent()) { + mob.setTarget(target.get()); + return true; + } + } + + return false; + } + + @Override + public boolean shouldContinue() { + LivingEntity target = mob.getTarget(); + return target != null && mob.isTarget(target, TargetPredicate.DEFAULT); + } + + static boolean canTarget(LivingEntity e) { + return EnchantmentHelper.getEquipmentLevel(UEnchantments.DESIRED, e) > 0; + } + +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/ai/WantItTakeItGoal.java b/src/main/java/com/minelittlepony/unicopia/entity/ai/WantItTakeItGoal.java new file mode 100644 index 00000000..0c2064b1 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/ai/WantItTakeItGoal.java @@ -0,0 +1,140 @@ +package com.minelittlepony.unicopia.entity.ai; + +import java.util.Comparator; +import java.util.EnumSet; +import java.util.Optional; + +import javax.annotation.Nullable; + +import com.minelittlepony.unicopia.AwaitTickQueue; +import com.minelittlepony.unicopia.item.enchantment.UEnchantments; +import com.minelittlepony.unicopia.particle.FollowingParticleEffect; +import com.minelittlepony.unicopia.particle.ParticleUtils; +import com.minelittlepony.unicopia.particle.UParticles; +import com.minelittlepony.unicopia.util.VecHelper; + +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.goal.Goal; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.item.ItemStack; + +public class WantItTakeItGoal extends Goal { + + private final MobEntity mob; + + @Nullable + private LivingEntity target; + @Nullable + private ItemEntity item; + + private int cooldown; + + public WantItTakeItGoal(MobEntity mob) { + this.mob = mob; + this.setControls(EnumSet.of(Goal.Control.MOVE, Goal.Control.LOOK)); + } + + @Override + public boolean canStart() { + LivingEntity target = mob.getTarget(); + if (target == null || !WantItNeedItTargetGoal.canTarget(target)) { + + Optional item = VecHelper.findInRange(mob, mob.world, mob.getPos(), 16, + e -> !e.removed && e instanceof ItemEntity && EnchantmentHelper.getLevel(UEnchantments.DESIRED, ((ItemEntity)e).getStack()) > 0) + .stream() + .map(e -> (ItemEntity)e) + .sorted(Comparator.comparing((Entity e) -> mob.distanceTo(e))) + .findFirst(); + + if (item.isPresent()) { + this.item = item.get(); + return true; + } + + return false; + } + + this.target = target; + return true; + } + + @Override + public boolean shouldContinue() { + return (target == null || ( + target.isAlive() + && WantItNeedItTargetGoal.canTarget(target) + && mob.squaredDistanceTo(target) <= 225)) + && (item == null || item.isAlive()) + && (!mob.getNavigation().isIdle() || canStart()); + } + + @Override + public void stop() { + target = null; + item = null; + mob.getNavigation().stop(); + } + + @Override + public void tick() { + if (target == null && (item == null || item.removed)) { + return; + } + + Entity targetEntity = target == null ? item : target; + + mob.getLookControl().lookAt(targetEntity, 30, 30); + + double reach = mob.getWidth() * 2 * mob.getWidth() * 2; + double distance = mob.squaredDistanceTo(targetEntity.getX(), targetEntity.getY(), targetEntity.getZ()); + + double speed = 0.8D; + + if (distance > reach && distance < 16) { + speed = 1.33; + } else if (distance < 225) { + speed = 0.6; + } + speed *= 2; + + ParticleUtils.spawnParticles(new FollowingParticleEffect(UParticles.HEALTH_DRAIN, mob, 0.2F), mob, 1); + + mob.getNavigation().startMovingTo(targetEntity, speed); + + cooldown = Math.max(this.cooldown - 1, 0); + if (distance <= reach) { + if (target != null) { + if (cooldown <= 0) { + cooldown = 20; + mob.tryAttack(target); + + if (mob.world.random.nextInt(20) == 0) { + for (EquipmentSlot slot : EquipmentSlot.values()) { + ItemStack stack = target.getEquippedStack(slot); + if (EnchantmentHelper.getLevel(UEnchantments.DESIRED, stack) > 0) { + target.equipStack(slot, ItemStack.EMPTY); + AwaitTickQueue.scheduleTask(mob.world, w -> { + mob.tryEquip(stack); + }, 0); + } + } + } + } + } else { + ItemStack stack = item.getStack(); + AwaitTickQueue.scheduleTask(mob.world, w -> { + if (!item.removed) { + mob.tryEquip(stack); + mob.method_29499(item); + mob.sendPickup(item, stack.getCount()); + item.remove(); + } + }, 0); + } + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java index e08030f7..39358766 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -6,11 +6,13 @@ import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.Spell; import com.minelittlepony.unicopia.entity.EntityPhysics; import com.minelittlepony.unicopia.entity.player.MagicReserves.Bar; +import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.util.NbtSerialisable; import com.minelittlepony.unicopia.util.MutableVector; import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityPose; import net.minecraft.entity.attribute.EntityAttributeInstance; @@ -196,6 +198,12 @@ public class PlayerPhysics extends EntityPhysics implements Tickable, Moti lastPos = new Vec3d(entity.getX(), 0, entity.getZ()); + if (!entity.isOnGround()) { + float heavyness = 1 - EnchantmentHelper.getEquipmentLevel(UEnchantments.HEAVY, entity) * 0.015F; + velocity.x *= heavyness; + velocity.z *= heavyness; + } + entity.setVelocity(velocity.toImmutable()); } @@ -245,12 +253,17 @@ public class PlayerPhysics extends EntityPhysics implements Tickable, Moti thrustScale *= 0.1889F; } + float heavyness = EnchantmentHelper.getEquipmentLevel(UEnchantments.HEAVY, player) / 6F; float thrustStrength = 0.135F * thrustScale; + if (heavyness > 0) { + thrustStrength /= heavyness; + } + Vec3d direction = player.getRotationVec(1).normalize().multiply(thrustStrength); velocity.x += direction.x; velocity.z += direction.z; - velocity.y += (direction.y * 2.45 + Math.abs(direction.y) * 10) * getGravitySignum(); + velocity.y += (direction.y * 2.45 + Math.abs(direction.y) * 10) * getGravitySignum() - heavyness / 5F; if (player.isSneaking()) { if (!isGravityNegative()) { @@ -264,7 +277,7 @@ public class PlayerPhysics extends EntityPhysics implements Tickable, Moti } } - protected void applyTurbulance(Entity player, MutableVector velocity) { + protected void applyTurbulance(PlayerEntity player, MutableVector velocity) { float glance = 360 * player.world.random.nextFloat(); float forward = 0.015F * player.world.random.nextFloat() * player.world.getRainGradient(1); @@ -287,6 +300,7 @@ public class PlayerPhysics extends EntityPhysics implements Tickable, Moti } forward = Math.min(forward, 7); + forward /= 1 + (EnchantmentHelper.getEquipmentLevel(UEnchantments.HEAVY, player) * 0.8F); velocity.x += - forward * MathHelper.sin((player.yaw + glance) * 0.017453292F); velocity.z += forward * MathHelper.cos((player.yaw + glance) * 0.017453292F); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java index 0f91a2f5..d7dd45c9 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -276,10 +276,10 @@ public class Pony extends Living implements Transmittable, Copieab ticksHanging = 0; } - super.tick(); - tickers.forEach(Tickable::tick); + super.tick(); + if (dirty) { sendCapabilities(true); } @@ -376,6 +376,7 @@ public class Pony extends Living implements Transmittable, Copieab @Override public void toNBT(CompoundTag compound) { + super.toNBT(compound); compound.putString("playerSpecies", getSpecies().name()); compound.putFloat("magicExhaustion", magicExhaustion); @@ -392,6 +393,7 @@ public class Pony extends Living implements Transmittable, Copieab @Override public void fromNBT(CompoundTag compound) { + super.fromNBT(compound); speciesPersisted = true; setSpecies(Race.fromName(compound.getString("playerSpecies"))); diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java index 9ed42cf8..279088d1 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java +++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.stream.Collectors; import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.item.toxin.ToxicHolder; import com.minelittlepony.unicopia.item.toxin.Toxics; import com.minelittlepony.unicopia.item.toxin.UFoodComponents; @@ -72,6 +73,7 @@ public interface UItems { static void bootstrap() { Toxics.bootstrap(); + UEnchantments.bootstrap(); URecipes.bootstrap(); FabricItemGroupBuilder.create(new Identifier("unicopia", "items")).appendItems(list -> { @@ -81,7 +83,7 @@ public interface UItems { .map(Item::getDefaultStack) .collect(Collectors.toList()) ); - }).icon(ZAP_APPLE::getDefaultStack).build(); + }).icon(EMPTY_JAR::getDefaultStack).build(); FabricItemGroupBuilder.create(new Identifier("unicopia", "horsefeed")).appendItems(list -> { list.addAll(Registry.ITEM.stream() diff --git a/src/main/java/com/minelittlepony/unicopia/item/enchantment/AttributedEnchantment.java b/src/main/java/com/minelittlepony/unicopia/item/enchantment/AttributedEnchantment.java new file mode 100644 index 00000000..18bddcb7 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/enchantment/AttributedEnchantment.java @@ -0,0 +1,62 @@ +package com.minelittlepony.unicopia.item.enchantment; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.IntFunction; + +import com.minelittlepony.unicopia.entity.Living; + +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.attribute.EntityAttribute; +import net.minecraft.entity.attribute.EntityAttributeInstance; +import net.minecraft.entity.attribute.EntityAttributeModifier; + +public class AttributedEnchantment extends SimpleEnchantment { + + private final Map> modifiers = new HashMap<>(); + + protected AttributedEnchantment(Rarity rarity, EnchantmentTarget target, boolean cursed, int maxLevel, EquipmentSlot... slots) { + super(rarity, target, cursed, maxLevel, slots); + } + + protected AttributedEnchantment(Rarity rarity, boolean cursed, int maxLevel, EquipmentSlot... slots) { + super(rarity, cursed, maxLevel, slots); + } + + public AttributedEnchantment addModifier(EntityAttribute attribute, IntFunction modifierSupplier) { + modifiers.put(attribute, modifierSupplier); + return this; + } + + @Override + public void onUserTick(Living user, int level) { + if (shouldChangeModifiers(user, level)) { + LivingEntity entity = user.getMaster(); + modifiers.forEach((attr, modifierSupplier) -> { + EntityAttributeInstance instance = entity.getAttributeInstance(attr); + + EntityAttributeModifier modifier = modifierSupplier.apply(level); + + instance.removeModifier(modifier.getId()); + instance.addPersistentModifier(modifier); + }); + } + } + + @Override + public void onUnequipped(Living user) { + LivingEntity entity = user.getMaster(); + modifiers.forEach((attr, modifierSupplier) -> { + EntityAttributeInstance instance = entity.getAttributeInstance(attr); + + instance.tryRemoveModifier(modifierSupplier.apply(1).getId()); + }); + user.getEnchants().remove(this); + } + + protected boolean shouldChangeModifiers(Living user, int level) { + return user.getEnchants().computeIfAbsent(this, Data::new).update(level); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/enchantment/GemFindingEnchantment.java b/src/main/java/com/minelittlepony/unicopia/item/enchantment/GemFindingEnchantment.java new file mode 100644 index 00000000..3fbff251 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/enchantment/GemFindingEnchantment.java @@ -0,0 +1,46 @@ +package com.minelittlepony.unicopia.item.enchantment; + +import com.minelittlepony.unicopia.UTags; +import com.minelittlepony.unicopia.client.sound.MagicAuraSoundInstance; +import com.minelittlepony.unicopia.entity.Living; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; + +public class GemFindingEnchantment extends SimpleEnchantment { + + protected GemFindingEnchantment() { + super(Rarity.RARE, EnchantmentTarget.DIGGER, false, 3, EquipmentSlot.MAINHAND, EquipmentSlot.OFFHAND); + } + + @Override + public void onUserTick(Living user, int level) { + int radius = 2 + (level * 2); + + BlockPos origin = user.getOrigin(); + + float volume = BlockPos.findClosest(origin, radius, radius, pos -> user.getWorld().getBlockState(pos).isIn(UTags.INTERESTING)) + .map(p -> user.getOriginVector().squaredDistanceTo(p.getX(), p.getY(), p.getZ())) + .map(find -> (1 - (MathHelper.sqrt(find) / radius))) + .orElse(-1F); + + volume = Math.max(volume, 0.04F); + + user.getEnchants().computeIfAbsent(this, Data::new).level = volume * (1.3F + level * 0.3F); + } + + @Override + public void onEquipped(Living user) { + if (user.isClient()) { + MinecraftClient.getInstance().getSoundManager().play(new MagicAuraSoundInstance(user.getEntity().getSoundCategory(), user)); + } + } + + @Override + public void onUnequipped(Living user) { + user.getEnchants().remove(this).level = 0; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/enchantment/SimpleEnchantment.java b/src/main/java/com/minelittlepony/unicopia/item/enchantment/SimpleEnchantment.java new file mode 100644 index 00000000..fd5ba0d8 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/enchantment/SimpleEnchantment.java @@ -0,0 +1,71 @@ +package com.minelittlepony.unicopia.item.enchantment; + +import com.minelittlepony.unicopia.entity.Living; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.item.ItemStack; + +public class SimpleEnchantment extends Enchantment { + + private final boolean cursed; + + private final boolean allItems; + + private final int maxLevel; + + protected SimpleEnchantment(Rarity rarity, EnchantmentTarget target, boolean cursed, int maxLevel, EquipmentSlot... slots) { + super(rarity, target, slots); + this.cursed = cursed; + this.allItems = false; + this.maxLevel = maxLevel; + } + + protected SimpleEnchantment(Rarity rarity, boolean cursed, int maxLevel, EquipmentSlot... slots) { + super(rarity, EnchantmentTarget.VANISHABLE, slots); // vanishable includes breakable. It's the one that accepts the widest variety of items + this.cursed = cursed; + this.allItems = true; + this.maxLevel = maxLevel; + } + + public void onUserTick(Living user, int level) { + + } + + public void onEquipped(Living user) { + + } + + public void onUnequipped(Living user) { + + } + + @Override + public boolean isAcceptableItem(ItemStack itemStack) { + return allItems || super.isAcceptableItem(itemStack); + } + + + @Override + public int getMaxLevel() { + return maxLevel; + } + + @Override + public boolean isCursed() { + return cursed; + } + + public static class Data { + public float level; + + public boolean update(int level) { + if (level == this.level) { + return false; + } + this.level = level; + return true; + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/enchantment/StressfulEnchantment.java b/src/main/java/com/minelittlepony/unicopia/item/enchantment/StressfulEnchantment.java new file mode 100644 index 00000000..73e90a7b --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/enchantment/StressfulEnchantment.java @@ -0,0 +1,25 @@ +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.EquipmentSlot; + +public class StressfulEnchantment extends SimpleEnchantment { + + protected StressfulEnchantment() { + super(Rarity.COMMON, true, 3, EquipmentSlot.values()); + } + + @Override + public void onUserTick(Living user, int level) { + if (user instanceof Pony) { + Bar bar = ((Pony)user).getMagicalReserves().getEnergy(); + float targetPercent = (level / (float)getMaxLevel()) * 0.5F; + if (bar.getPercentFill() < targetPercent) { + bar.add(20); + } + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/enchantment/UEnchantments.java b/src/main/java/com/minelittlepony/unicopia/item/enchantment/UEnchantments.java new file mode 100644 index 00000000..546815c7 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/enchantment/UEnchantments.java @@ -0,0 +1,82 @@ +package com.minelittlepony.unicopia.item.enchantment; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.Enchantment.Rarity; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.attribute.EntityAttributeModifier; +import net.minecraft.entity.attribute.EntityAttributes; +import net.minecraft.entity.attribute.EntityAttributeModifier.Operation; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +public interface UEnchantments { + + List REGISTRY = new ArrayList<>(); + + /** + * Makes a sound when there are interesting blocks in your area. + */ + Enchantment GEM_LOCATION = register("gem_location", new GemFindingEnchantment()); + + /** + * Protects against wall collisions and earth pony attacks! + * + * TODO: + */ + Enchantment PADDED = register("padded", new SimpleEnchantment(Rarity.COMMON, EnchantmentTarget.ARMOR, false, 3, EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET)); + + /** + * Heavy players move more slowly but are less likely to be flung around wildly. + * + * TODO: + */ + Enchantment HEAVY = register("heavy", new AttributedEnchantment(Rarity.COMMON, EnchantmentTarget.ARMOR_FEET, false, 4, EquipmentSlot.FEET)) + .addModifier(EntityAttributes.GENERIC_MOVEMENT_SPEED, level -> { + // 1 -> 0.9 + // 2 -> 0.8 + // 3 -> 0.7 + return new EntityAttributeModifier(UUID.fromString("a3d5a94f-4c40-48f6-a343-558502a13e10"), "Heavyness", (1 - level/(float)10) - 1, Operation.MULTIPLY_TOTAL); + }); + + /** + * It's dangerous to go alone, take this! + * + * Weapons will become stronger the more allies you have around. + * + * TODO: + */ + Enchantment COLLABORATOR = register("collaborator", new SimpleEnchantment(Rarity.COMMON, EnchantmentTarget.WEAPON, false, 1, EquipmentSlot.MAINHAND)); + + /** + * I want it, I neeeed it! + * + * Mobs really want your candy. You'd better give it to them. + */ + Enchantment DESIRED = register("desired", new WantItNeedItEnchantment()); + + /** + * Hahaha geddit? + * + * Random things happen. + * + * TODO: + */ + Enchantment POISON_JOKE = register("poison_joke", new SimpleEnchantment(Rarity.COMMON, true, 3, EquipmentSlot.values())); + + /** + * Who doesn't like a good freakout? + */ + Enchantment STRESS = register("stress", new StressfulEnchantment()); + + static void bootstrap() { } + + static T register(String name, T enchantment) { + REGISTRY.add(enchantment); + return Registry.register(Registry.ENCHANTMENT, new Identifier("unicopia", name), enchantment); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/enchantment/WantItNeedItEnchantment.java b/src/main/java/com/minelittlepony/unicopia/item/enchantment/WantItNeedItEnchantment.java new file mode 100644 index 00000000..d25e46c4 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/enchantment/WantItNeedItEnchantment.java @@ -0,0 +1,23 @@ +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.EquipmentSlot; + +public class WantItNeedItEnchantment extends SimpleEnchantment { + + protected WantItNeedItEnchantment() { + super(Rarity.VERY_RARE, true, 1, EquipmentSlot.values()); + } + + @Override + public void onUserTick(Living user, int level) { + if (user instanceof Creature && user.getWorld().random.nextInt(10) == 0) { + ParticleUtils.spawnParticles(new FollowingParticleEffect(UParticles.HEALTH_DRAIN, user.getEntity(), 0.2F), user.getEntity(), 1); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java index 82f99c59..c386c4a4 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java @@ -22,6 +22,7 @@ import com.minelittlepony.unicopia.entity.ItemWielder; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.attribute.DefaultAttributeContainer; import net.minecraft.entity.damage.DamageSource; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundTag; @@ -58,6 +59,11 @@ abstract class MixinLivingEntity extends Entity implements PonyContainer info) { + Creature.registerAttributes(info.getReturnValue()); + } + @Inject(method = "isClimbing()Z", at = @At("HEAD"), cancellable = true) public void onIsClimbing(CallbackInfoReturnable info) { if (get() instanceof Pony && horizontalCollision) { diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinMobEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinMobEntity.java new file mode 100644 index 00000000..f86144dc --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinMobEntity.java @@ -0,0 +1,29 @@ +package com.minelittlepony.unicopia.mixin; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import com.minelittlepony.unicopia.entity.Creature; +import com.minelittlepony.unicopia.entity.PonyContainer; +import com.minelittlepony.unicopia.entity.Equine; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.goal.GoalSelector; +import net.minecraft.entity.mob.MobEntity; + +@Mixin(MobEntity.class) +abstract class MixinMobEntity extends LivingEntity implements PonyContainer> { + private MixinMobEntity() { super(null, null); } + + @Shadow + protected @Final GoalSelector goalSelector; + @Shadow + protected @Final GoalSelector targetSelector; + + @Inject(method = "()V", at = @At("RETURN"), remap = false) + private void init(CallbackInfo info) { + ((Creature)get()).initAi(goalSelector, targetSelector); + } +} diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index e24c1d8d..d8243fec 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -163,6 +163,14 @@ "key.unicopia.hud_page_dn": "Hud Previous Page", "key.unicopia.hud_page_up": "Hud Next Page", + + "enchantment.unicopia.gem_location": "Gem Locator", + "enchantment.unicopia.padded": "Padded", + "enchantment.unicopia.heavy": "Heavy", + "enchantment.unicopia.collaborator": "Collaborator", + "enchantment.unicopia.desired": "Want It Need It", + "enchantment.unicopia.poison_joke": "Poisoned Joke", + "enchantment.unicopia.stress": "Stressful", "commands.race.success.self": "Your race has been updated", "commands.race.success.otherself": "%1$s changed race to %2$s", diff --git a/src/main/resources/assets/unicopia/sounds.json b/src/main/resources/assets/unicopia/sounds.json index efde3981..e6c86df8 100644 --- a/src/main/resources/assets/unicopia/sounds.json +++ b/src/main/resources/assets/unicopia/sounds.json @@ -9,6 +9,12 @@ "unicopia:wing/wing3" ] }, + "aura": { + "category": "player", + "sounds": [ + "unicopia:aura/aura0" + ] + }, "batpony_eeee": { "category": "player", "subtitle": "unicopia.subtitle.batpony_eeee", diff --git a/src/main/resources/assets/unicopia/sounds/aura/aura0.ogg b/src/main/resources/assets/unicopia/sounds/aura/aura0.ogg new file mode 100644 index 00000000..83ba55a2 Binary files /dev/null and b/src/main/resources/assets/unicopia/sounds/aura/aura0.ogg differ diff --git a/src/main/resources/data/unicopia/tags/blocks/interesting.json b/src/main/resources/data/unicopia/tags/blocks/interesting.json new file mode 100644 index 00000000..24f185cc --- /dev/null +++ b/src/main/resources/data/unicopia/tags/blocks/interesting.json @@ -0,0 +1,18 @@ +{ + "replace": false, + "values": [ + "minecraft:coal_ore", + "minecraft:iron_ore", + "minecraft:gold_ore", + "minecraft:emerald_ore", + "minecraft:lapis_ore", + "minecraft:diamond_ore", + "minecraft:nether_gold_ore", + "minecraft:nether_quartz_ore", + "minecraft:sea_lantern", + "minecraft:ender_chest", + "minecraft:end_portal_frame", + "minecraft:jukebox", + "minecraft:spawner" + ] +} diff --git a/src/main/resources/unicopia.mixin.json b/src/main/resources/unicopia.mixin.json index 76c2ffa4..e63dfd13 100644 --- a/src/main/resources/unicopia.mixin.json +++ b/src/main/resources/unicopia.mixin.json @@ -13,6 +13,7 @@ "MixinItems", "MixinLivingEntity", "MixinMilkBucketItem", + "MixinMobEntity", "MixinPersistentProjectileEntity", "MixinPlayerEntity", "MixinProjectileEntity",