diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java index b52023e5..8c70d6dc 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; +import com.minelittlepony.unicopia.block.data.Ether; import com.minelittlepony.unicopia.entity.CastSpellEntity; import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.UEntities; @@ -86,6 +87,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell { entity.getSpellSlot().put(spell.toPlaceable()); entity.setMaster(source); entity.world.spawnEntity(entity); + Ether.get(entity.world).put(getType(), entity); castEntity.set(entity); setDirty(); @@ -96,6 +98,16 @@ public class PlaceableSpell extends AbstractDelegatingSpell { } if (situation == Situation.GROUND_ENTITY) { + + if (!source.isClient()) { + Ether ether = Ether.get(source.getReferenceWorld()); + if (ether.getEntry(getType(), source).filter(Ether.Entry::isAlive).isEmpty()) { + ether.remove(getType(), source); + setDead(); + return false; + } + } + particlEffect.update(getUuid(), source, spawner -> { spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.MAGIC_RUNES, 90, 0), source.getOriginVector(), Vec3d.ZERO); }).ifPresent(p -> { @@ -108,24 +120,15 @@ public class PlaceableSpell extends AbstractDelegatingSpell { return !isDead(); } - /** - * Detaches this spell from the placed version. - * This spell and the placed entity effectively become independent. - * - * @return The previous cast spell entity if one existed, otherwise empty. - */ - protected Optional detach(Caster source) { - return getSpellEntity(source).map(e -> { - castEntity.set(null); - return e; - }); - } - @Override public void onDestroyed(Caster source) { if (!source.isClient()) { + castEntity.getId().ifPresent(id -> { + getWorld(source).map(Ether::get) + .flatMap(ether -> ether.getEntry(getType(), id)) + .ifPresent(Ether.Entry::markDead); + }); getSpellEntity(source).ifPresent(e -> { - e.getSpellSlot().clear(); castEntity.set(null); }); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java index 1d910250..d2b36e83 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java @@ -34,7 +34,7 @@ public class PortalSpell extends AbstractSpell { if (situation == Situation.GROUND) { teleportationTarget.getPosition().ifPresentOrElse( targetPos -> tickWithTargetLink(source, targetPos), - () -> advertiseAvailable(source) + () -> findLink(source) ); if (!publishedPosition) { @@ -76,15 +76,15 @@ public class PortalSpell extends AbstractSpell { } @SuppressWarnings("unchecked") - private void advertiseAvailable(Caster source) { + private void findLink(Caster source) { Ether ether = Ether.get(source.getReferenceWorld()); - ether.getIds(getType()) + ether.getEntries(getType()) .stream() - .filter(id -> !id.referenceEquals(source.getEntity())) + .filter(entry -> entry.isAvailable() && !entry.entity.referenceEquals(source.getEntity())) .findAny() - .ifPresent(ref -> { - ether.remove(getType(), ref.getId().get()); - teleportationTarget.copyFrom((EntityReference)ref); + .ifPresent(entry -> { + entry.setTaken(true); + teleportationTarget.copyFrom((EntityReference)entry.entity); }); } @@ -106,4 +106,10 @@ public class PortalSpell extends AbstractSpell { publishedPosition = compound.getBoolean("publishedPosition"); teleportationTarget.fromNBT(compound.getCompound("teleportationTarget")); } + + @Override + public void setDead() { + super.setDead(); + particlEffect.destroy(); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/data/Ether.java b/src/main/java/com/minelittlepony/unicopia/block/data/Ether.java index 67b611f9..fdba8062 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/data/Ether.java +++ b/src/main/java/com/minelittlepony/unicopia/block/data/Ether.java @@ -6,6 +6,7 @@ import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.EntityReference; +import com.minelittlepony.unicopia.util.NbtSerialisable; import net.minecraft.nbt.*; import net.minecraft.util.Identifier; @@ -20,7 +21,7 @@ public class Ether extends PersistentState { return WorldOverlay.getPersistableStorage(world, ID, Ether::new, Ether::new); } - private final Map>> advertisingEndpoints = new HashMap<>(); + private final Map> advertisingEndpoints = new HashMap<>(); private final Object locker = new Object(); @@ -29,9 +30,11 @@ public class Ether extends PersistentState { compound.getKeys().forEach(key -> { Identifier typeId = Identifier.tryParse(key); if (typeId != null) { - Set> uuids = getIds(typeId); + Set uuids = getEntries(typeId); compound.getList(key, NbtElement.COMPOUND_TYPE).forEach(entry -> { - uuids.add(new EntityReference<>((NbtCompound)entry)); + Entry e = new Entry(); + e.fromNBT((NbtCompound)entry); + uuids.add(e); }); } }); @@ -56,7 +59,7 @@ public class Ether extends PersistentState { public void put(SpellType spellType, Caster caster) { synchronized (locker) { - getIds(spellType.getId()).add(new EntityReference<>(caster.getEntity())); + getEntries(spellType.getId()).add(new Entry(caster)); } markDirty(); } @@ -64,9 +67,9 @@ public class Ether extends PersistentState { public void remove(SpellType spellType, UUID id) { synchronized (locker) { Identifier typeId = spellType.getId(); - Set> refs = advertisingEndpoints.get(typeId); + Set refs = advertisingEndpoints.get(typeId); if (refs != null) { - refs.removeIf(ref -> ref.getId().orElse(Util.NIL_UUID).equals(id)); + refs.removeIf(ref -> ref.entity.getId().orElse(Util.NIL_UUID).equals(id)); if (refs.isEmpty()) { advertisingEndpoints.remove(typeId); } @@ -75,13 +78,89 @@ public class Ether extends PersistentState { } } - public Set> getIds(SpellType spellType) { - return getIds(spellType.getId()); + public void remove(SpellType spellType, Caster caster) { + remove(spellType, caster.getEntity().getUuid()); } - private Set> getIds(Identifier typeId) { + public Set getEntries(SpellType spellType) { + return getEntries(spellType.getId()); + } + + private Set getEntries(Identifier typeId) { synchronized (locker) { return advertisingEndpoints.computeIfAbsent(typeId, i -> new HashSet<>()); } } + + public Optional getEntry(SpellType spellType, Caster caster) { + synchronized (locker) { + return getEntries(spellType).stream().filter(e -> e.entity.referenceEquals(caster.getEntity())).findFirst(); + } + } + + public Optional getEntry(SpellType spellType, UUID uuid) { + synchronized (locker) { + return getEntries(spellType).stream().filter(e -> e.equals(uuid)).findFirst(); + } + } + + public class Entry implements NbtSerialisable { + public final EntityReference entity; + private boolean removed; + private boolean taken; + + public Entry() { + entity = new EntityReference<>(); + } + + public Entry(Caster caster) { + entity = new EntityReference<>(caster.getEntity()); + } + + public boolean isAlive() { + return !removed; + } + + public void markDead() { + removed = true; + markDirty(); + } + + public boolean isAvailable() { + return !removed && !taken; + } + + public void setTaken(boolean taken) { + this.taken = taken; + markDirty(); + } + + @Override + public void toNBT(NbtCompound compound) { + entity.toNBT(compound); + compound.putBoolean("remove", removed); + compound.putBoolean("taken", taken); + } + @Override + public void fromNBT(NbtCompound compound) { + entity.fromNBT(compound); + removed = compound.getBoolean("removed"); + taken = compound.getBoolean("taken"); + } + + @Override + public boolean equals(Object other) { + return other instanceof Entry e + && e.equals(entity.getId().orElse(Util.NIL_UUID)); + } + + public boolean equals(UUID uuid) { + return entity.getId().orElse(Util.NIL_UUID).equals(uuid); + } + + @Override + public int hashCode() { + return entity.getId().orElse(Util.NIL_UUID).hashCode(); + } + } }