From 539f094582a3d76b1af4713b62846355393a4c01 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sun, 5 Mar 2023 01:28:43 +0000 Subject: [PATCH] Implement grogar's bell --- .../unicopia/entity/Creature.java | 49 +++- .../unicopia/entity/Living.java | 11 + .../unicopia/entity/player/PlayerPhysics.java | 1 - .../unicopia/item/BellItem.java | 210 ++++++++++++++++++ .../minelittlepony/unicopia/item/UItems.java | 2 +- 5 files changed, 270 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/item/BellItem.java diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Creature.java b/src/main/java/com/minelittlepony/unicopia/entity/Creature.java index 1ea9520b..d9a92ce1 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/Creature.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/Creature.java @@ -38,6 +38,7 @@ public class Creature extends Living implements WeaklyOwned.Mutabl private static final TrackedData MASTER = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.NBT_COMPOUND); public static final TrackedData GRAVITY = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.FLOAT); private static final TrackedData EATING = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.INTEGER); + private static final TrackedData DISCORDED = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.BOOLEAN); public static void boostrap() {} @@ -52,11 +53,14 @@ public class Creature extends Living implements WeaklyOwned.Mutabl @Nullable private EatMuffinGoal eatMuffinGoal; + private boolean discordedChanged = true; + public Creature(LivingEntity entity) { super(entity, EFFECT); physics = new EntityPhysics<>(entity, GRAVITY); entity.getDataTracker().startTracking(MASTER, owner.toNBT()); entity.getDataTracker().startTracking(EATING, 0); + entity.getDataTracker().startTracking(DISCORDED, false); addTicker(physics); addTicker(this::updateConsumption); @@ -75,6 +79,15 @@ public class Creature extends Living implements WeaklyOwned.Mutabl return owner.getId().isPresent(); } + public boolean isDiscorded() { + return entity.getDataTracker().get(DISCORDED); + } + + public void setDiscorded(boolean discorded) { + entity.getDataTracker().set(DISCORDED, discorded); + discordedChanged = true; + } + @Override @NotNull public LivingEntity getMaster() { @@ -115,6 +128,10 @@ public class Creature extends Living implements WeaklyOwned.Mutabl initMinionAi(targets); } + if (isDiscorded()) { + initDiscordedAi(); + } + if (entity instanceof CreeperEntity mob) { goals.add(1, new FleeEntityGoal<>(mob, LivingEntity.class, 10, 1.5, 1.9, AmuletSelectors.ALICORN_AMULET)); } @@ -131,12 +148,32 @@ public class Creature extends Living implements WeaklyOwned.Mutabl .isEmpty(); }); - targets.clear(g -> true); + clearGoals(targets); targets.add(2, new ActiveTargetGoal<>((MobEntity)entity, PlayerEntity.class, true, filter)); targets.add(2, new ActiveTargetGoal<>((MobEntity)entity, HostileEntity.class, true, filter)); targets.add(2, new ActiveTargetGoal<>((MobEntity)entity, SlimeEntity.class, true, filter)); } + private void initDiscordedAi() { + targets.ifPresent(this::clearGoals); + // the brain drain + entity.getBrain().clear(); + if (entity instanceof MobEntity mob) { + mob.setTarget(null); + goals.ifPresent(goalSelector -> { + clearGoals(goalSelector); + goalSelector.add(1, new SwimGoal(mob)); + if (mob instanceof PathAwareEntity pae) { + goalSelector.add(5, new WanderAroundFarGoal(pae, 0.8)); + } + goalSelector.add(6, new LookAtEntityGoal(mob, PlayerEntity.class, 8.0f)); + goalSelector.add(6, new LookAroundGoal(mob)); + }); + } else { + goals.ifPresent(this::clearGoals); + } + } + public static void registerAttributes(DefaultAttributeContainer.Builder builder) { builder.add(EntityAttributes.GENERIC_ATTACK_DAMAGE); builder.add(EntityAttributes.GENERIC_ATTACK_KNOCKBACK); @@ -145,9 +182,17 @@ public class Creature extends Living implements WeaklyOwned.Mutabl @Override public boolean beforeUpdate() { + if (isDiscorded() && discordedChanged) { + discordedChanged = false; + initDiscordedAi(); + } return false; } + private void clearGoals(GoalSelector t) { + t.clear(g -> true); + } + private void updateConsumption() { if (isClient()) { eatTimer = entity.getDataTracker().get(EATING); @@ -227,6 +272,7 @@ public class Creature extends Living implements WeaklyOwned.Mutabl }); compound.put("master", owner.toNBT()); physics.toNBT(compound); + compound.putBoolean("discorded", isDiscorded()); } @Override @@ -242,5 +288,6 @@ public class Creature extends Living implements WeaklyOwned.Mutabl } } physics.fromNBT(compound); + setDiscorded(compound.getBoolean("discorded")); } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Living.java b/src/main/java/com/minelittlepony/unicopia/entity/Living.java index 9c46ac67..617414fd 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/Living.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/Living.java @@ -61,6 +61,8 @@ public abstract class Living implements Equine, Caste @Nullable private Caster attacker; + private Optional> target = Optional.empty(); + private int invinsibilityTicks; private final List tickers = new ArrayList<>(); @@ -135,6 +137,15 @@ public abstract class Living implements Equine, Caste entity.getDataTracker().set(CARRIER_ID, Optional.ofNullable(carrier).map(Entity::getUuid)); } + @Nullable + public Optional> getTarget() { + return target; + } + + public void setTarget(Living target) { + this.target = Optional.ofNullable(target); + } + public boolean isBeingCarried() { Entity vehicle = entity.getVehicle(); return vehicle != null && getCarrierId().filter(vehicle.getUuid()::equals).isPresent(); 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 984fcb9a..49a3a1dd 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -32,7 +32,6 @@ import net.minecraft.nbt.NbtCompound; import net.minecraft.particle.ParticleTypes; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvents; -import net.minecraft.text.Text; import net.minecraft.util.math.*; import net.minecraft.world.event.GameEvent; diff --git a/src/main/java/com/minelittlepony/unicopia/item/BellItem.java b/src/main/java/com/minelittlepony/unicopia/item/BellItem.java new file mode 100644 index 00000000..630122fb --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/BellItem.java @@ -0,0 +1,210 @@ +package com.minelittlepony.unicopia.item; + +import java.util.List; +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.entity.Creature; +import com.minelittlepony.unicopia.entity.Living; +import com.minelittlepony.unicopia.entity.player.Pony; +import com.minelittlepony.unicopia.particle.FollowingParticleEffect; +import com.minelittlepony.unicopia.particle.MagicParticleEffect; +import com.minelittlepony.unicopia.particle.UParticles; +import com.minelittlepony.unicopia.util.MagicalDamageSource; +import com.minelittlepony.unicopia.util.VecHelper; +import com.minelittlepony.unicopia.util.shape.Sphere; + +import net.minecraft.client.item.TooltipContext; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.CreeperEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.mob.IllagerEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.particle.ParticleEffect; +import net.minecraft.particle.ParticleTypes; +import net.minecraft.sound.SoundEvents; +import net.minecraft.text.Text; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.TypedActionResult; +import net.minecraft.util.UseAction; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class BellItem extends Item implements ChargeableItem { + + public BellItem(Settings settings) { + super(settings); + } + + @Override + public UseAction getUseAction(ItemStack stack) { + return UseAction.BOW; + } + + @Override + public void appendTooltip(ItemStack stack, @Nullable World world, List list, TooltipContext tooltipContext) { + list.add(Text.translatable(getTranslationKey() + ".charges", (int)Math.floor(ChargeableItem.getEnergy(stack)), getMaxCharge())); + } + + @Override + public int getMaxUseTime(ItemStack stack) { + return 3000; + } + + @Override + public ActionResult useOnEntity(ItemStack stack, PlayerEntity player, LivingEntity target, Hand hand) { + player.setCurrentHand(hand); + Pony pony = Pony.of(player); + pony.getCorruption().add(1); + pony.playSound(SoundEvents.BLOCK_BELL_USE, 0.4F, 0.2F); + Living targetLiving = target instanceof HostileEntity || target instanceof PlayerEntity ? Living.getOrEmpty(target) + .filter(living -> !(living instanceof Creature c && c.isDiscorded())) + .orElse(null) : null; + pony.setTarget(targetLiving); + return targetLiving == null ? ActionResult.FAIL : ActionResult.CONSUME_PARTIAL; + } + + @Override + public TypedActionResult use(World world, PlayerEntity player, Hand hand) { + ItemStack stack = player.getStackInHand(hand); + ItemStack offhandStack = AmuletItem.getForEntity(player); + + if (!(offhandStack.getItem() instanceof ChargeableItem)) { + offhandStack = player.getStackInHand(hand == Hand.MAIN_HAND ? Hand.OFF_HAND : Hand.MAIN_HAND); + } + + Pony pony = Pony.of(player); + + if (hasCharge(stack)) { + pony.playSound(SoundEvents.BLOCK_BELL_RESONATE, 0.6F, 1); + pony.getCorruption().add(1); + if (offhandStack.getItem() instanceof ChargeableItem chargeable) { + float maxChargeBy = chargeable.getMaxCharge() - ChargeableItem.getEnergy(offhandStack); + float energyTransferred = Math.min(ChargeableItem.getEnergy(stack), maxChargeBy); + chargeable.recharge(offhandStack, energyTransferred); + ChargeableItem.consumeEnergy(stack, energyTransferred); + } else { + pony.getMagicalReserves().getMana().add(ChargeableItem.getEnergy(stack)); + ChargeableItem.setEnergy(stack, 0); + } + + pony.spawnParticles(pony.getPhysics().getHeadPosition().toCenterPos(), new Sphere(false, 0.5F), 7, p -> { + pony.addParticle(new MagicParticleEffect(0xAAFFFF), p, Vec3d.ZERO); + }); + + return TypedActionResult.consume(stack); + } + + pony.playSound(SoundEvents.BLOCK_BELL_USE, 0.01F, 0.9F); + return TypedActionResult.consume(stack); + } + + @Override + public void usageTick(World world, LivingEntity userEntity, ItemStack stack, int remainingUseTicks) { + if (userEntity.age % 5 != 0) { + return; + } + + Living.getOrEmpty(userEntity).ifPresent(user -> { + user.getTarget().ifPresent(living -> { + float maxUseTime = getMaxUseTime(stack); + float progress = (maxUseTime - remainingUseTicks) / maxUseTime; + + if (tickDraining(user, living, stack, progress)) { + onStoppedDraining(user, living, true); + } + }); + }); + } + + private void onStoppedDraining(Living user, Living target, boolean completed) { + user.setTarget(null); + user.playSound(SoundEvents.BLOCK_BELL_USE, 0.2F, 0.3F); + if (target instanceof Creature creature && (completed || target.asEntity().getHealth() < (target.asEntity().getMaxHealth() * 0.5F) + 1)) { + creature.setDiscorded(true); + } + } + + private boolean tickDraining(Living user, Living living, ItemStack stack, float progress) { + if (living.getOrigin().getSquaredDistance(user.getOrigin()) > 25 || living.asEntity().isRemoved()) { + return true; + } + + float amountDrawn; + ParticleEffect particleType = ParticleTypes.COMPOSTER; + + if (living instanceof Pony pony) { + amountDrawn = pony.getMagicalReserves().getMana().get() * 0.2F; + pony.getMagicalReserves().getMana().multiply(0.8F); + if (pony.getActualSpecies() == Race.CHANGELING) { + particleType = ParticleTypes.HEART; + } + } else { + float damageAmount = Math.min(Math.max(1, living.asEntity().getMaxHealth() / 25F), living.asEntity().getHealth() - 1); + living.asEntity().damage(MagicalDamageSource.EXHAUSTION, damageAmount); + living.asEntity().setAttacker(user.asEntity()); + if (living.asEntity() instanceof MobEntity mob) { + mob.setTarget(null); + } + if (living.asEntity() instanceof CreeperEntity creeper) { + creeper.setFuseSpeed(0); + } + amountDrawn = Math.max(living.asEntity().getWidth(), living.asEntity().getHeight()); + if (living.asEntity() instanceof IllagerEntity) { + particleType = ParticleTypes.ANGRY_VILLAGER; + } + + if (living.asEntity().getHealth() <= 1) { + return true; + } + } + ChargeableItem.consumeEnergy(stack, -amountDrawn); + + user.playSound(SoundEvents.ENTITY_GUARDIAN_ATTACK, 0.2F, progress); + + for (int i = 0; i < 4; i++) { + living.addParticle( + new FollowingParticleEffect(UParticles.HEALTH_DRAIN, user.asEntity(), 0.4F) + .withChild(particleType), + living.getOriginVector().add(0, living.getPhysics().getGravitySignum() * living.asEntity().getHeight() / 2, 0) + .add(VecHelper.supply(() -> user.asWorld().random.nextTriangular(0, 0.2))), + Vec3d.ZERO + ); + } + + return false; + } + + @Override + public ItemStack finishUsing(ItemStack stack, World world, LivingEntity user) { + Living.getOrEmpty(user).ifPresent(living -> { + living.getTarget().ifPresent(target -> { + onStoppedDraining(living, target, true); + }); + }); + return super.finishUsing(stack, world, user); + } + + @Override + public void onStoppedUsing(ItemStack stack, World world, LivingEntity user, int remainingUseTicks) { + Living.getOrEmpty(user).ifPresent(living -> { + living.getTarget().ifPresent(target -> { + onStoppedDraining(living, target, false); + }); + }); + } + + @Override + public boolean hasGlint(ItemStack stack) { + return stack.hasEnchantments() || ChargeableItem.getEnergy(stack) > 0; + } + + @Override + public int getMaxCharge() { + return 1000; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java index 896ae225..754dd9a8 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java +++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java @@ -99,7 +99,7 @@ public interface UItems { Item GOLDEN_WING = register("golden_wing", new Item(new Item.Settings().rarity(Rarity.UNCOMMON)), ItemGroups.NATURAL); Item DRAGON_BREATH_SCROLL = register("dragon_breath_scroll", new DragonBreathScrollItem(new Item.Settings().rarity(Rarity.UNCOMMON)), ItemGroups.TOOLS); - Item GROGARS_BELL = register("grogars_bell", new Item(new Item.Settings().rarity(Rarity.RARE)), ItemGroups.TOOLS); + Item GROGARS_BELL = register("grogars_bell", new BellItem(new Item.Settings().rarity(Rarity.RARE).maxCount(1)), ItemGroups.TOOLS); Item MEADOWBROOKS_STAFF = register("meadowbrooks_staff", new StaffItem(new Settings().rarity(Rarity.UNCOMMON).maxCount(1).maxDamage(120)), ItemGroups.TOOLS); Item MAGIC_STAFF = register("magic_staff", new EnchantedStaffItem(new Settings().rarity(Rarity.UNCOMMON).maxCount(1).maxDamage(120)), ItemGroups.TOOLS);