Unicopia/src/main/java/com/minelittlepony/unicopia/entity/Living.java

544 lines
20 KiB
Java
Raw Normal View History

2021-02-14 16:52:56 +01:00
package com.minelittlepony.unicopia.entity;
import java.util.*;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
2021-08-04 15:38:03 +02:00
import org.jetbrains.annotations.Nullable;
2021-02-14 16:52:56 +01:00
import com.google.common.base.Preconditions;
import com.minelittlepony.unicopia.USounds;
2023-06-02 21:20:30 +02:00
import com.minelittlepony.unicopia.UTags;
2021-12-29 16:18:26 +01:00
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.Abilities;
2021-02-14 16:52:56 +01:00
import com.minelittlepony.unicopia.ability.magic.Caster;
2021-03-05 18:22:27 +01:00
import com.minelittlepony.unicopia.ability.magic.SpellContainer;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractDisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.advancement.UCriteria;
import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance;
import com.minelittlepony.unicopia.entity.duck.LivingEntityDuck;
2022-12-04 16:25:03 +01:00
import com.minelittlepony.unicopia.entity.effect.UEffects;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.input.Heuristic;
import com.minelittlepony.unicopia.input.Interactable;
import com.minelittlepony.unicopia.item.GlassesItem;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.network.datasync.EffectSync;
import com.minelittlepony.unicopia.network.datasync.Transmittable;
2022-09-18 01:23:29 +02:00
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.projectile.ProjectileImpactListener;
2023-04-30 11:46:33 +02:00
import com.minelittlepony.unicopia.server.world.DragonBreathStore;
import com.minelittlepony.unicopia.trinkets.TrinketsDelegate;
import com.minelittlepony.unicopia.util.*;
2021-02-14 16:52:56 +01:00
2023-07-06 18:27:03 +02:00
import it.unimi.dsi.fastutil.floats.Float2ObjectFunction;
2022-09-18 01:23:29 +02:00
import net.minecraft.entity.*;
2023-07-06 18:27:03 +02:00
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeInstance;
import net.minecraft.entity.attribute.EntityAttributeModifier;
2021-02-14 16:52:56 +01:00
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.data.*;
2022-09-18 01:23:29 +02:00
import net.minecraft.entity.player.PlayerEntity;
2021-02-14 16:52:56 +01:00
import net.minecraft.entity.projectile.ProjectileEntity;
import net.minecraft.item.ItemStack;
2021-08-04 15:38:03 +02:00
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket;
2022-09-18 01:23:29 +02:00
import net.minecraft.particle.ParticleTypes;
2023-06-02 21:20:30 +02:00
import net.minecraft.registry.tag.DamageTypeTags;
import net.minecraft.server.world.ServerWorld;
2022-09-18 01:23:29 +02:00
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Hand;
2022-09-18 01:23:29 +02:00
import net.minecraft.util.math.BlockPos;
2023-08-11 19:51:35 +02:00
import net.minecraft.util.math.Box;
2023-07-06 18:27:03 +02:00
import net.minecraft.util.math.MathHelper;
2022-09-18 01:23:29 +02:00
import net.minecraft.util.math.Vec3d;
2021-02-14 16:52:56 +01:00
public abstract class Living<T extends LivingEntity> implements Equine<T>, Caster<T>, Transmittable {
private static final TrackedData<Optional<UUID>> CARRIER_ID = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.OPTIONAL_UUID);
2021-02-14 16:52:56 +01:00
protected final T entity;
private final EffectSync effectDelegate;
private final Interactable sneakingHeuristic;
private final Interactable landedHeuristic;
private final Interactable jumpingHeuristic;
2021-02-14 16:52:56 +01:00
@Nullable
private Runnable landEvent;
private boolean invisible = false;
@Nullable
private Entity supportingEntity;
@Nullable
private Vec3d supportPositionOffset;
@Nullable
private Caster<?> attacker;
2023-03-05 02:28:43 +01:00
private Optional<Living<?>> target = Optional.empty();
private int invinsibilityTicks;
private final List<Tickable> tickers = new ArrayList<>();
private final Enchantments enchants = addTicker(new Enchantments(this));
private final ItemTracker armour = addTicker(new ItemTracker(this));
2021-08-04 15:38:03 +02:00
protected Living(T entity, TrackedData<NbtCompound> effect) {
2021-02-14 16:52:56 +01:00
this.entity = entity;
this.effectDelegate = new EffectSync(this, effect);
this.sneakingHeuristic = addTicker(new Interactable(entity::isSneaking));
this.landedHeuristic = addTicker(new Interactable(entity::isOnGround));
this.jumpingHeuristic = addTicker(new Interactable(((LivingEntityDuck)entity)::isJumping));
2021-08-04 15:38:03 +02:00
entity.getDataTracker().startTracking(effect, new NbtCompound());
entity.getDataTracker().startTracking(CARRIER_ID, Optional.empty());
2021-02-14 16:52:56 +01:00
}
public <Q extends Tickable> Q addTicker(Q tickable) {
tickers.add(Preconditions.checkNotNull(tickable, "tickable"));
return tickable;
}
public boolean isInvisible() {
return invisible && SpellPredicate.IS_DISGUISE.isOn(this);
}
public void setInvisible(boolean invisible) {
this.invisible = invisible;
}
2021-02-14 16:52:56 +01:00
public void waitForFall(Runnable action) {
if (entity.isOnGround()) {
action.run();
} else {
landEvent = action;
}
2021-02-14 16:52:56 +01:00
}
public boolean sneakingChanged() {
return sneakingHeuristic.hasChanged(Heuristic.ONCE);
2021-02-14 16:52:56 +01:00
}
public boolean landedChanged() {
return landedHeuristic.hasChanged(Heuristic.ONCE);
}
public Interactable getJumpingHeuristic() {
return jumpingHeuristic;
2021-02-14 16:52:56 +01:00
}
@Override
2021-03-05 18:22:27 +01:00
public SpellContainer getSpellSlot() {
2021-02-14 16:52:56 +01:00
return effectDelegate;
}
public Enchantments getEnchants() {
return enchants;
}
public ItemTracker getArmour() {
return armour;
}
@Override
public final T asEntity() {
2021-02-14 16:52:56 +01:00
return entity;
}
public Optional<UUID> getCarrierId() {
return entity.getDataTracker().get(CARRIER_ID);
}
public void setCarrier(UUID carrier) {
entity.getDataTracker().set(CARRIER_ID, Optional.ofNullable(carrier));
}
public void setCarrier(Entity carrier) {
entity.getDataTracker().set(CARRIER_ID, Optional.ofNullable(carrier).map(Entity::getUuid));
}
2023-03-05 02:28:43 +01:00
@Nullable
public Optional<Living<?>> 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();
}
2023-08-11 19:51:35 +02:00
public void setSupportingEntity(Entity supportingEntity) {
this.supportingEntity = supportingEntity;
}
public void setPositionOffset(@Nullable Vec3d positionOffset) {
2023-08-11 19:51:35 +02:00
this.supportPositionOffset = positionOffset;
}
public void updatePositionOffset() {
setPositionOffset(supportingEntity == null ? null : entity.getPos().subtract(supportingEntity.getPos()));
}
public static void checkGroundCollission(Entity entity, Box box) {
double height = box.getYLength();
if (height < 3 || entity.getBoundingBox().minY > box.minY + height / 2D) {
if (entity.getBoundingBox().minY < box.maxY) {
entity.setPos(entity.getX(), box.maxY - 0.002, entity.getZ());
}
if (entity.getBoundingBox().minY > box.maxY) {
entity.setPos(entity.getX(), box.maxY - 0.002, entity.getZ());
}
}
2023-08-11 19:51:35 +02:00
}
public void updateRelativePosition(Box box) {
2023-08-11 19:51:35 +02:00
if (supportingEntity == null || supportPositionOffset == null) {
return;
}
2023-08-11 19:51:35 +02:00
Vec3d newPos = supportingEntity.getPos().add(supportPositionOffset);
Vec3d posChange = entity.getPos().subtract(newPos);
entity.setPosition(newPos);
if (isClient()) {
Vec3d newServerPos = LivingEntityDuck.serverPos(entity);
if (newServerPos.lengthSquared() != 0) {
newServerPos = newServerPos.subtract(posChange);
entity.updateTrackedPositionAndAngles(
newServerPos.x, newServerPos.y, newServerPos.z,
entity.getYaw(), entity.getPitch(), 3, true);
}
} else {
entity.updateTrackedPosition(newPos.x, newPos.y, newPos.z);
}
if (!(entity instanceof PlayerEntity)) {
entity.lastRenderX = supportingEntity.lastRenderX + supportPositionOffset.x;
entity.lastRenderY = supportingEntity.lastRenderY + supportPositionOffset.y;
entity.lastRenderZ = supportingEntity.lastRenderZ + supportPositionOffset.z;
if (entity.getVelocity().length() < 0.1) {
LimbAnimationUtil.resetToZero(entity.limbAnimator);
}
}
entity.horizontalSpeed = 0;
entity.prevHorizontalSpeed = 0;
entity.speed = 0;
entity.setOnGround(true);
entity.verticalCollision = true;
entity.groundCollision = true;
entity.fallDistance = 0;
}
@Override
public boolean beforeUpdate() {
updateSupportingEntity();
return false;
}
public void updateSupportingEntity() {
2023-08-11 19:51:35 +02:00
if (supportingEntity != null) {
Box ownBox = entity.getBoundingBox().expand(0.1);
MultiBoundingBoxEntity.getBoundingBoxes(supportingEntity).stream().filter(box -> {
2023-08-11 19:51:35 +02:00
return box.expand(0, 0.5, 0).intersects(ownBox);
}).findFirst().ifPresentOrElse(box -> {
if (supportPositionOffset == null) {
updatePositionOffset();
} else {
updateRelativePosition(box);
}
entity.setOnGround(true);
entity.verticalCollision = true;
entity.groundCollision = true;
}, () -> {
2023-08-11 19:51:35 +02:00
supportingEntity = null;
supportPositionOffset = null;
});
2023-08-11 19:51:35 +02:00
}
}
2021-02-14 16:52:56 +01:00
@Override
public void tick() {
tickers.forEach(Tickable::tick);
2021-12-29 16:18:26 +01:00
try {
2023-06-03 13:40:54 +02:00
getSpellSlot().forEach(spell -> Operation.ofBoolean(spell.tick(this, Situation.BODY)), entity.getWorld().isClient);
2021-12-29 16:18:26 +01:00
} catch (Exception e) {
2022-12-19 16:10:09 +01:00
Unicopia.LOGGER.error("Error whilst ticking spell on entity {}", entity, e);
2021-12-29 16:18:26 +01:00
}
2021-02-14 16:52:56 +01:00
if (!(entity instanceof PlayerEntity)) {
if (!entity.hasVehicle() && getCarrierId().isPresent() && !asWorld().isClient && entity.age % 10 == 0) {
UUID carrierId = getCarrierId().get();
Entity carrier = ((ServerWorld)asWorld()).getEntity(carrierId);
if (carrier != null) {
asEntity().startRiding(carrier, true);
Living.transmitPassengers(carrier);
} else {
Unicopia.LOGGER.warn("No passenger with id {]", carrierId);
}
}
}
if (invinsibilityTicks > 0) {
invinsibilityTicks--;
}
if (landEvent != null && entity.isOnGround() && landedChanged()) {
landEvent.run();
landEvent = null;
}
2021-02-14 16:52:56 +01:00
2022-12-04 16:25:03 +01:00
if (entity.hasStatusEffect(UEffects.PARALYSIS) && entity.getVelocity().horizontalLengthSquared() > 0) {
entity.setVelocity(entity.getVelocity().multiply(0, 1, 0));
updateVelocity();
}
if (isBeingCarried()) {
Pony carrier = Pony.of(entity.getVehicle()).orElse(null);
if (!carrier.getCompositeRace().any(Abilities.CARRY::canUse)) {
entity.stopRiding();
entity.refreshPositionAfterTeleport(carrier.getOriginVector());
Living.transmitPassengers(carrier.asEntity());
}
entity.setYaw(carrier.asEntity().getYaw());
}
updateDragonBreath();
2023-08-11 19:51:35 +02:00
updatePositionOffset();
}
2022-09-18 01:23:29 +02:00
2023-07-06 18:27:03 +02:00
public void updateAttributeModifier(UUID id, EntityAttribute attribute, float desiredValue, Float2ObjectFunction<EntityAttributeModifier> modifierSupplier, boolean permanent) {
@Nullable
EntityAttributeInstance instance = asEntity().getAttributeInstance(attribute);
if (instance == null) {
return;
}
@Nullable
EntityAttributeModifier modifier = instance.getModifier(id);
if (!MathHelper.approximatelyEquals(desiredValue, modifier == null ? 0 : modifier.getValue())) {
if (modifier != null) {
instance.removeModifier(modifier);
}
if (desiredValue != 0) {
if (permanent) {
instance.addPersistentModifier(modifierSupplier.get(desiredValue));
} else {
instance.addTemporaryModifier(modifierSupplier.get(desiredValue));
}
}
}
}
2023-05-12 15:49:00 +02:00
public boolean canBeSeenBy(Entity entity) {
return !isInvisible()
&& getSpellSlot()
.get(SpellPredicate.IS_DISGUISE, true)
.filter(spell -> spell.getDisguise().getAppearance() == entity)
.isEmpty();
}
private void updateDragonBreath() {
2023-06-03 13:40:54 +02:00
if (!entity.getWorld().isClient && (entity instanceof PlayerEntity || entity.hasCustomName())) {
2022-09-18 01:23:29 +02:00
Vec3d targetPos = entity.getRotationVector().multiply(2).add(entity.getEyePos());
2023-06-02 21:20:30 +02:00
if (entity.getWorld().isAir(BlockPos.ofFloored(targetPos))) {
2023-06-03 13:40:54 +02:00
DragonBreathStore store = DragonBreathStore.get(entity.getWorld());
2022-09-18 01:23:29 +02:00
String name = entity.getDisplayName().getString();
store.popEntries(name).forEach(stack -> {
Vec3d randomPos = targetPos.add(VecHelper.supply(() -> entity.getRandom().nextTriangular(0.1, 0.5)));
2023-06-02 21:20:30 +02:00
if (!entity.getWorld().isAir(BlockPos.ofFloored(randomPos))) {
2022-09-18 01:23:29 +02:00
store.put(name, stack.payload());
}
for (int i = 0; i < 10; i++) {
2023-06-03 13:40:54 +02:00
ParticleUtils.spawnParticle(entity.getWorld(), ParticleTypes.FLAME, randomPos.add(
2022-09-18 01:23:29 +02:00
VecHelper.supply(() -> entity.getRandom().nextTriangular(0.1, 0.5))
), Vec3d.ZERO);
}
2023-06-03 13:40:54 +02:00
ItemEntity item = EntityType.ITEM.create(entity.getWorld());
2022-09-18 01:23:29 +02:00
item.setStack(stack.payload());
item.setPosition(randomPos);
2023-06-03 13:40:54 +02:00
item.getWorld().spawnEntity(item);
entity.getWorld().playSoundFromEntity(null, entity, USounds.ITEM_DRAGON_BREATH_ARRIVE, entity.getSoundCategory(), 1, 1);
if (stack.payload().getItem() == UItems.OATS && entity instanceof PlayerEntity player) {
UCriteria.RECEIVE_OATS.trigger(player);
}
2022-09-18 01:23:29 +02:00
});
}
}
2021-02-14 16:52:56 +01:00
}
public boolean onUpdatePassengerPosition(Entity passender, Entity.PositionUpdater positionUpdater) {
return false;
}
2021-02-14 16:52:56 +01:00
public void onJump() {
if (getPhysics().isGravityNegative()) {
entity.setVelocity(entity.getVelocity().multiply(1, -1, 1));
}
}
@Nullable
public final Caster<?> getAttacker() {
return attacker;
}
public Optional<Boolean> onDamage(DamageSource source, float amount) {
2023-06-02 21:20:30 +02:00
if (source.isIn(DamageTypeTags.IS_LIGHTNING) && (invinsibilityTicks > 0 || tryCaptureLightning())) {
return Optional.of(false);
}
if (source instanceof MagicalDamageSource magical) {
Caster<?> attacker = ((MagicalDamageSource)source).getSpell();
if (attacker != null) {
this.attacker = attacker;
}
2023-06-02 21:20:30 +02:00
if (magical.isIn(UTags.BREAKS_SUNGLASSES)) {
ItemStack glasses = GlassesItem.getForEntity(entity);
if (glasses.getItem() == UItems.SUNGLASSES) {
ItemStack broken = UItems.BROKEN_SUNGLASSES.getDefaultStack();
broken.setNbt(glasses.getNbt());
TrinketsDelegate.getInstance().setEquippedStack(entity, TrinketsDelegate.FACE, broken);
2022-09-26 21:13:03 +02:00
playSound(SoundEvents.BLOCK_GLASS_BREAK, 1, 1);
}
}
}
return Optional.empty();
}
public Optional<BlockPos> chooseClimbingPos() {
return getSpellSlot().get(SpellPredicate.IS_DISGUISE, false)
.map(AbstractDisguiseSpell::getDisguise)
.filter(EntityAppearance::canClimbWalls)
.map(v -> entity.getBlockPos());
}
private boolean tryCaptureLightning() {
return getInventoryStacks().filter(stack -> !stack.isEmpty() && stack.getItem() == UItems.EMPTY_JAR).findFirst().map(stack -> {
invinsibilityTicks = 20;
stack.split(1);
giveBackItem(UItems.LIGHTNING_JAR.getDefaultStack());
return stack;
}).isPresent();
}
protected Stream<ItemStack> getInventoryStacks() {
return Stream.of(entity.getStackInHand(Hand.MAIN_HAND), entity.getStackInHand(Hand.OFF_HAND));
}
protected Stream<ItemStack> getArmourStacks() {
if (!TrinketsDelegate.hasTrinkets()) {
return StreamSupport.stream(entity.getArmorItems().spliterator(), false);
}
return Stream.concat(
TrinketsDelegate.getInstance().getEquipped(entity, TrinketsDelegate.NECKLACE),
StreamSupport.stream(entity.getArmorItems().spliterator(), false)
);
}
protected void giveBackItem(ItemStack stack) {
entity.dropStack(stack);
}
2021-02-14 16:52:56 +01:00
@Override
public boolean onProjectileImpact(ProjectileEntity projectile) {
2021-03-03 10:33:23 +01:00
return getSpellSlot().get(true)
.filter(effect -> !effect.isDead()
&& effect instanceof ProjectileImpactListener
&& ((ProjectileImpactListener)effect).onProjectileImpact(projectile))
2021-03-03 10:33:23 +01:00
.isPresent();
2021-02-14 16:52:56 +01:00
}
2021-08-04 15:38:03 +02:00
protected void handleFall(float distance, float damageMultiplier, DamageSource cause) {
getSpellSlot().get(SpellPredicate.IS_DISGUISE, false).ifPresent(spell -> {
2021-08-04 15:38:03 +02:00
spell.getDisguise().onImpact(this, distance, damageMultiplier, cause);
2021-02-14 16:52:56 +01:00
});
}
@Override
public void setDirty() {}
@Override
2021-08-04 15:38:03 +02:00
public void toNBT(NbtCompound compound) {
enchants.toNBT(compound);
effectDelegate.toNBT(compound);
toSyncronisedNbt(compound);
}
@Override
2021-08-04 15:38:03 +02:00
public void fromNBT(NbtCompound compound) {
enchants.fromNBT(compound);
effectDelegate.fromNBT(compound);
fromSynchronizedNbt(compound);
}
@Override
public void toSyncronisedNbt(NbtCompound compound) {
compound.put("armour", armour.toNBT());
getCarrierId().ifPresent(id -> compound.putUuid("carrier", id));
}
@Override
public void fromSynchronizedNbt(NbtCompound compound) {
armour.fromNBT(compound.getCompound("armour"));
setCarrier(compound.containsUuid("carrier") ? compound.getUuid("carrier") : null);
}
2022-09-26 21:13:03 +02:00
public void updateVelocity() {
updateVelocity(entity);
}
public static Optional<Living<?>> getOrEmpty(Entity entity) {
return Equine.of(entity, a -> a instanceof Living);
}
public static Living<?> living(Entity entity) {
return getOrEmpty(entity).orElse(null);
}
public static <E extends LivingEntity> Living<E> living(E entity) {
return Equine.<E, Living<E>>of(entity, e -> e instanceof Living<?>).orElse(null);
}
public static void updateVelocity(@Nullable Entity entity) {
if (entity != null) {
entity.velocityModified = true;
//if (entity instanceof ServerPlayerEntity ply) {
// ply.networkHandler.sendPacket(new EntityVelocityUpdateS2CPacket(ply));
//}
}
2022-09-26 21:13:03 +02:00
}
public static void transmitPassengers(@Nullable Entity entity) {
2023-06-03 13:40:54 +02:00
if (entity != null && entity.getWorld() instanceof ServerWorld sw) {
sw.getChunkManager().sendToNearbyPlayers(entity, new EntityPassengersSetS2CPacket(entity));
}
}
2021-02-14 16:52:56 +01:00
}