Fix shields targetting the entity it belongs to

This commit is contained in:
Sollace 2024-01-26 15:57:36 +00:00
parent bbe9eb0068
commit 4151b66e99
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
12 changed files with 71 additions and 59 deletions

View file

@ -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<E extends Entity> {
* Since {@link Owned#getMaster()} will only return if the owner is loaded, use this to perform checks
* in the owner's absence.
*/
default Optional<UUID> getMasterId() {
return Optional.of(getMaster()).map(Entity::getUuid);
Optional<UUID> 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) {

View file

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

View file

@ -97,7 +97,6 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
}
}
@Override
public boolean isFriendlyTogether(Affine other) {
return accumulatedMass < 4;

View file

@ -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));
}

View file

@ -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 {

View file

@ -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) {

View file

@ -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<UUID, Target> targets = new TreeMap<>();
private final Spell spell;
private BiPredicate<Caster<?>, Entity> filter = (a, b) -> true;
public TargetSelecter(Spell spell) {
this.spell = spell;
}
public Stream<Entity> getEntities(Caster<?> source, double radius, BiPredicate<Caster<?>, Entity> filter) {
public TargetSelecter setFilter(BiPredicate<Caster<?>, Entity> filter) {
this.filter = filter;
return this;
}
public Stream<Entity> 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 <T extends Entity> Predicate<T> notOwnerOrFriend(Affine affine, Caster<?> source) {
return target -> notOwnerOrFriend(affine, source, target);
public static <T extends Entity> Predicate<T> validTarget(Affine affine, Caster<?> source) {
return target -> validTarget(affine, source, target);
}
public static <T extends Entity> Predicate<T> isOwnerOrFriend(Affine affine, Caster<?> source) {
return target -> isOwnerOrFriend(affine, source, target);
}
public static <T extends Entity> 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 <T extends Entity> 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) { }

View file

@ -60,7 +60,7 @@ public class Creature extends Living<LivingEntity> implements WeaklyOwned.Mutabl
private boolean discordedChanged = true;
private int smittenTicks;
private final Predicate<LivingEntity> targetPredicate = TargetSelecter.<LivingEntity>notOwnerOrFriend(() -> getOriginatingCaster().getAffinity(), this).and(e -> {
private final Predicate<LivingEntity> targetPredicate = TargetSelecter.<LivingEntity>validTarget(() -> getOriginatingCaster().getAffinity(), this).and(e -> {
return Equine.of(e)
.filter(eq -> eq instanceof Creature)
.filter(eq -> isDiscorded() != ((Creature)eq).hasCommonOwner(this))

View file

@ -362,6 +362,11 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
return asEntity();
}
@Override
public Optional<UUID> 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());

View file

@ -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<UUID> getMasterId() {
return Optional.ofNullable(owner).map(Entity::getUuid);
}
}

View file

@ -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<PlayerEntit
this.owner = owner;
}
@Override
public Optional<UUID> getMasterId() {
return Optional.ofNullable(owner).map(Entity::getUuid);
}
@Override
public boolean shouldRenderName() {
return !InteractionManager.instance().isClientPlayer(getMaster());

View file

@ -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<Pony> getPartyMembers(Caster<?> caster, double radius) {