Implement grogar's bell

This commit is contained in:
Sollace 2023-03-05 01:28:43 +00:00
parent b728416c17
commit 539f094582
5 changed files with 270 additions and 3 deletions

View file

@ -38,6 +38,7 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
private static final TrackedData<NbtCompound> MASTER = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.NBT_COMPOUND); private static final TrackedData<NbtCompound> MASTER = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.NBT_COMPOUND);
public static final TrackedData<Float> GRAVITY = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.FLOAT); public static final TrackedData<Float> GRAVITY = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.FLOAT);
private static final TrackedData<Integer> EATING = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.INTEGER); private static final TrackedData<Integer> EATING = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.INTEGER);
private static final TrackedData<Boolean> DISCORDED = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
public static void boostrap() {} public static void boostrap() {}
@ -52,11 +53,14 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
@Nullable @Nullable
private EatMuffinGoal eatMuffinGoal; private EatMuffinGoal eatMuffinGoal;
private boolean discordedChanged = true;
public Creature(LivingEntity entity) { public Creature(LivingEntity entity) {
super(entity, EFFECT); super(entity, EFFECT);
physics = new EntityPhysics<>(entity, GRAVITY); physics = new EntityPhysics<>(entity, GRAVITY);
entity.getDataTracker().startTracking(MASTER, owner.toNBT()); entity.getDataTracker().startTracking(MASTER, owner.toNBT());
entity.getDataTracker().startTracking(EATING, 0); entity.getDataTracker().startTracking(EATING, 0);
entity.getDataTracker().startTracking(DISCORDED, false);
addTicker(physics); addTicker(physics);
addTicker(this::updateConsumption); addTicker(this::updateConsumption);
@ -75,6 +79,15 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
return owner.getId().isPresent(); 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 @Override
@NotNull @NotNull
public LivingEntity getMaster() { public LivingEntity getMaster() {
@ -115,6 +128,10 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
initMinionAi(targets); initMinionAi(targets);
} }
if (isDiscorded()) {
initDiscordedAi();
}
if (entity instanceof CreeperEntity mob) { if (entity instanceof CreeperEntity mob) {
goals.add(1, new FleeEntityGoal<>(mob, LivingEntity.class, 10, 1.5, 1.9, AmuletSelectors.ALICORN_AMULET)); 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<LivingEntity> implements WeaklyOwned.Mutabl
.isEmpty(); .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, PlayerEntity.class, true, filter));
targets.add(2, new ActiveTargetGoal<>((MobEntity)entity, HostileEntity.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)); 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) { public static void registerAttributes(DefaultAttributeContainer.Builder builder) {
builder.add(EntityAttributes.GENERIC_ATTACK_DAMAGE); builder.add(EntityAttributes.GENERIC_ATTACK_DAMAGE);
builder.add(EntityAttributes.GENERIC_ATTACK_KNOCKBACK); builder.add(EntityAttributes.GENERIC_ATTACK_KNOCKBACK);
@ -145,9 +182,17 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
@Override @Override
public boolean beforeUpdate() { public boolean beforeUpdate() {
if (isDiscorded() && discordedChanged) {
discordedChanged = false;
initDiscordedAi();
}
return false; return false;
} }
private void clearGoals(GoalSelector t) {
t.clear(g -> true);
}
private void updateConsumption() { private void updateConsumption() {
if (isClient()) { if (isClient()) {
eatTimer = entity.getDataTracker().get(EATING); eatTimer = entity.getDataTracker().get(EATING);
@ -227,6 +272,7 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
}); });
compound.put("master", owner.toNBT()); compound.put("master", owner.toNBT());
physics.toNBT(compound); physics.toNBT(compound);
compound.putBoolean("discorded", isDiscorded());
} }
@Override @Override
@ -242,5 +288,6 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
} }
} }
physics.fromNBT(compound); physics.fromNBT(compound);
setDiscorded(compound.getBoolean("discorded"));
} }
} }

View file

@ -61,6 +61,8 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
@Nullable @Nullable
private Caster<?> attacker; private Caster<?> attacker;
private Optional<Living<?>> target = Optional.empty();
private int invinsibilityTicks; private int invinsibilityTicks;
private final List<Tickable> tickers = new ArrayList<>(); private final List<Tickable> tickers = new ArrayList<>();
@ -135,6 +137,15 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
entity.getDataTracker().set(CARRIER_ID, Optional.ofNullable(carrier).map(Entity::getUuid)); entity.getDataTracker().set(CARRIER_ID, Optional.ofNullable(carrier).map(Entity::getUuid));
} }
@Nullable
public Optional<Living<?>> getTarget() {
return target;
}
public void setTarget(Living<?> target) {
this.target = Optional.ofNullable(target);
}
public boolean isBeingCarried() { public boolean isBeingCarried() {
Entity vehicle = entity.getVehicle(); Entity vehicle = entity.getVehicle();
return vehicle != null && getCarrierId().filter(vehicle.getUuid()::equals).isPresent(); return vehicle != null && getCarrierId().filter(vehicle.getUuid()::equals).isPresent();

View file

@ -32,7 +32,6 @@ import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes; import net.minecraft.particle.ParticleTypes;
import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents; import net.minecraft.sound.SoundEvents;
import net.minecraft.text.Text;
import net.minecraft.util.math.*; import net.minecraft.util.math.*;
import net.minecraft.world.event.GameEvent; import net.minecraft.world.event.GameEvent;

View file

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

View file

@ -99,7 +99,7 @@ public interface UItems {
Item GOLDEN_WING = register("golden_wing", new Item(new Item.Settings().rarity(Rarity.UNCOMMON)), ItemGroups.NATURAL); 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 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 MEADOWBROOKS_STAFF = register("meadowbrooks_staff", new StaffItem(new Settings().rarity(Rarity.UNCOMMON).maxCount(1).maxDamage(120)), ItemGroups.TOOLS);
Item MAGIC_STAFF = register("magic_staff", new EnchantedStaffItem(new Settings().rarity(Rarity.UNCOMMON).maxCount(1).maxDamage(120)), ItemGroups.TOOLS); Item MAGIC_STAFF = register("magic_staff", new EnchantedStaffItem(new Settings().rarity(Rarity.UNCOMMON).maxCount(1).maxDamage(120)), ItemGroups.TOOLS);