diff --git a/src/main/java/com/minelittlepony/unicopia/Owned.java b/src/main/java/com/minelittlepony/unicopia/Owned.java index 37642a1d..337a59ed 100644 --- a/src/main/java/com/minelittlepony/unicopia/Owned.java +++ b/src/main/java/com/minelittlepony/unicopia/Owned.java @@ -5,6 +5,8 @@ import java.util.UUID; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.item.FriendshipBraceletItem; + import net.minecraft.entity.Entity; /** @@ -27,14 +29,23 @@ public interface Owned { * Since {@link Owned#getMaster()} will only return if the owner is loaded, use this to perform checks * in the owner's absence. */ - default Optional getMasterId() { - return Optional.of(getMaster()).map(Entity::getUuid); + Optional getMasterId(); + + default boolean isOwnerOrFriend(Entity target) { + return FriendshipBraceletItem.isComrade(this, target) || isOwnerOrVehicle(target); + } + + default boolean isOwnerOrVehicle(@Nullable Entity target) { + if (isOwnedBy(target)) { + return true; + } + + Entity owner = getMaster(); + return target != null && owner != null && owner.isConnectedThroughVehicle(target); } default boolean isOwnedBy(@Nullable Object owner) { - return owner instanceof Entity e - && getMasterId().isPresent() - && e.getUuid().equals(getMasterId().get()); + return owner instanceof Entity e && e.getUuid().equals(getMasterId().orElse(null)); } default boolean hasCommonOwner(Owned sibling) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/Affine.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/Affine.java index 007da627..7cdfdaef 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/Affine.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/Affine.java @@ -19,4 +19,8 @@ public interface Affine { default boolean isFriendlyTogether(Affine other) { return getAffinity() != Affinity.BAD && other.getAffinity() != Affinity.BAD; } + + default boolean applyInversion(Affine other, boolean friendly) { + return isEnemy(other) ? !friendly : friendly; + } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java index 7c05d439..02eb96d3 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java @@ -97,7 +97,6 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega } } - @Override public boolean isFriendlyTogether(Affine other) { return accumulatedMass < 4; diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireBoltSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireBoltSpell.java index c2324a5f..a76ef43b 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireBoltSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireBoltSpell.java @@ -48,7 +48,7 @@ public class FireBoltSpell extends AbstractSpell implements HomingSpell, if (caster instanceof MagicProjectileEntity && getTraits().get(Trait.FOCUS) >= 50) { caster.findAllEntitiesInRange( getTraits().get(Trait.FOCUS) - 49, - EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.notOwnerOrFriend(this, caster)) + EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.validTarget(this, caster)) ).findFirst().ifPresent(target -> { ((MagicProjectileEntity)caster).setHomingTarget(target); }); @@ -60,7 +60,7 @@ public class FireBoltSpell extends AbstractSpell implements HomingSpell, if (getTraits().get(Trait.FOCUS) >= 50 && target.getOrEmpty(caster.asWorld()).isEmpty()) { target.set(caster.findAllEntitiesInRange( getTraits().get(Trait.FOCUS) - 49, - EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.notOwnerOrFriend(this, caster)) + EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.validTarget(this, caster)) ).findFirst().orElse(null)); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java index fcac287e..fa2963f5 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java @@ -24,7 +24,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; import net.minecraft.nbt.NbtCompound; import net.minecraft.network.packet.s2c.play.PositionFlag; -import net.minecraft.particle.ParticleEffect; import net.minecraft.particle.ParticleTypes; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; @@ -101,13 +100,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme if (situation == Situation.GROUND) { if (source.isClient()) { - Vec3d origin = source.getOriginVector(); - - ParticleEffect effect = getTarget() - .map(target -> (ParticleEffect)new FollowingParticleEffect(UParticles.HEALTH_DRAIN, target.pos(), 0.2F).withChild(ParticleTypes.ELECTRIC_SPARK)) - .orElse(ParticleTypes.ELECTRIC_SPARK); - - source.spawnParticles(origin, particleArea, 5, pos -> { + source.spawnParticles(particleArea, 5, pos -> { source.addParticle(ParticleTypes.ELECTRIC_SPARK, pos, Vec3d.ZERO); }); } else { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java index 5ad19d2d..04831ef6 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java @@ -41,7 +41,7 @@ public class ShieldSpell extends AbstractSpell { .with(Trait.AIR, 9) .build(); - private final TargetSelecter targetSelecter = new TargetSelecter(this); + private final TargetSelecter targetSelecter = new TargetSelecter(this).setFilter(this::isValidTarget); private float prevRadius; private float radius; @@ -174,11 +174,8 @@ public class ShieldSpell extends AbstractSpell { } protected long applyEntities(Caster source) { - double radius = this.radius; - Vec3d origin = getOrigin(source); - - targetSelecter.getEntities(source, radius, this::isValidTarget).forEach(i -> { + targetSelecter.getEntities(source, radius).forEach(i -> { try { applyRadialEffect(source, i, i.getPos().distanceTo(origin), radius); } catch (Throwable e) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java index d1730e78..87119eab 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java @@ -11,26 +11,31 @@ import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.ability.magic.Affine; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.Spell; -import com.minelittlepony.unicopia.entity.player.Pony; -import com.minelittlepony.unicopia.item.FriendshipBraceletItem; import net.minecraft.entity.Entity; +import net.minecraft.predicate.entity.EntityPredicates; public class TargetSelecter { - private final Map targets = new TreeMap<>(); private final Spell spell; + private BiPredicate, Entity> filter = (a, b) -> true; + public TargetSelecter(Spell spell) { this.spell = spell; } - public Stream getEntities(Caster source, double radius, BiPredicate, Entity> filter) { + public TargetSelecter setFilter(BiPredicate, Entity> filter) { + this.filter = filter; + return this; + } + + public Stream getEntities(Caster source, double radius) { targets.values().removeIf(Target::tick); return source.findAllEntitiesInRange(radius) - .filter(entity -> entity.isAlive() && !entity.isRemoved() && notOwnerOrFriend(spell, source, entity)) + .filter(EntityPredicates.VALID_ENTITY) .filter(EquinePredicates.EXCEPT_MAGIC_IMMUNE) - .filter(e -> filter.test(source, e)) + .filter(entity -> entity != source.asEntity() && validTarget(spell, source, entity) && filter.test(source, entity)) .map(i -> { targets.computeIfAbsent(i.getUuid(), Target::new); return i; @@ -41,35 +46,19 @@ public class TargetSelecter { return targets.values().stream().filter(Target::canHurt).count(); } - public static Predicate notOwnerOrFriend(Affine affine, Caster source) { - return target -> notOwnerOrFriend(affine, source, target); + public static Predicate validTarget(Affine affine, Caster source) { + return target -> validTarget(affine, source, target); } - public static Predicate isOwnerOrFriend(Affine affine, Caster source) { - return target -> isOwnerOrFriend(affine, source, target); - } - - public static boolean notOwnerOrFriend(Affine affine, Caster source, Entity target) { + public static boolean validTarget(Affine affine, Caster source, Entity target) { return !isOwnerOrFriend(affine, source, target); } - public static boolean isOwnerOrFriend(Affine affine, Caster source, Entity target) { - Entity owner = source.getMaster(); - - var equine = Pony.of(target); - if (equine.isPresent() && !affine.isFriendlyTogether(equine.get())) { - return false; - } - - if (affine.isEnemy(source)) { - return FriendshipBraceletItem.isComrade(source, target); - } - - return FriendshipBraceletItem.isComrade(source, target) - || (owner != null && (Pony.equal(target, owner) || owner.isConnectedThroughVehicle(target))); + public static boolean isOwnerOrFriend(Affine affine, Caster source, Entity target) { + return affine.applyInversion(source, source.isOwnerOrFriend(target)); } - static final class Target { + private static final class Target { private int cooldown = 20; Target(UUID id) { } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Creature.java b/src/main/java/com/minelittlepony/unicopia/entity/Creature.java index eec0a798..82039a7f 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/Creature.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/Creature.java @@ -60,7 +60,7 @@ public class Creature extends Living implements WeaklyOwned.Mutabl private boolean discordedChanged = true; private int smittenTicks; - private final Predicate targetPredicate = TargetSelecter.notOwnerOrFriend(() -> getOriginatingCaster().getAffinity(), this).and(e -> { + private final Predicate targetPredicate = TargetSelecter.validTarget(() -> getOriginatingCaster().getAffinity(), this).and(e -> { return Equine.of(e) .filter(eq -> eq instanceof Creature) .filter(eq -> isDiscorded() != ((Creature)eq).hasCommonOwner(this)) 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 666ca9c2..7b1f6ccf 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -362,6 +362,11 @@ public class Pony extends Living implements Copyable, Update return asEntity(); } + @Override + public Optional getMasterId() { + return Optional.of(asEntity().getUuid()); + } + public void onSpawn() { if (entity.getWorld() instanceof ServerWorld sw && sw.getServer().getSaveProperties().getGameMode() != GameMode.ADVENTURE) { boolean mustAvoidSun = getObservedSpecies() == Race.BAT && MeteorlogicalUtil.isPositionExposedToSun(sw, getOrigin()); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyClientPlayerEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyClientPlayerEntity.java index f7ee0cde..926581a9 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyClientPlayerEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyClientPlayerEntity.java @@ -1,5 +1,8 @@ package com.minelittlepony.unicopia.entity.player.dummy; +import java.util.Optional; +import java.util.UUID; + import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -13,6 +16,7 @@ import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.PlayerListEntry; import net.minecraft.client.render.entity.PlayerModelPart; import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; @@ -79,4 +83,9 @@ public class DummyClientPlayerEntity extends AbstractClientPlayerEntity implemen public void setMaster(PlayerEntity owner) { this.owner = owner; } + + @Override + public Optional getMasterId() { + return Optional.ofNullable(owner).map(Entity::getUuid); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyPlayerEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyPlayerEntity.java index 798dc6e1..c5ce8f09 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyPlayerEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyPlayerEntity.java @@ -1,5 +1,8 @@ package com.minelittlepony.unicopia.entity.player.dummy; +import java.util.Optional; +import java.util.UUID; + import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.InteractionManager; @@ -7,6 +10,7 @@ import com.minelittlepony.unicopia.Owned; import com.mojang.authlib.GameProfile; import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; @@ -42,6 +46,11 @@ public class DummyPlayerEntity extends PlayerEntity implements Owned getMasterId() { + return Optional.ofNullable(owner).map(Entity::getUuid); + } + @Override public boolean shouldRenderName() { return !InteractionManager.instance().isClientPlayer(getMaster()); diff --git a/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java b/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java index da2018d0..4062ae4a 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java @@ -7,6 +7,7 @@ import java.util.stream.Stream; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.EquinePredicates; +import com.minelittlepony.unicopia.Owned; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.compat.trinkets.TrinketsDelegate; @@ -113,15 +114,10 @@ public class FriendshipBraceletItem extends WearableItem implements DyeableItem, && ((FriendshipBraceletItem)stack.getItem()).checkSignature(stack, player); } - public static boolean isComrade(Caster caster, Entity entity) { - if (entity instanceof LivingEntity) { - return caster.getMasterId() - .filter(id -> getWornBangles((LivingEntity)entity) - .anyMatch(stack -> isSignedBy(stack, id)) - ) - .isPresent(); - } - return false; + public static boolean isComrade(Owned caster, Entity entity) { + return entity instanceof LivingEntity l && caster.getMasterId() + .filter(id -> getWornBangles(l).anyMatch(stack -> isSignedBy(stack, id))) + .isPresent(); } public static Stream getPartyMembers(Caster caster, double radius) {