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