mirror of
https://github.com/Sollace/Unicopia.git
synced 2025-02-01 03:26:44 +01:00
Allow spells and entities to retain their owner if the owner is offline (multiplayer) or in another dimension (nether), and handle situations where the owner is not around (getOwner() == null)
This commit is contained in:
parent
e3d1f8c973
commit
e6b0ad9fa4
32 changed files with 290 additions and 153 deletions
|
@ -1,19 +1,55 @@
|
|||
package com.minelittlepony.unicopia;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
|
||||
/**
|
||||
* Interface for things that can be owned.
|
||||
* Interface for things that can be owned by an entity.
|
||||
* <p>
|
||||
* Ownership is retained so long as the owner is still active. If the owner leaves or dies, the link is broken.
|
||||
*
|
||||
* @param <E> The type of object that owns us.
|
||||
*/
|
||||
public interface Owned<E> {
|
||||
public interface Owned<E extends Entity> {
|
||||
/**
|
||||
* Gets the owner that holds this object.
|
||||
*/
|
||||
@Nullable
|
||||
E getMaster();
|
||||
|
||||
/**
|
||||
* Gets the unique entity id of the entity that holds this object.
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the owner of this object.
|
||||
*/
|
||||
void setMaster(E owner);
|
||||
void setMaster(@Nullable E owner);
|
||||
|
||||
/**
|
||||
* Gets the owner that holds this object.
|
||||
* Updated the owner of this object to be the same as another.
|
||||
*
|
||||
* @param sibling
|
||||
*/
|
||||
E getMaster();
|
||||
default void setMaster(Owned<? extends E> sibling) {
|
||||
setMaster(sibling.getMaster());
|
||||
}
|
||||
|
||||
default boolean isOwnedBy(@Nullable Object owner) {
|
||||
return owner instanceof Entity e && getMasterId().isPresent() && e.getUuid().equals(getMasterId().get());
|
||||
}
|
||||
|
||||
default boolean hasCommonOwner(Owned<?> sibling) {
|
||||
return getMasterId().isPresent() && getMasterId().equals(sibling.getMasterId());
|
||||
}
|
||||
}
|
||||
|
|
54
src/main/java/com/minelittlepony/unicopia/WeaklyOwned.java
Normal file
54
src/main/java/com/minelittlepony/unicopia/WeaklyOwned.java
Normal file
|
@ -0,0 +1,54 @@
|
|||
package com.minelittlepony.unicopia;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.entity.EntityReference;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/**
|
||||
* Interface for things that can be weakly owned (by an entity).
|
||||
* Ownership links for these kinds of owned instances are preserved even if the owner is not present to oversee it.
|
||||
*
|
||||
* @param <E> The type of object that owns us.
|
||||
*/
|
||||
public interface WeaklyOwned<E extends Entity> extends Owned<E> {
|
||||
|
||||
World getWorld();
|
||||
|
||||
EntityReference<E> getMasterReference();
|
||||
|
||||
/**
|
||||
* Updated the owner of this object to be the same as another.
|
||||
*
|
||||
* @param sibling
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default void setMaster(WeaklyOwned<? extends E> sibling) {
|
||||
if (sibling instanceof WeaklyOwned) {
|
||||
getMasterReference().copyFrom(((WeaklyOwned<E>)sibling).getMasterReference());
|
||||
} else {
|
||||
setMaster(sibling.getMaster());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
default E getMaster() {
|
||||
return getMasterReference().get(getWorld());
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setMaster(E master) {
|
||||
getMasterReference().set(master);
|
||||
}
|
||||
|
||||
@Override
|
||||
default Optional<UUID> getMasterId() {
|
||||
return getMasterReference().getId();
|
||||
}
|
||||
}
|
|
@ -62,7 +62,7 @@ public class UnicornDispellAbility implements Ability<Pos> {
|
|||
@Override
|
||||
public double getCostEstimate(Pony player) {
|
||||
return getTarget(player)
|
||||
.filter(caster -> caster.getMaster() != player.getMaster())
|
||||
.filter(caster -> !caster.hasCommonOwner(player))
|
||||
.isPresent() ? 10 : 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -129,12 +129,17 @@ public class UnicornTeleportAbility implements Ability<Pos> {
|
|||
}
|
||||
|
||||
protected void teleport(Pony teleporter, Caster<?> teleportee, Pos destination) {
|
||||
|
||||
LivingEntity player = teleportee.getMaster();
|
||||
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
teleportee.getWorld().playSound(null, teleportee.getOrigin(), SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.PLAYERS, 1, 1);
|
||||
|
||||
double distance = destination.distanceTo(teleportee) / 10;
|
||||
|
||||
LivingEntity player = teleportee.getMaster();
|
||||
|
||||
if (player.hasVehicle()) {
|
||||
Entity mount = player.getVehicle();
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.ability.magic.spell;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -18,6 +19,9 @@ import com.minelittlepony.unicopia.particle.UParticles;
|
|||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/**
|
||||
* A spell that can be attached to a specific location in the world.
|
||||
|
@ -26,13 +30,25 @@ import net.minecraft.util.math.Vec3d;
|
|||
* spell loses affect until they return.
|
||||
*/
|
||||
public class PlaceableSpell extends AbstractDelegatingSpell {
|
||||
/**
|
||||
* Dimension the spell was originally cast in
|
||||
*/
|
||||
@Nullable
|
||||
private Identifier dimension;
|
||||
private RegistryKey<World> dimension;
|
||||
|
||||
/**
|
||||
* The visual effect
|
||||
*/
|
||||
private final ParticleHandle particlEffect = new ParticleHandle();
|
||||
|
||||
/**
|
||||
* The cast spell entity
|
||||
*/
|
||||
private final EntityReference<CastSpellEntity> castEntity = new EntityReference<>();
|
||||
|
||||
/**
|
||||
* The spell being cast
|
||||
*/
|
||||
private Spell spell;
|
||||
|
||||
public PlaceableSpell(SpellType<?> type, SpellTraits traits) {
|
||||
|
@ -59,19 +75,18 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
|
|||
public boolean tick(Caster<?> source, Situation situation) {
|
||||
if (situation == Situation.BODY) {
|
||||
if (!source.isClient()) {
|
||||
|
||||
if (dimension == null) {
|
||||
dimension = source.getWorld().getRegistryKey().getValue();
|
||||
dimension = source.getWorld().getRegistryKey();
|
||||
setDirty();
|
||||
} else if (!source.getWorld().getRegistryKey().getValue().equals(dimension)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!castEntity.isPresent(source.getWorld())) {
|
||||
if (getSpellEntity(source).isEmpty()) {
|
||||
CastSpellEntity entity = UEntities.CAST_SPELL.create(source.getWorld());
|
||||
Vec3d pos = castEntity.getPosition().orElse(source.getOriginVector());
|
||||
entity.updatePositionAndAngles(pos.x, pos.y, pos.z, 0, 0);
|
||||
entity.getSpellSlot().put(this);
|
||||
entity.setMaster(source.getMaster());
|
||||
entity.setMaster(source);
|
||||
entity.world.spawnEntity(entity);
|
||||
|
||||
castEntity.set(entity);
|
||||
|
@ -92,14 +107,33 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
|
|||
return super.tick(source, Situation.GROUND);
|
||||
}
|
||||
|
||||
this.onDestroyed(source);
|
||||
|
||||
return !isDead();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyed(Caster<?> source) {
|
||||
if (!source.isClient()) {
|
||||
getSpellEntity(source).ifPresent(e -> {
|
||||
e.getSpellSlot().clear();
|
||||
castEntity.set(null);
|
||||
});
|
||||
}
|
||||
super.onDestroyed(source);
|
||||
}
|
||||
|
||||
protected Optional<CastSpellEntity> getSpellEntity(Caster<?> source) {
|
||||
return Optional.ofNullable(dimension)
|
||||
.map(dim -> source.getWorld().getServer().getWorld(dimension))
|
||||
.map(castEntity::get);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toNBT(NbtCompound compound) {
|
||||
super.toNBT(compound);
|
||||
if (dimension != null) {
|
||||
compound.putString("dimension", dimension.toString());
|
||||
compound.putString("dimension", dimension.getValue().toString());
|
||||
}
|
||||
compound.put("castEntity", castEntity.toNBT());
|
||||
compound.put("spell", Spell.writeNbt(spell));
|
||||
|
@ -109,7 +143,10 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
|
|||
public void fromNBT(NbtCompound compound) {
|
||||
super.fromNBT(compound);
|
||||
if (compound.contains("dimension")) {
|
||||
dimension = new Identifier(compound.getString("dimension"));
|
||||
Identifier id = Identifier.tryParse(compound.getString("dimension"));
|
||||
if (id != null) {
|
||||
dimension = RegistryKey.of(Registry.WORLD_KEY, id);
|
||||
}
|
||||
}
|
||||
if (compound.contains("castEntity")) {
|
||||
castEntity.fromNBT(compound.getCompound("castEntity"));
|
||||
|
|
|
@ -62,6 +62,10 @@ public class RainboomAbilitySpell extends AbstractSpell {
|
|||
|
||||
LivingEntity owner = source.getMaster();
|
||||
|
||||
if (owner == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
source.findAllEntitiesInRange(rad).forEach(e -> {
|
||||
e.damage(MagicalDamageSource.create("rainboom", source), 6);
|
||||
});
|
||||
|
|
|
@ -53,6 +53,10 @@ public final class ThrowableSpell extends AbstractDelegatingSpell {
|
|||
|
||||
LivingEntity entity = caster.getMaster();
|
||||
|
||||
if (entity == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
caster.playSound(SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, 0.7F, 0.4F / (world.random.nextFloat() * 0.4F + 0.8F));
|
||||
|
||||
if (!caster.isClient()) {
|
||||
|
|
|
@ -86,7 +86,7 @@ public class CatapultSpell extends AbstractSpell implements ProjectileSpell {
|
|||
|
||||
double maxDistance = 2 + (getTraits().get(Trait.FOCUS) - 50) * 8;
|
||||
|
||||
HitResult ray = RayTraceHelper.doTrace(caster.getMaster(), maxDistance, 1, EntityPredicates.CAN_COLLIDE).getResult();
|
||||
HitResult ray = RayTraceHelper.doTrace(caster.getEntity(), maxDistance, 1, EntityPredicates.CAN_COLLIDE).getResult();
|
||||
|
||||
if (ray.getType() == HitResult.Type.ENTITY) {
|
||||
EntityHitResult result = (EntityHitResult)ray;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.minelittlepony.unicopia.ability.magic.spell.effect;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.ability.magic.Affine;
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
|
||||
|
@ -198,13 +200,18 @@ public class DarkVortexSpell extends AttractiveSpell {
|
|||
if (distance <= getEventHorizonRadius()) {
|
||||
target.setVelocity(target.getVelocity().multiply(distance / (2 * radius)));
|
||||
|
||||
@Nullable
|
||||
Entity master = source.getMaster();
|
||||
|
||||
if (target instanceof MagicProjectileEntity) {
|
||||
Item item = ((MagicProjectileEntity)target).getStack().getItem();
|
||||
if (item instanceof ProjectileDelegate) {
|
||||
((ProjectileDelegate) item).onImpact(((MagicProjectileEntity)target), source.getMaster());
|
||||
if (item instanceof ProjectileDelegate && master != null) {
|
||||
((ProjectileDelegate) item).onImpact(((MagicProjectileEntity)target), master);
|
||||
}
|
||||
} else if (target instanceof PersistentProjectileEntity) {
|
||||
source.getMaster().damage(DamageSource.thrownProjectile(target, ((PersistentProjectileEntity)target).getOwner()), 4);
|
||||
if (master != null) {
|
||||
master.damage(DamageSource.thrownProjectile(target, ((PersistentProjectileEntity)target).getOwner()), 4);
|
||||
}
|
||||
target.discard();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -58,13 +58,12 @@ public class FeatherFallSpell extends AbstractSpell {
|
|||
final float strength = 1F / (getTraits().get(Trait.STRENGTH, 2, 9) / targets.size());
|
||||
final float generosity = getTraits().get(Trait.GENEROSITY, 1, MAX_GENEROSITY_FACTOR) / MAX_GENEROSITY_FACTOR;
|
||||
|
||||
Entity master = caster.getMaster();
|
||||
Entity entity = caster.getEntity();
|
||||
Vec3d masterVelocity = caster.getEntity().getVelocity().multiply(0.1);
|
||||
targets.forEach(target -> {
|
||||
if (target.getVelocity().y < 0) {
|
||||
|
||||
boolean isSelf = target == master || target == entity;
|
||||
boolean isSelf = caster.isOwnedBy(target) || target == entity;
|
||||
float delta = strength * (isSelf ? (1F - generosity) : generosity);
|
||||
|
||||
if (!isSelf || generosity < 0.5F) {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.minelittlepony.unicopia.ability.magic.spell.effect;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.EquinePredicates;
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
|
||||
|
@ -48,7 +50,7 @@ public class FireSpell extends AbstractAreaEffectSpell implements ProjectileSpel
|
|||
@Override
|
||||
public void onImpact(MagicProjectileEntity projectile, BlockPos pos, BlockState state) {
|
||||
if (!projectile.isClient()) {
|
||||
projectile.getWorld().createExplosion(projectile.getMaster(), pos.getX(), pos.getY(), pos.getZ(), 2, DestructionType.DESTROY);
|
||||
projectile.getWorld().createExplosion(projectile.getEntity(), pos.getX(), pos.getY(), pos.getZ(), 2, DestructionType.DESTROY);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,11 +114,11 @@ public class FireSpell extends AbstractAreaEffectSpell implements ProjectileSpel
|
|||
return false;
|
||||
}
|
||||
|
||||
protected boolean applyEntities(Entity owner, World world, Vec3d pos) {
|
||||
protected boolean applyEntities(@Nullable Entity owner, World world, Vec3d pos) {
|
||||
return !VecHelper.findInRange(owner, world, pos, Math.max(0, 3 + getTraits().get(Trait.POWER)), i -> applyEntitySingle(owner, world, i)).isEmpty();
|
||||
}
|
||||
|
||||
protected boolean applyEntitySingle(Entity owner, World world, Entity e) {
|
||||
protected boolean applyEntitySingle(@Nullable Entity owner, World world, Entity e) {
|
||||
if ((!e.equals(owner) ||
|
||||
(owner instanceof PlayerEntity && !EquinePredicates.PLAYER_UNICORN.test(owner))) && !(e instanceof ItemEntity)
|
||||
&& !(e instanceof Caster<?>)) {
|
||||
|
@ -129,7 +131,7 @@ public class FireSpell extends AbstractAreaEffectSpell implements ProjectileSpel
|
|||
return false;
|
||||
}
|
||||
|
||||
protected DamageSource getDamageCause(Entity target, LivingEntity attacker) {
|
||||
protected DamageSource getDamageCause(Entity target, @Nullable LivingEntity attacker) {
|
||||
return MagicalDamageSource.create("fire", attacker);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,12 +43,10 @@ public class IceSpell extends AbstractSpell {
|
|||
|
||||
@Override
|
||||
public boolean tick(Caster<?> source, Situation situation) {
|
||||
LivingEntity owner = source.getMaster();
|
||||
|
||||
boolean submerged = source.getEntity().isSubmergedInWater() || source.getEntity().isSubmergedIn(FluidTags.LAVA);
|
||||
|
||||
long blocksAffected = PosHelper.getAllInRegionMutable(source.getOrigin(), outerRange).filter(i -> {
|
||||
if (source.canModifyAt(i) && applyBlockSingle(owner, source.getWorld(), i, situation)) {
|
||||
if (source.canModifyAt(i) && applyBlockSingle(source.getEntity(), source.getWorld(), i, situation)) {
|
||||
|
||||
if (submerged & source.getOrigin().isWithinDistance(i, rad - 1)) {
|
||||
BlockState state = source.getWorld().getBlockState(i);
|
||||
|
|
|
@ -59,9 +59,9 @@ public class LightSpell extends AbstractSpell {
|
|||
if (!ref.isPresent(caster.getWorld())) {
|
||||
FairyEntity entity = UEntities.TWITTERMITE.create(caster.getWorld());
|
||||
entity.setPosition(ref.getPosition().orElseGet(() -> {
|
||||
return caster.getMaster().getPos().add(VecHelper.supply(() -> caster.getWorld().random.nextInt(3) - 1));
|
||||
return caster.getOriginVector().add(VecHelper.supply(() -> caster.getWorld().random.nextInt(3) - 1));
|
||||
}));
|
||||
entity.setMaster(caster.getMaster());
|
||||
entity.setMaster(caster);
|
||||
entity.world.spawnEntity(entity);
|
||||
|
||||
ref.set(entity);
|
||||
|
|
|
@ -139,7 +139,7 @@ public class NecromancySpell extends AbstractAreaEffectSpell {
|
|||
minion.equipStack(EquipmentSlot.HEAD, Items.IRON_HELMET.getDefaultStack());
|
||||
|
||||
Equine.of(minion).filter(eq -> eq instanceof Creature).ifPresent(eq -> {
|
||||
((Creature)eq).setMaster(source.getMaster());
|
||||
((Creature)eq).setMaster(source);
|
||||
});
|
||||
|
||||
source.getWorld().spawnEntity(minion);
|
||||
|
|
|
@ -109,7 +109,7 @@ public class ShieldSpell extends AbstractSpell {
|
|||
* Calculates the maximum radius of the shield. aka The area of effect.
|
||||
*/
|
||||
public double getDrawDropOffRange(Caster<?> source) {
|
||||
float multiplier = source.getMaster().isSneaking() ? 1 : 2;
|
||||
float multiplier = source.getMaster() != null && source.getMaster().isSneaking() ? 1 : 2;
|
||||
float min = 4 + getTraits().get(Trait.POWER);
|
||||
return (min + (source.getLevel().get() * 2)) / multiplier;
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ public class SiphoningSpell extends AbstractAreaEffectSpell {
|
|||
|
||||
private void collectHealth(Caster<?> source) {
|
||||
LivingEntity owner = source.getMaster();
|
||||
float maxHealthGain = owner.getMaxHealth() - owner.getHealth();
|
||||
float maxHealthGain = owner == null ? 0 : owner.getMaxHealth() - owner.getHealth();
|
||||
|
||||
if (maxHealthGain == 0) {
|
||||
return;
|
||||
|
|
|
@ -29,22 +29,10 @@ public class TargetSelecter {
|
|||
public Stream<Entity> getEntities(Caster<?> source, double radius, BiPredicate<Caster<?>, Entity> filter) {
|
||||
targets.values().removeIf(Target::tick);
|
||||
|
||||
Entity owner = source.getMaster();
|
||||
|
||||
boolean ownerIsValid = spell.isFriendlyTogether(source) && (EquinePredicates.PLAYER_UNICORN.test(owner));
|
||||
Predicate<Entity> ownerCheck = isOwnerOrFriend(spell, source);
|
||||
|
||||
return source.findAllEntitiesInRange(radius)
|
||||
.filter(entity -> entity.isAlive() && !entity.isRemoved())
|
||||
.filter(entity -> {
|
||||
boolean hasShield = SpellPredicate.IS_SHIELD_LIKE.isOn(entity);
|
||||
boolean isOwnerOrFriend = Pony.equal(entity, owner) || owner.isConnectedThroughVehicle(entity) || FriendshipBraceletItem.isComrade(source, entity);
|
||||
|
||||
if (!ownerIsValid && isOwnerOrFriend) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !hasShield && (!ownerIsValid || !isOwnerOrFriend);
|
||||
})
|
||||
.filter(entity -> entity.isAlive() && !entity.isRemoved() && !ownerCheck.test(entity) && !SpellPredicate.IS_SHIELD_LIKE.isOn(entity))
|
||||
.filter(e -> filter.test(source, e))
|
||||
.map(i -> {
|
||||
targets.computeIfAbsent(i.getUuid(), Target::new);
|
||||
|
@ -57,16 +45,18 @@ public class TargetSelecter {
|
|||
}
|
||||
|
||||
public static <T extends Entity> Predicate<T> notOwnerOrFriend(Affine spell, Caster<?> source) {
|
||||
return TargetSelecter.<T>isOwnerOrFriend(spell, source).negate();
|
||||
}
|
||||
|
||||
public static <T extends Entity> Predicate<T> isOwnerOrFriend(Affine spell, Caster<?> source) {
|
||||
Entity owner = source.getMaster();
|
||||
|
||||
boolean ownerIsValid = spell.isFriendlyTogether(source) && (EquinePredicates.PLAYER_UNICORN.test(owner));
|
||||
|
||||
if (!ownerIsValid) {
|
||||
return e -> true;
|
||||
if (!(spell.isFriendlyTogether(source) && EquinePredicates.PLAYER_UNICORN.test(owner))) {
|
||||
return e -> FriendshipBraceletItem.isComrade(source, e);
|
||||
}
|
||||
|
||||
return entity -> {
|
||||
return !ownerIsValid || !(Pony.equal(entity, owner) || owner.isConnectedThroughVehicle(entity) || FriendshipBraceletItem.isComrade(source, entity));
|
||||
return FriendshipBraceletItem.isComrade(source, entity) || (owner != null && (Pony.equal(entity, owner) || owner.isConnectedThroughVehicle(entity)));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package com.minelittlepony.unicopia.entity;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.minelittlepony.unicopia.Affinity;
|
||||
import com.minelittlepony.unicopia.WeaklyOwned;
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.Levelled;
|
||||
import com.minelittlepony.unicopia.ability.magic.SpellContainer;
|
||||
|
@ -12,6 +10,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.Situation;
|
|||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
import com.minelittlepony.unicopia.network.Channel;
|
||||
import com.minelittlepony.unicopia.network.MsgSpawnProjectile;
|
||||
import com.minelittlepony.unicopia.network.datasync.EffectSync;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
|
@ -25,45 +24,25 @@ import net.minecraft.text.Text;
|
|||
import net.minecraft.text.TranslatableText;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class CastSpellEntity extends LightEmittingEntity implements Caster<LivingEntity> {
|
||||
public class CastSpellEntity extends LightEmittingEntity implements Caster<LivingEntity>, WeaklyOwned<LivingEntity> {
|
||||
private static final TrackedData<Float> GRAVITY = DataTracker.registerData(CastSpellEntity.class, TrackedDataHandlerRegistry.FLOAT);
|
||||
private static final TrackedData<Optional<UUID>> SPELL = DataTracker.registerData(CastSpellEntity.class, TrackedDataHandlerRegistry.OPTIONAL_UUID);
|
||||
private static final TrackedData<NbtCompound> EFFECT = DataTracker.registerData(CastSpellEntity.class, TrackedDataHandlerRegistry.TAG_COMPOUND);
|
||||
|
||||
private static final LevelStore LEVELS = Levelled.fixed(0);
|
||||
|
||||
private final EntityPhysics<CastSpellEntity> physics = new EntityPhysics<>(this, GRAVITY);
|
||||
|
||||
private final SpellContainer spell = new SpellContainer.Delegate() {
|
||||
@Override
|
||||
public SpellContainer delegate() {
|
||||
return Caster.of(getMaster()).map(Caster::getSpellSlot).orElse(SpellContainer.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Spell spell) {
|
||||
getDataTracker().set(SPELL, Optional.ofNullable(spell).map(Spell::getUuid));
|
||||
SpellContainer.Delegate.super.put(spell);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clear() {
|
||||
return getDataTracker().get(SPELL).map(id -> {
|
||||
return delegate().removeIf(spell -> spell.getUuid().equals(id), true);
|
||||
}).orElse(false);
|
||||
}
|
||||
};
|
||||
private final EffectSync effectDelegate = new EffectSync(this, EFFECT);
|
||||
|
||||
private final EntityReference<LivingEntity> owner = new EntityReference<>();
|
||||
|
||||
private int orphanedTicks;
|
||||
|
||||
public CastSpellEntity(EntityType<?> type, World world) {
|
||||
super(type, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initDataTracker() {
|
||||
getDataTracker().startTracking(SPELL, Optional.empty());
|
||||
getDataTracker().startTracking(EFFECT, new NbtCompound());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -88,35 +67,14 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster<Livin
|
|||
return;
|
||||
}
|
||||
|
||||
LivingEntity master = getMaster();
|
||||
|
||||
if (master == null || master.isRemoved()) {
|
||||
if (orphanedTicks-- > 0) {
|
||||
return;
|
||||
}
|
||||
discard();
|
||||
return;
|
||||
}
|
||||
|
||||
orphanedTicks = 0;
|
||||
|
||||
if (dataTracker.get(SPELL).filter(spellId -> {
|
||||
return getSpellSlot().forEach(spell -> {
|
||||
return spell.getUuid().equals(spellId) ? Operation.ofBoolean(spell.tick(this, Situation.GROUND_ENTITY)) : Operation.SKIP;
|
||||
}, true);
|
||||
}).isEmpty()) {
|
||||
if (!getSpellSlot().forEach(spell -> Operation.ofBoolean(spell.tick(this, Situation.GROUND_ENTITY)), true)) {
|
||||
discard();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaster(LivingEntity owner) {
|
||||
this.owner.set(owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LivingEntity getMaster() {
|
||||
return owner.get(((Entity)this).world);
|
||||
public EntityReference<LivingEntity> getMasterReference() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -141,7 +99,7 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster<Livin
|
|||
|
||||
@Override
|
||||
public SpellContainer getSpellSlot() {
|
||||
return spell;
|
||||
return effectDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -152,8 +110,8 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster<Livin
|
|||
@Override
|
||||
protected void writeCustomDataToNbt(NbtCompound tag) {
|
||||
tag.put("owner", owner.toNBT());
|
||||
dataTracker.get(SPELL).ifPresent(spellId -> {
|
||||
tag.putUuid("spellId", spellId);
|
||||
getSpellSlot().get(true).ifPresent(effect -> {
|
||||
tag.put("effect", Spell.writeNbt(effect));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -162,9 +120,8 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster<Livin
|
|||
if (tag.contains("owner")) {
|
||||
owner.fromNBT(tag.getCompound("owner"));
|
||||
}
|
||||
orphanedTicks = 60;
|
||||
if (tag.contains("spellId")) {
|
||||
dataTracker.set(SPELL, Optional.ofNullable(tag.getUuid("spellId")));
|
||||
if (tag.contains("effect")) {
|
||||
getSpellSlot().put(Spell.readNbt(tag.getCompound("effect")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,10 +3,12 @@ package com.minelittlepony.unicopia.entity;
|
|||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.Affinity;
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.WeaklyOwned;
|
||||
import com.minelittlepony.unicopia.ability.magic.Affine;
|
||||
import com.minelittlepony.unicopia.ability.magic.Levelled;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
|
@ -33,8 +35,9 @@ import net.minecraft.entity.mob.SlimeEntity;
|
|||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtElement;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Creature extends Living<LivingEntity> {
|
||||
public class Creature extends Living<LivingEntity> implements WeaklyOwned<LivingEntity> {
|
||||
private static final TrackedData<NbtCompound> EFFECT = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.TAG_COMPOUND);
|
||||
private static final TrackedData<NbtCompound> MASTER = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.TAG_COMPOUND);
|
||||
public static final TrackedData<Float> GRAVITY = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.FLOAT);
|
||||
|
@ -68,17 +71,27 @@ public class Creature extends Living<LivingEntity> {
|
|||
}
|
||||
|
||||
public boolean isMinion() {
|
||||
Entity master = getMaster();
|
||||
return master != null && master != getEntity();
|
||||
return master.getId().isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld() {
|
||||
return super.getWorld();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public LivingEntity getMaster() {
|
||||
NbtCompound data = entity.getDataTracker().get(MASTER);
|
||||
master.fromNBT(data);
|
||||
return master.getOrEmpty(getWorld()).orElse(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityReference<LivingEntity> getMasterReference() {
|
||||
return master;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getEntity() {
|
||||
return entity;
|
||||
|
@ -112,7 +125,7 @@ public class Creature extends Living<LivingEntity> {
|
|||
Predicate<LivingEntity> filter = TargetSelecter.<LivingEntity>notOwnerOrFriend(this, this).and(e -> {
|
||||
return Equine.of(e)
|
||||
.filter(eq -> eq instanceof Creature)
|
||||
.filter(eq -> ((Creature)eq).getMaster() == getMaster())
|
||||
.filter(eq -> ((Creature)eq).hasCommonOwner(this))
|
||||
.isEmpty();
|
||||
});
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import net.minecraft.world.World;
|
|||
|
||||
public class EntityReference<T extends Entity> implements NbtSerialisable {
|
||||
|
||||
private UUID uuid;
|
||||
private Optional<UUID> uuid = Optional.empty();
|
||||
private int clientId;
|
||||
|
||||
private Optional<Vec3d> pos = Optional.empty();
|
||||
|
@ -32,18 +32,28 @@ public class EntityReference<T extends Entity> implements NbtSerialisable {
|
|||
fromNBT(nbt);
|
||||
}
|
||||
|
||||
public void copyFrom(EntityReference<T> other) {
|
||||
uuid = other.uuid;
|
||||
clientId = other.clientId;
|
||||
pos = other.pos;
|
||||
}
|
||||
|
||||
public void set(@Nullable T entity) {
|
||||
if (entity != null) {
|
||||
uuid = entity.getUuid();
|
||||
uuid = Optional.of(entity.getUuid());
|
||||
clientId = entity.getId();
|
||||
pos = Optional.of(entity.getPos());
|
||||
} else {
|
||||
uuid = null;
|
||||
uuid = Optional.empty();
|
||||
clientId = 0;
|
||||
pos = Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<UUID> getId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position the last known position of the assigned entity.
|
||||
*/
|
||||
|
@ -66,8 +76,8 @@ public class EntityReference<T extends Entity> implements NbtSerialisable {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Optional<T> getOrEmpty(World world) {
|
||||
if (uuid != null && world instanceof ServerWorld) {
|
||||
return Optional.ofNullable((T)((ServerWorld)world).getEntity(uuid)).filter(this::checkReference);
|
||||
if (uuid.isPresent() && world instanceof ServerWorld) {
|
||||
return uuid.map(((ServerWorld)world)::getEntity).map(e -> (T)e).filter(this::checkReference);
|
||||
}
|
||||
|
||||
if (clientId != 0) {
|
||||
|
@ -84,18 +94,14 @@ public class EntityReference<T extends Entity> implements NbtSerialisable {
|
|||
|
||||
@Override
|
||||
public void toNBT(NbtCompound tag) {
|
||||
if (uuid != null) {
|
||||
tag.putUuid("uuid", uuid);
|
||||
}
|
||||
pos.ifPresent(p -> {
|
||||
tag.put("pos", NbtSerialisable.writeVector(p));
|
||||
});
|
||||
uuid.ifPresent(uuid -> tag.putUuid("uuid", uuid));
|
||||
pos.ifPresent(p -> tag.put("pos", NbtSerialisable.writeVector(p)));
|
||||
tag.putInt("clientId", clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromNBT(NbtCompound tag) {
|
||||
uuid = tag.containsUuid("uuid") ? tag.getUuid("uuid") : null;
|
||||
uuid = tag.containsUuid("uuid") ? Optional.of(tag.getUuid("uuid")) : Optional.empty();
|
||||
pos = tag.contains("pos") ? Optional.ofNullable(NbtSerialisable.readVector(tag.getList("pos", NbtElement.DOUBLE_TYPE))) : Optional.empty();
|
||||
clientId = tag.getInt("clientId");
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ import java.util.Optional;
|
|||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.Owned;
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.WeaklyOwned;
|
||||
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
|
||||
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
||||
import com.minelittlepony.unicopia.particle.UParticles;
|
||||
|
@ -42,7 +42,7 @@ import net.minecraft.util.math.Vec3d;
|
|||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.event.GameEvent;
|
||||
|
||||
public class FairyEntity extends PathAwareEntity implements DynamicLightSource, Owned<LivingEntity> {
|
||||
public class FairyEntity extends PathAwareEntity implements DynamicLightSource, WeaklyOwned<LivingEntity> {
|
||||
private final EntityReference<LivingEntity> owner = new EntityReference<>();
|
||||
|
||||
private final EntityReference<LivingEntity> assignment = new EntityReference<>();
|
||||
|
@ -101,13 +101,8 @@ public class FairyEntity extends PathAwareEntity implements DynamicLightSource,
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setMaster(LivingEntity owner) {
|
||||
this.owner.set(owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LivingEntity getMaster() {
|
||||
return owner.get(((Entity)this).world);
|
||||
public EntityReference<LivingEntity> getMasterReference() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2,6 +2,8 @@ package com.minelittlepony.unicopia.entity;
|
|||
|
||||
import java.util.Random;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.minelittlepony.unicopia.Owned;
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.UTags;
|
||||
|
@ -132,12 +134,12 @@ public class ItemImpl implements Equine<ItemEntity>, Owned<ItemEntity> {
|
|||
|
||||
@Override
|
||||
public Race getSpecies() {
|
||||
return Race.fromId(getMaster().getDataTracker().get(ITEM_RACE));
|
||||
return Race.fromId(owner.getDataTracker().get(ITEM_RACE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpecies(Race race) {
|
||||
getMaster().getDataTracker().set(ITEM_RACE, race.ordinal());
|
||||
owner.getDataTracker().set(ITEM_RACE, race.ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -158,6 +160,7 @@ public class ItemImpl implements Equine<ItemEntity>, Owned<ItemEntity> {
|
|||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public ItemEntity getMaster() {
|
||||
return owner;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.entity;
|
|||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
|
@ -81,13 +82,13 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
|
|||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public T getMaster() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
|
||||
try {
|
||||
getSpellSlot().forEach(spell -> Operation.ofBoolean(spell.tick(this, Situation.BODY)), true);
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -101,7 +101,7 @@ public interface Disguise extends FlightType.Provider, PlayerDimensions.Provider
|
|||
player.setInvisible(true);
|
||||
|
||||
if (entity instanceof Owned) {
|
||||
((Owned<LivingEntity>)entity).setMaster(player.getMaster());
|
||||
((Owned<LivingEntity>)entity).setMaster(player);
|
||||
}
|
||||
|
||||
if (entity instanceof PlayerEntity) {
|
||||
|
|
|
@ -27,7 +27,7 @@ public class MobBehaviour<T extends MobEntity> extends EntityBehaviour<T> {
|
|||
|
||||
protected LivingEntity findTarget(Pony player, T entity) {
|
||||
return RayTraceHelper.<LivingEntity>findEntity(player.getEntity(), 6, 1,
|
||||
e -> e instanceof LivingEntity && e != entity && e != player.getMaster())
|
||||
e -> e instanceof LivingEntity && e != entity && !player.isOwnedBy(e))
|
||||
.orElseGet(() -> getDummy(entity));
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.entity.player.dummy;
|
|||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.InteractionManager;
|
||||
import com.minelittlepony.unicopia.Owned;
|
||||
|
@ -66,6 +67,7 @@ public class DummyClientPlayerEntity extends AbstractClientPlayerEntity implemen
|
|||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PlayerEntity getMaster() {
|
||||
return owner;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.minelittlepony.unicopia.entity.player.dummy;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.InteractionManager;
|
||||
import com.minelittlepony.unicopia.Owned;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
@ -23,6 +25,7 @@ public class DummyPlayerEntity extends PlayerEntity implements Owned<PlayerEntit
|
|||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PlayerEntity getMaster() {
|
||||
return owner;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.minelittlepony.unicopia.entity.player.dummy;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.InteractionManager;
|
||||
import com.minelittlepony.unicopia.Owned;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
@ -29,6 +31,7 @@ public class DummyServerPlayerEntity extends ServerPlayerEntity implements Owned
|
|||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PlayerEntity getMaster() {
|
||||
return owner;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.minelittlepony.unicopia.item;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -41,6 +42,7 @@ public class FriendshipBraceletItem extends WearableItem implements DyeableItem,
|
|||
ItemStack result = stack.copy();
|
||||
result.setCount(1);
|
||||
result.getOrCreateNbt().putString("issuer", player.getName().asString());
|
||||
result.getOrCreateNbt().putUuid("issuer_id", player.getUuid());
|
||||
|
||||
if (!player.getAbilities().creativeMode) {
|
||||
stack.decrement(1);
|
||||
|
@ -65,7 +67,7 @@ public class FriendshipBraceletItem extends WearableItem implements DyeableItem,
|
|||
@Environment(EnvType.CLIENT)
|
||||
public void appendTooltip(ItemStack stack, @Nullable World world, List<Text> list, TooltipContext tooltipContext) {
|
||||
if (isSigned(stack)) {
|
||||
list.add(new TranslatableText("item.unicopia.friendship_bracelet.issuer", getSignature(stack)));
|
||||
list.add(new TranslatableText("item.unicopia.friendship_bracelet.issuer", getSignatorName(stack)));
|
||||
}
|
||||
if (isGlowing(stack)) {
|
||||
list.add(new TranslatableText("item.unicopia.friendship_bracelet.glowing").formatted(Formatting.ITALIC, Formatting.GRAY));
|
||||
|
@ -78,16 +80,25 @@ public class FriendshipBraceletItem extends WearableItem implements DyeableItem,
|
|||
}
|
||||
|
||||
private boolean checkSignature(ItemStack stack, PlayerEntity player) {
|
||||
return player.getName().asString().contentEquals(getSignature(stack));
|
||||
return checkSignature(stack, player.getUuid());
|
||||
}
|
||||
|
||||
private boolean checkSignature(ItemStack stack, UUID player) {
|
||||
return player.equals(getSignatorId(stack));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getSignature(ItemStack stack) {
|
||||
public static String getSignatorName(ItemStack stack) {
|
||||
return isSigned(stack) ? stack.getNbt().getString("issuer") : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static UUID getSignatorId(ItemStack stack) {
|
||||
return isSigned(stack) ? stack.getNbt().getUuid("issuer_id") : null;
|
||||
}
|
||||
|
||||
public static boolean isSigned(ItemStack stack) {
|
||||
return stack.hasNbt() && stack.getNbt().contains("issuer");
|
||||
return stack.hasNbt() && stack.getNbt().contains("issuer_id");
|
||||
}
|
||||
|
||||
public static boolean isSignedBy(ItemStack stack, PlayerEntity player) {
|
||||
|
@ -95,13 +106,18 @@ public class FriendshipBraceletItem extends WearableItem implements DyeableItem,
|
|||
&& ((FriendshipBraceletItem)stack.getItem()).checkSignature(stack, player);
|
||||
}
|
||||
|
||||
public static boolean isComrade(Caster<?> caster, Entity entity) {
|
||||
Entity master = caster.getMaster();
|
||||
if (master instanceof PlayerEntity && entity instanceof LivingEntity) {
|
||||
return isSignedBy(((LivingEntity)entity).getOffHandStack(), (PlayerEntity)master)
|
||||
|| isSignedBy(((LivingEntity)entity).getEquippedStack(EquipmentSlot.CHEST), (PlayerEntity)master);
|
||||
}
|
||||
public static boolean isSignedBy(ItemStack stack, UUID player) {
|
||||
return stack.getItem() instanceof FriendshipBraceletItem
|
||||
&& ((FriendshipBraceletItem)stack.getItem()).checkSignature(stack, player);
|
||||
}
|
||||
|
||||
public static boolean isComrade(Caster<?> caster, Entity entity) {
|
||||
if (entity instanceof LivingEntity) {
|
||||
return caster.getMasterId().filter(id -> {
|
||||
return isSignedBy(((LivingEntity)entity).getOffHandStack(), id)
|
||||
|| isSignedBy(((LivingEntity)entity).getEquippedStack(EquipmentSlot.CHEST), id);
|
||||
}).isPresent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,14 @@ import net.minecraft.text.TranslatableText;
|
|||
|
||||
@Mixin(DamageSource.class)
|
||||
abstract class MixinDamageSource {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Inject(method = "getDeathMessage", at = @At("RETURN"), cancellable = true)
|
||||
private void onGetDeathMessage(LivingEntity entity, CallbackInfoReturnable<Text> info) {
|
||||
Equine.of(entity).map(Equine::getAttacker).ifPresent(attacker -> {
|
||||
DamageSource self = (DamageSource)(Object)this;
|
||||
|
||||
Entity prime = entity.getPrimeAdversary();
|
||||
if (prime != null && !(attacker instanceof Owned<?> && ((Owned<?>)attacker).getMaster() == prime)) {
|
||||
if (prime != null && !(attacker instanceof Owned<?> && ((Owned<Entity>)attacker).isOwnedBy(prime))) {
|
||||
info.setReturnValue(new TranslatableText("death.attack.generic.and_also", info.getReturnValue(), attacker.getDisplayName()));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Li
|
|||
super(type, world);
|
||||
}
|
||||
|
||||
public MagicProjectileEntity(World world, LivingEntity thrower) {
|
||||
public MagicProjectileEntity(World world, @Nullable LivingEntity thrower) {
|
||||
super(UEntities.THROWN_ITEM, thrower, world);
|
||||
}
|
||||
|
||||
|
@ -103,6 +103,7 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Li
|
|||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public LivingEntity getMaster() {
|
||||
return (LivingEntity)getOwner();
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ public class MagicalDamageSource extends EntityDamageSource {
|
|||
return new MagicalDamageSource(type, null, null, false, false);
|
||||
}
|
||||
|
||||
public static DamageSource create(String type, LivingEntity source) {
|
||||
public static DamageSource create(String type, @Nullable LivingEntity source) {
|
||||
return new MagicalDamageSource(type, source, null, false, false);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue