From 1ecb28e51140a06c72e752a6ca50ee37d62a04c7 Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 23 May 2024 17:02:29 +0100 Subject: [PATCH] Move placed spell logic into the cast spell entity and fix portals mislinking/multilinking --- .../ability/magic/SpellPredicate.java | 2 - .../magic/spell/AbstractDelegatingSpell.java | 16 +- .../ability/magic/spell/PlaceableSpell.java | 162 ------------------ .../magic/spell/PlacementControlSpell.java | 63 ++++--- .../unicopia/ability/magic/spell/Spell.java | 9 +- .../magic/spell/effect/PortalSpell.java | 113 ++++++------ .../ability/magic/spell/effect/SpellType.java | 2 - .../client/gui/DismissSpellScreen.java | 6 +- .../client/render/HornFeatureRenderer.java | 4 +- .../entity/CastSpellEntityRenderer.java | 74 +++++++- .../render/spell/PlacedSpellRenderer.java | 92 ---------- .../render/spell/PortalFrameBuffer.java | 4 +- .../render/spell/PortalSpellRenderer.java | 7 +- .../spell/SpellEffectsRenderDispatcher.java | 1 - .../unicopia/entity/mob/CastSpellEntity.java | 119 +++++++++++-- .../unicopia/entity/mob/UEntities.java | 2 +- .../unicopia/entity/player/Pony.java | 2 +- .../unicopia/item/EnchantedStaffItem.java | 3 +- .../unicopia/server/world/Ether.java | 45 +++-- 19 files changed, 326 insertions(+), 400 deletions(-) delete mode 100644 src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java delete mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellPredicate.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellPredicate.java index 358c4fb6..9a3ab3d2 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellPredicate.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellPredicate.java @@ -12,14 +12,12 @@ import net.minecraft.entity.Entity; public interface SpellPredicate extends Predicate { SpellPredicate ALL = spell -> true; SpellPredicate CAN_SUPPRESS = s -> s instanceof IllusionarySpell; - SpellPredicate IS_PLACED = s -> s instanceof PlaceableSpell; SpellPredicate IS_DISGUISE = s -> s instanceof AbstractDisguiseSpell; SpellPredicate IS_MIMIC = s -> s instanceof MimicSpell; SpellPredicate IS_SHIELD_LIKE = spell -> spell instanceof ShieldSpell; SpellPredicate IS_TIMED = spell -> spell instanceof TimedSpell; SpellPredicate IS_ORIENTED = spell -> spell instanceof OrientedSpell; - SpellPredicate IS_NOT_PLACED = IS_PLACED.negate(); SpellPredicate IS_VISIBLE = spell -> spell != null && !spell.isHidden(); SpellPredicate IS_CORRUPTING = spell -> spell.getAffinity() == Affinity.BAD; diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java index 79ed5954..b65acecf 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java @@ -50,31 +50,31 @@ public abstract class AbstractDelegatingSpell implements Spell { @Override public void setDead() { - if (delegate.get() instanceof Spell p) { - p.setDead(); + if (delegate.get() != null) { + delegate.get().setDead(); } } @Override public void tickDying(Caster caster) { - if (delegate.get() instanceof Spell p) { - p.tickDying(caster); + if (delegate.get() != null) { + delegate.get().tickDying(caster); } } @Override public boolean isDead() { - return !(delegate.get() instanceof Spell p) || p.isDead(); + return delegate.get() == null; } @Override public boolean isDying() { - return delegate.get() instanceof Spell p && p.isDying(); + return delegate.get() != null && delegate.get().isDying(); } @Override public boolean isDirty() { - return dirty || (delegate.get() instanceof Spell p && p.isDirty()); + return dirty || delegate.hasDirtySpell(); } @Override @@ -84,7 +84,7 @@ public abstract class AbstractDelegatingSpell implements Spell { @Override public boolean isHidden() { - return hidden || (delegate.get() instanceof Spell p && p.isHidden()); + return hidden || (delegate.get() != null && delegate.get().isHidden()); } @Override 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 deleted file mode 100644 index 548e66ea..00000000 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java +++ /dev/null @@ -1,162 +0,0 @@ -package com.minelittlepony.unicopia.ability.magic.spell; - -import java.util.UUID; - -import com.minelittlepony.unicopia.ability.magic.Caster; -import com.minelittlepony.unicopia.ability.magic.spell.PlacementControlSpell.PlacementDelegate; -import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; -import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; -import com.minelittlepony.unicopia.server.world.Ether; - -import net.minecraft.entity.Entity; -import net.minecraft.nbt.*; -import net.minecraft.util.math.MathHelper; - -/** - * A spell that can be attached to a specific location in the world. - *

- * The spell's effects are still powered by the casting player, so if the player dies or leaves the area, their - * spell loses affect until they return. - *

- * When cast two copies of this spell are created. One is attached to the player and is the controlling spell, - * the other is attached to a cast spell entity and placed in the world. - * - */ -public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedSpell { - private int prevAge; - private int age; - - private boolean dead; - private int prevDeathTicks; - private int deathTicks; - - private UUID controllingEntityUuid; - private UUID controllingSpellUuid; - - public float pitch; - public float yaw; - - public PlaceableSpell(CustomisedSpellType type) { - super(type); - } - - PlaceableSpell(Caster caster, PlacementControlSpell control, Spell delegate) { - this(SpellType.PLACED_SPELL.withTraits()); - this.controllingEntityUuid = caster.asEntity().getUuid(); - this.controllingSpellUuid = control.getUuid(); - this.delegate.set(delegate); - - if (delegate instanceof PlacementDelegate s) { - s.onPlaced(caster, control); - } - } - - public float getAge(float tickDelta) { - return MathHelper.lerp(tickDelta, prevAge, age); - } - - public float getScale(float tickDelta) { - float add = MathHelper.clamp(getAge(tickDelta) / 25F, 0, 1); - float subtract = dead ? 1 - (MathHelper.lerp(tickDelta, prevDeathTicks, deathTicks) / 20F) : 0; - return MathHelper.clamp(add - subtract, 0, 1); - } - - @Override - public boolean isDying() { - return dead && deathTicks > 0 || super.isDying(); - } - - @Override - public void setDead() { - super.setDead(); - dead = true; - deathTicks = 20; - setDirty(); - } - - @Override - public boolean isDead() { - return dead && deathTicks <= 0 && super.isDead(); - } - - @Override - public boolean tick(Caster source, Situation situation) { - if (!source.isClient()) { - if (!checkConnection(source)) { - setDead(); - return true; - } - - var entry = Ether.get(source.asWorld()).get(this, source); - if (entry != null && entry.hasChanged()) { - setOrientation(source, entry.getPitch(), entry.getYaw()); - } - } - - prevAge = age; - if (age < 25) { - age++; - } - - return super.tick(source, Situation.GROUND); - } - - private boolean checkConnection(Caster source) { - return Ether.get(source.asWorld()).get(SpellType.PLACE_CONTROL_SPELL, controllingEntityUuid, controllingSpellUuid) != null; - } - - @Override - public void tickDying(Caster caster) { - super.tickDying(caster); - prevDeathTicks = deathTicks; - deathTicks--; - } - - @Override - public void setOrientation(Caster caster, float pitch, float yaw) { - this.pitch = -pitch - 90; - this.yaw = -yaw; - Entity entity = caster.asEntity(); - entity.updatePositionAndAngles(entity.getX(), entity.getY(), entity.getZ(), this.yaw, this.pitch); - entity.setYaw(this.yaw); - entity.setPitch(this.pitch); - - if (!caster.isClient()) { - var entry = Ether.get(caster.asWorld()).get(this, caster); - if (entry != null) { - entry.setPitch(pitch); - entry.setYaw(yaw); - } - } - - setDirty(); - } - - @Override - public void toNBT(NbtCompound compound) { - super.toNBT(compound); - compound.putBoolean("dead", dead); - compound.putInt("deathTicks", deathTicks); - compound.putInt("age", age); - compound.putFloat("pitch", pitch); - compound.putFloat("yaw", yaw); - if (controllingEntityUuid != null) { - compound.putUuid("owningEntity", controllingEntityUuid); - } - if (controllingSpellUuid != null) { - compound.putUuid("owningSpell", controllingSpellUuid); - } - } - - @Override - public void fromNBT(NbtCompound compound) { - super.fromNBT(compound); - dead = compound.getBoolean("dead"); - deathTicks = compound.getInt("deathTicks"); - age = compound.getInt("age"); - controllingEntityUuid = compound.containsUuid("owningEntity") ? compound.getUuid("owningEntity") : null; - controllingSpellUuid = compound.containsUuid("owningSpell") ? compound.getUuid("owningSpell") : null; - pitch = compound.getFloat("pitch"); - yaw = compound.getFloat("yaw"); - } -} diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlacementControlSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlacementControlSpell.java index 6103890b..a42cd9a3 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlacementControlSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlacementControlSpell.java @@ -7,9 +7,7 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.AbstractSpell; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; -import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; -import com.minelittlepony.unicopia.entity.mob.UEntities; import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.util.NbtSerialisable; @@ -22,8 +20,6 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; public class PlacementControlSpell extends AbstractSpell implements OrientedSpell { - @Nullable - private UUID placedSpellId; @Nullable private UUID placedEntityId; @@ -31,20 +27,21 @@ public class PlacementControlSpell extends AbstractSpell implements OrientedSpel private Optional position = Optional.empty(); private Optional orientation = Optional.empty(); - private SpellReference delegate = new SpellReference<>(); + @Nullable + private Spell delegate; public PlacementControlSpell(CustomisedSpellType type) { super(type); } PlacementControlSpell(CustomisedSpellType type, Spell delegate) { - super(type); - this.delegate.set(delegate); + this(type); + this.delegate = delegate; } @Nullable public Spell getDelegate() { - return delegate.get(); + return delegate; } public Optional getPosition() { @@ -62,20 +59,24 @@ public class PlacementControlSpell extends AbstractSpell implements OrientedSpel @Override public void setOrientation(Caster caster, float pitch, float yaw) { this.orientation = Optional.of(new Vec3d(pitch, yaw, 0)); + if (delegate instanceof OrientedSpell o) { + o.setOrientation(caster, pitch, yaw); + } setDirty(); - if (!caster.isClient() && placedEntityId != null) { - getWorld(caster).ifPresent(world -> { - var entry = Ether.get(world).get(SpellType.PLACED_SPELL, placedEntityId, placedSpellId); - if (entry != null) { - entry.setPitch(pitch); - entry.setYaw(yaw); - } - }); + if (!caster.isClient()) { + var entry = getConnection(caster); + if (entry != null) { + entry.setPitch(pitch); + entry.setYaw(yaw); + } } } @Override public boolean apply(Caster caster) { + if (delegate == null) { + return false; + } boolean result = super.apply(caster); if (result) { if (dimension.isEmpty()) { @@ -84,21 +85,18 @@ public class PlacementControlSpell extends AbstractSpell implements OrientedSpel if (position.isEmpty()) { setPosition(caster.asEntity().getPos()); } + if (delegate instanceof PlacementDelegate) { + ((PlacementDelegate)delegate).onPlaced(caster, this); + } - PlaceableSpell copy = new PlaceableSpell(caster, this, delegate.get()); + CastSpellEntity entity = new CastSpellEntity(caster.asWorld(), caster, this); - Vec3d pos = position.orElse(caster.asEntity().getPos()); + Vec3d pos = position.get(); Vec3d rot = orientation.orElse(Vec3d.ZERO); - CastSpellEntity entity = UEntities.CAST_SPELL.create(caster.asWorld()); - entity.setCaster(caster); entity.updatePositionAndAngles(pos.x, pos.y, pos.z, (float)rot.y, (float)rot.x); - entity.setYaw((float)rot.y); - entity.setPitch((float)rot.x); - copy.apply(entity); entity.getWorld().spawnEntity(entity); - placedSpellId = copy.getUuid(); placedEntityId = entity.getUuid(); setDirty(); } @@ -107,14 +105,17 @@ public class PlacementControlSpell extends AbstractSpell implements OrientedSpel @Override public boolean tick(Caster source, Situation situation) { - if (!source.isClient() && !checkConnection(source)) { + if (!source.isClient() && getConnection(source) == null) { setDead(); } return !isDead(); } - private boolean checkConnection(Caster source) { - return getWorld(source).map(world -> Ether.get(world).get(SpellType.PLACED_SPELL, placedEntityId, placedSpellId)).isPresent(); + @Nullable + private Ether.Entry getConnection(Caster source) { + return delegate == null || placedEntityId == null ? null : getWorld(source) + .map(world -> Ether.get(world).get(getDelegate().getTypeAndTraits().type(), placedEntityId, delegate.getUuid())) + .orElse(null); } private Optional getWorld(Caster source) { @@ -124,13 +125,10 @@ public class PlacementControlSpell extends AbstractSpell implements OrientedSpel @Override public void toNBT(NbtCompound compound) { super.toNBT(compound); - compound.put("spell", delegate.toNBT()); + compound.put("spell", Spell.writeNbt(delegate)); position.ifPresent(pos -> compound.put("position", NbtSerialisable.writeVector(pos))); orientation.ifPresent(o -> compound.put("orientation", NbtSerialisable.writeVector(o))); dimension.ifPresent(d -> compound.putString("dimension", d.getValue().toString())); - if (placedSpellId != null) { - compound.putUuid("placedSpellId", placedSpellId); - } if (placedEntityId != null) { compound.putUuid("placedEntityId", placedEntityId); } @@ -139,8 +137,7 @@ public class PlacementControlSpell extends AbstractSpell implements OrientedSpel @Override public void fromNBT(NbtCompound compound) { super.fromNBT(compound); - delegate.fromNBT(compound.getCompound("spell")); - placedSpellId = compound.containsUuid("placedSpellId") ? compound.getUuid("placedSpellId") : null; + delegate = Spell.readNbt(compound.getCompound("spell")); placedEntityId = compound.containsUuid("placedEntityId") ? compound.getUuid("placedEntityId") : null; position = compound.contains("position") ? Optional.of(NbtSerialisable.readVector(compound.getList("position", NbtElement.FLOAT_TYPE))) : Optional.empty(); orientation = compound.contains("orientation") ? Optional.of(NbtSerialisable.readVector(compound.getList("orientation", NbtElement.FLOAT_TYPE))) : Optional.empty(); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java index d087ed0c..92f44adf 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java @@ -156,9 +156,16 @@ public interface Spell extends NbtSerialisable, Affine { return compound == null || !compound.containsUuid("uuid") ? Util.NIL_UUID : compound.getUuid("uuid"); } - static NbtCompound writeNbt(Spell effect) { + static NbtCompound writeNbt(@Nullable Spell effect) { + if (effect == null) { + return new NbtCompound(); + } NbtCompound compound = effect.toNBT(); effect.getTypeAndTraits().toNbt(compound); return compound; } + + static Spell copy(T spell) { + return readNbt(writeNbt(spell)); + } } 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 944f1365..8a012993 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 @@ -1,12 +1,10 @@ package com.minelittlepony.unicopia.ability.magic.spell.effect; -import java.util.Optional; import java.util.UUID; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.USounds; -import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.*; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; @@ -23,7 +21,6 @@ import com.minelittlepony.unicopia.util.shape.*; import net.minecraft.block.Block; import net.minecraft.block.Blocks; import net.minecraft.entity.Entity; -import net.minecraft.entity.LivingEntity; import net.minecraft.nbt.NbtCompound; import net.minecraft.network.packet.s2c.play.PositionFlag; import net.minecraft.particle.ParticleTypes; @@ -59,12 +56,8 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. super(type); } - public boolean isLinked() { - return teleportationTarget.isSet(); - } - - public Optional> getTarget() { - return teleportationTarget.getTarget(); + public EntityReference getDestinationReference() { + return teleportationTarget; } public float getPitch() { @@ -88,13 +81,16 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. } @SuppressWarnings("unchecked") - private Optional> getDestination(Caster source) { - return getTarget().map(target -> Ether.get(source.asWorld()).get((SpellType)getType(), target, targetPortalId)); + private Ether.Entry getDestination(Caster source) { + return targetPortalId == null ? null : getDestinationReference() + .getTarget() + .map(target -> Ether.get(source.asWorld()).get((SpellType)getType(), target.uuid(), targetPortalId)) + .filter(destination -> destination.isClaimedBy(getUuid())) + .orElse(null); } @Override public boolean apply(Caster caster) { - setOrientation(caster, caster.asEntity().getPitch(), caster.asEntity().getYaw()); return toPlaceable().apply(caster); } @@ -106,19 +102,43 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. source.addParticle(ParticleTypes.ELECTRIC_SPARK, pos, Vec3d.ZERO); }); } else { - teleportationTarget.getTarget().ifPresent(target -> { - if (Ether.get(source.asWorld()).get(getType(), target, targetPortalId) == null) { - Unicopia.LOGGER.debug("Lost sibling, breaking connection to " + target.uuid()); - teleportationTarget.set(null); - setDirty(); - source.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, source.getOrigin(), Block.getRawIdFromState(Blocks.GLASS.getDefaultState())); - } - }); + var ownEntry = Ether.get(source.asWorld()).get(this, source); + synchronized (ownEntry) { + var targetEntry = getDestination(source); - getDestination(source).ifPresentOrElse( - entry -> tickWithTargetLink(source, entry), - () -> findLink(source) - ); + if (targetEntry == null) { + if (teleportationTarget.isSet()) { + teleportationTarget.set(null); + targetPortalId = null; + setDirty(); + source.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, source.getOrigin(), Block.getRawIdFromState(Blocks.GLASS.getDefaultState())); + } else { + Ether.get(source.asWorld()).anyMatch(getType(), entry -> { + if (entry.isAlive() && !entry.hasClaimant() && !entry.entityMatches(source.asEntity().getUuid())) { + entry.claim(getUuid()); + ownEntry.claim(entry.getSpellId()); + synchronized (entry) { + if (entry.getSpell() instanceof PortalSpell portal) { + portal.teleportationTarget.copyFrom(ownEntry.entity); + portal.targetPortalId = getUuid(); + portal.targetPortalPitch = pitch; + portal.targetPortalYaw = yaw; + portal.setDirty(); + } + } + teleportationTarget.copyFrom(entry.entity); + targetPortalId = entry.getSpellId(); + targetPortalPitch = entry.getPitch(); + targetPortalYaw = entry.getYaw(); + setDirty(); + } + return false; + }); + } + } else { + tickActive(source, targetEntry); + } + } } var entry = Ether.get(source.asWorld()).getOrCreate(this, source); @@ -129,13 +149,7 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. return !isDead(); } - private void tickWithTargetLink(Caster source, Ether.Entry destination) { - - if (destination.hasChanged()) { - targetPortalPitch = destination.getPitch(); - targetPortalYaw = destination.getYaw(); - } - + private void tickActive(Caster source, Ether.Entry destination) { destination.entity.getTarget().ifPresent(target -> { source.findAllEntitiesInRange(1).forEach(entity -> { if (!entity.hasPortalCooldown()) { @@ -145,8 +159,9 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. return; } - Vec3d offset = entity.getPos().subtract(source.getOriginVector()); - float yawDifference = pitch < 15 ? getYawDifference() : 0; + Vec3d offset = entity.getPos().subtract(source.asEntity().getPos()) + .add(new Vec3d(0, 0, -0.7F).rotateY(-getTargetYaw() * MathHelper.RADIANS_PER_DEGREE)); + float yawDifference = getYawDifference(); Vec3d dest = target.pos().add(offset.rotateY(yawDifference * MathHelper.RADIANS_PER_DEGREE)).add(0, 0.1, 0); if (entity.getWorld().isTopSolid(BlockPos.ofFloored(dest).up(), entity)) { @@ -176,37 +191,22 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. }); } - private void findLink(Caster source) { - if (source.isClient()) { - return; - } - - Ether.get(source.asWorld()).anyMatch(getType(), entry -> { - if (!entry.entity.referenceEquals(source.asEntity()) && entry.claim()) { - teleportationTarget.copyFrom(entry.entity); - targetPortalId = entry.getSpellId(); - setDirty(); - } - return false; - }); - } - @Override public void setOrientation(Caster caster, float pitch, float yaw) { - this.pitch = pitch; - this.yaw = yaw; + this.pitch = 90 - pitch; + this.yaw = -yaw; particleArea = PARTICLE_AREA.rotate( - pitch * MathHelper.RADIANS_PER_DEGREE, - yaw * MathHelper.RADIANS_PER_DEGREE + this.pitch * MathHelper.RADIANS_PER_DEGREE, + this.yaw * MathHelper.RADIANS_PER_DEGREE ); setDirty(); } @Override public void onPlaced(Caster source, PlacementControlSpell parent) { - parent.setOrientation(source, source.asEntity().getPitch(), source.asEntity().getYaw()); - LivingEntity caster = source.getMaster(); + Entity caster = source.asEntity(); Vec3d targetPos = caster.getRotationVector().multiply(3).add(caster.getEyePos()); + parent.setOrientation(source, -90 - source.asEntity().getPitch(), -source.asEntity().getYaw()); parent.setPosition(new Vec3d(targetPos.x, caster.getPos().y, targetPos.z)); if (source instanceof Pony pony) { Channel.SERVER_REQUEST_PLAYER_LOOK.sendToPlayer(new MsgCasterLookRequest(parent.getUuid()), (ServerPlayerEntity)pony.asEntity()); @@ -217,7 +217,10 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell. protected void onDestroyed(Caster caster) { super.onDestroyed(caster); if (!caster.isClient()) { - getDestination(caster).ifPresent(Ether.Entry::release); + var destination = getDestination(caster); + if (destination != null) { + destination.release(getUuid()); + } } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java index c35911d5..617fcf07 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java @@ -11,7 +11,6 @@ import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.ability.magic.spell.ChangelingFeedingSpell; import com.minelittlepony.unicopia.ability.magic.spell.DispersableDisguiseSpell; import com.minelittlepony.unicopia.ability.magic.spell.RainboomAbilitySpell; -import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell; import com.minelittlepony.unicopia.ability.magic.spell.PlacementControlSpell; import com.minelittlepony.unicopia.ability.magic.spell.RageAbilitySpell; import com.minelittlepony.unicopia.ability.magic.spell.Spell; @@ -45,7 +44,6 @@ public final class SpellType implements Affine, SpellPredicate< private static final DynamicCommandExceptionType UNKNOWN_SPELL_TYPE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("spell_type.unknown", id)); public static final SpellType PLACE_CONTROL_SPELL = register("place_controller", SpellType.builder(PlacementControlSpell::new).affinity(Affinity.NEUTRAL).unobtainable().stackable().shape(GemstoneItem.Shape.DONUT)); - public static final SpellType PLACED_SPELL = register("placed", builder(PlaceableSpell::new).affinity(Affinity.NEUTRAL).unobtainable().stackable().shape(GemstoneItem.Shape.DONUT)); public static final SpellType THROWN_SPELL = register("thrown", builder(ThrowableSpell::new).affinity(Affinity.NEUTRAL).unobtainable().shape(GemstoneItem.Shape.DONUT)); public static final SpellType CHANGELING_DISGUISE = register("disguise", builder(DispersableDisguiseSpell::new).affinity(Affinity.BAD).color(0x19E48E).unobtainable().shape(GemstoneItem.Shape.ARROW)); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/DismissSpellScreen.java b/src/main/java/com/minelittlepony/unicopia/client/gui/DismissSpellScreen.java index 9ff61ac3..bad720d8 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/DismissSpellScreen.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/DismissSpellScreen.java @@ -2,9 +2,9 @@ package com.minelittlepony.unicopia.client.gui; import java.util.ArrayList; import java.util.List; - import org.joml.Vector4f; +import com.google.common.base.MoreObjects; import com.minelittlepony.common.client.gui.GameGui; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.SpellPredicate; @@ -136,8 +136,8 @@ public class DismissSpellScreen extends GameGui { } private Spell getActualSpell() { - return spell instanceof AbstractDelegatingSpell s && s.getDelegate() instanceof Spell p ? p - : spell instanceof PlacementControlSpell s && s.getDelegate() instanceof Spell p ? p + return spell instanceof AbstractDelegatingSpell s ? MoreObjects.firstNonNull(s.getDelegate(), s) + : spell instanceof PlacementControlSpell s ? MoreObjects.firstNonNull(s.getDelegate(), s) : spell; } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/HornFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/HornFeatureRenderer.java index 2b38a87f..8b430d57 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/HornFeatureRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/HornFeatureRenderer.java @@ -3,7 +3,7 @@ package com.minelittlepony.unicopia.client.render; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.ability.AbilityDispatcher.Stat; -import com.minelittlepony.unicopia.ability.magic.SpellPredicate; +import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import net.minecraft.client.model.Dilation; import net.minecraft.client.model.Model; @@ -56,7 +56,7 @@ public class HornFeatureRenderer implements AccessoryFea .flatMap(Stat::getActiveAbility) .map(ability -> ability.getColor(pony)) .filter(i -> i != -1).or(() -> pony.getSpellSlot() - .get(SpellPredicate.IS_NOT_PLACED) + .get(SpellType.PLACE_CONTROL_SPELL.negate()) .map(spell -> spell.getTypeAndTraits().type().getColor())); }).ifPresent(color -> { model.setState(true); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/CastSpellEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/CastSpellEntityRenderer.java index ece6638b..086d3b48 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/CastSpellEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/CastSpellEntityRenderer.java @@ -1,16 +1,35 @@ package com.minelittlepony.unicopia.client.render.entity; +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.common.util.Color; +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.ability.magic.spell.Spell; +import com.minelittlepony.unicopia.client.render.model.PlaneModel; import com.minelittlepony.unicopia.client.render.spell.SpellEffectsRenderDispatcher; +import com.minelittlepony.unicopia.client.render.spell.SpellRenderer; import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.EntityRenderer; import net.minecraft.client.render.entity.EntityRendererFactory; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.screen.PlayerScreenHandler; import net.minecraft.util.Identifier; +import net.minecraft.util.math.RotationAxis; public class CastSpellEntityRenderer extends EntityRenderer { + private static final Identifier[] TEXTURES = new Identifier[] { + Unicopia.id("textures/particles/runes_0.png"), + Unicopia.id("textures/particles/runes_1.png"), + Unicopia.id("textures/particles/runes_2.png"), + Unicopia.id("textures/particles/runes_3.png"), + Unicopia.id("textures/particles/runes_4.png"), + Unicopia.id("textures/particles/runes_5.png") + }; + public CastSpellEntityRenderer(EntityRendererFactory.Context ctx) { super(ctx); } @@ -21,11 +40,62 @@ public class CastSpellEntityRenderer extends EntityRenderer { } @Override - public void render(CastSpellEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light) { - SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertexConsumers, light, entity, 0, 0, tickDelta, getAnimationProgress(entity, tickDelta), yaw, 0); + public void render(CastSpellEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light) { + matrices.push(); + matrices.translate(0, 0.001, 0); + final float height = entity.getHeight(); + final float pitch = entity.getPitch(tickDelta); + matrices.translate(0, (-pitch / 90F) * height * 0.5F, 0); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw)); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-pitch)); + + float animationProgress = getAnimationProgress(entity, tickDelta); + renderAmbientEffects(matrices, vertices, entity, entity.getSpellSlot().get().orElse(null), light, animationProgress, tickDelta); + SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertices, light, entity, entity.getScale(tickDelta), 0, tickDelta, animationProgress, yaw, pitch); + + matrices.pop(); } protected float getAnimationProgress(CastSpellEntity entity, float tickDelta) { return entity.age + tickDelta; } + + protected void renderAmbientEffects(MatrixStack matrices, VertexConsumerProvider vertices, CastSpellEntity entity, @Nullable Spell spell, int light, float animationProgress, float tickDelta) { + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90)); + + float scale = entity.getScale(tickDelta) * 3; + matrices.scale(scale, scale, scale); + + float angle = (animationProgress / 9F) % 360; + + int color = spell == null ? 0 : spell.getTypeAndTraits().type().getColor(); + + float red = Color.r(color); + float green = Color.g(color); + float blue = Color.b(color); + + @Nullable + SpellRenderer renderer = spell == null ? null : SpellEffectsRenderDispatcher.INSTANCE.getRenderer(spell); + + for (int i = 0; i < TEXTURES.length; i++) { + if (renderer != null && !renderer.shouldRenderEffectPass(i)) { + continue; + } + VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(TEXTURES[i])); + + for (int dim = 0; dim < 3; dim++) { + float ringSpeed = (i % 2 == 0 ? i : -1) * i; + + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(angle * ringSpeed)); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(angle * ringSpeed * dim)); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(angle * ringSpeed * dim)); + PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, red, green, blue, scale / ((float)(dim * 3) + 1)); + matrices.pop(); + } + } + + matrices.pop(); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java deleted file mode 100644 index 856064d8..00000000 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.minelittlepony.unicopia.client.render.spell; - -import com.minelittlepony.common.util.Color; -import com.minelittlepony.unicopia.Unicopia; -import com.minelittlepony.unicopia.ability.magic.Caster; -import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell; -import com.minelittlepony.unicopia.ability.magic.spell.Spell; -import com.minelittlepony.unicopia.client.render.model.PlaneModel; - -import net.minecraft.client.render.RenderLayer; -import net.minecraft.client.render.VertexConsumer; -import net.minecraft.client.render.VertexConsumerProvider; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.RotationAxis; - -public class PlacedSpellRenderer extends SpellRenderer { - private static final Identifier[] TEXTURES = new Identifier[] { - Unicopia.id("textures/particles/runes_0.png"), - Unicopia.id("textures/particles/runes_1.png"), - Unicopia.id("textures/particles/runes_2.png"), - Unicopia.id("textures/particles/runes_3.png"), - Unicopia.id("textures/particles/runes_4.png"), - Unicopia.id("textures/particles/runes_5.png") - }; - - @Override - public void render(MatrixStack matrices, VertexConsumerProvider vertices, PlaceableSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { - matrices.push(); - - Spell delegate = spell.getDelegate(); - - if (delegate != null) { - renderAmbientEffects(matrices, vertices, spell, delegate, caster, light, animationProgress, tickDelta); - - matrices.push(); - float height = caster.asEntity().getHeight(); - matrices.translate(0, (-spell.pitch / 90F) * height * 0.5F, 0); - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(spell.yaw)); - matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-spell.pitch)); - SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertices, delegate, caster, light, spell.getScale(tickDelta), limbDistance, tickDelta, animationProgress, headYaw, headPitch); - matrices.pop(); - } - - matrices.pop(); - } - - protected void renderAmbientEffects(MatrixStack matrices, VertexConsumerProvider vertices, PlaceableSpell spell, Spell delegate, Caster caster, int light, float animationProgress, float tickDelta) { - matrices.push(); - matrices.translate(0, 0.001, 0); - - float height = caster.asEntity().getHeight(); - matrices.translate(0, (-spell.pitch / 90F) * height * 0.5F, 0); - - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(spell.yaw)); - matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-spell.pitch)); - matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90)); - - float scale = spell.getScale(tickDelta) * 3; - matrices.scale(scale, scale, scale); - - float angle = (animationProgress / 9F) % 360; - - int color = delegate.getTypeAndTraits().type().getColor(); - - float red = Color.r(color); - float green = Color.g(color); - float blue = Color.b(color); - - SpellRenderer renderer = SpellEffectsRenderDispatcher.INSTANCE.getRenderer(delegate); - - for (int i = 0; i < TEXTURES.length; i++) { - if (!renderer.shouldRenderEffectPass(i)) { - continue; - } - VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(TEXTURES[i])); - - for (int dim = 0; dim < 3; dim++) { - float ringSpeed = (i % 2 == 0 ? i : -1) * i; - - matrices.push(); - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(angle * ringSpeed)); - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(angle * ringSpeed * dim)); - matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(angle * ringSpeed * dim)); - PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, red, green, blue, scale / ((float)(dim * 3) + 1)); - matrices.pop(); - } - } - - matrices.pop(); - } -} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java index e594b647..7a558eef 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java @@ -159,13 +159,13 @@ class PortalFrameBuffer implements AutoCloseable { Camera camera = client.gameRenderer.getCamera(); Entity cameraEntity = UEntities.CAST_SPELL.create(caster.asWorld()); - Vec3d offset = new Vec3d(0, -0.2F, -0.2F).rotateY(-spell.getTargetYaw() * MathHelper.RADIANS_PER_DEGREE); + Vec3d offset = new Vec3d(0, 0, -0.1F).rotateY(-spell.getTargetYaw() * MathHelper.RADIANS_PER_DEGREE); float yaw = spell.getTargetYaw() + camera.getYaw() - spell.getYaw() + 180; float pitch = spell.getTargetPitch() + (camera.getPitch() - spell.getPitch()) * 1.65F; cameraEntity.setPosition(target.pos().add(offset)); - cameraEntity.setPitch(pitch); + cameraEntity.setPitch(90 + pitch); cameraEntity.setYaw(yaw); drawWorld(cameraEntity, 400, 400); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java index a75c3f72..bb65f063 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java @@ -10,6 +10,7 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.Entity; import net.minecraft.util.math.RotationAxis; public class PortalSpellRenderer extends SpellRenderer { @@ -29,7 +30,9 @@ public class PortalSpellRenderer extends SpellRenderer { SphereModel.DISK.render(matrices, buff, light, 0, 2F * strength, 1, 1, 1, 1); matrices.pop(); - if (Unicopia.getConfig().simplifiedPortals.get() || !spell.isLinked()) { + EntityReference destination = spell.getDestinationReference(); + + if (Unicopia.getConfig().simplifiedPortals.get() || !destination.isSet()) { matrices.push(); matrices.translate(0, -0.02, 0); matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180)); @@ -52,7 +55,7 @@ public class PortalSpellRenderer extends SpellRenderer { matrices.push(); matrices.scale(strength, strength, strength); - spell.getTarget().ifPresent(target -> { + destination.getTarget().ifPresent(target -> { float grown = Math.min(caster.asEntity().age, 20) / 20F; matrices.push(); matrices.translate(0, -0.01, 0); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java index 423a8888..d80d55b6 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java @@ -47,7 +47,6 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader } static { - register(SpellType.PLACED_SPELL, PlacedSpellRenderer::new); register(SpellType.SHIELD, ShieldSpellRenderer::new); register(SpellType.DARK_VORTEX, DarkVortexSpellRenderer::new); register(SpellType.BUBBLE, BubbleSpellRenderer::new); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java index 2ae93d61..18280081 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java @@ -1,10 +1,14 @@ package com.minelittlepony.unicopia.entity.mob; +import java.util.UUID; + import com.minelittlepony.unicopia.*; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Levelled; import com.minelittlepony.unicopia.ability.magic.SpellInventory; +import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.ability.magic.SpellSlots; +import com.minelittlepony.unicopia.ability.magic.spell.PlacementControlSpell; import com.minelittlepony.unicopia.ability.magic.spell.Situation; import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; @@ -13,6 +17,7 @@ import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.MagicImmune; import com.minelittlepony.unicopia.entity.Physics; import com.minelittlepony.unicopia.network.track.Trackable; +import com.minelittlepony.unicopia.server.world.Ether; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityDimensions; @@ -24,6 +29,7 @@ import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.data.TrackedDataHandlerRegistry; import net.minecraft.nbt.NbtCompound; import net.minecraft.text.Text; +import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; public class CastSpellEntity extends LightEmittingEntity implements Caster, WeaklyOwned.Mutable, MagicImmune { @@ -32,6 +38,8 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster CORRUPTION = DataTracker.registerData(CastSpellEntity.class, TrackedDataHandlerRegistry.INTEGER); private static final TrackedData MAX_CORRUPTION = DataTracker.registerData(CastSpellEntity.class, TrackedDataHandlerRegistry.INTEGER); + private static final TrackedData DEAD = DataTracker.registerData(CastSpellEntity.class, TrackedDataHandlerRegistry.BOOLEAN); + private final EntityPhysics physics = new EntityPhysics<>(this); private final SpellInventory spells = SpellSlots.ofSingle(this); @@ -49,6 +57,23 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster dataTracker.get(MAX_CORRUPTION) ); + private UUID controllingEntityUuid; + private UUID controllingSpellUuid; + + private int prevAge; + + private int prevDeathTicks; + private int deathTicks; + + public CastSpellEntity(World world, Caster caster, PlacementControlSpell control) { + this(UEntities.CAST_SPELL, world); + this.controllingEntityUuid = caster.asEntity().getUuid(); + this.controllingSpellUuid = control.getUuid(); + setCaster(caster); + Spell spell = Spell.copy(control.getDelegate()); + spells.getSlots().put(spell); + } + public CastSpellEntity(EntityType type, World world) { super(type, world); ignoreCameraFrustum = true; @@ -61,6 +86,27 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster spell.setOrientation(this, pitch, yaw)); + } + + private boolean checkConnection() { + return Ether.get(getWorld()).get(SpellType.PLACE_CONTROL_SPELL, controllingEntityUuid, controllingSpellUuid) != null; + } + + public float getAge(float tickDelta) { + return MathHelper.lerp(tickDelta, prevAge, age); + } + + public float getScale(float tickDelta) { + float add = MathHelper.clamp(getAge(tickDelta) / 25F, 0, 1); + float subtract = MathHelper.clamp(MathHelper.lerp(tickDelta, prevDeathTicks, deathTicks) / 20F, 0, 1); + return MathHelper.clamp(add - subtract, 0, 1); } @Override @@ -78,19 +124,51 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster { + var entry = Ether.get(getWorld()).getOrCreate(spell, this); + if (entry.hasChanged()) { + //updatePositionAndAngles(getX(), getY(), getZ(), entry.getYaw(), entry.getPitch()); + } + }); + } + + prevDeathTicks = deathTicks; + + if (!spells.tick(Situation.GROUND) && deathTicks++ > 40) { + remove(Entity.RemovalReason.KILLED); + } + } + + @Override + public void kill() { + spells.getSlots().clear(); + } + + public boolean isDead() { + return dataTracker.get(DEAD); + } + + public void setDead(boolean dead) { + dataTracker.set(DEAD, dead); + if (dead) { + spells.getSlots().clear(); } } @Override public EntityDimensions getDimensions(EntityPose pose) { - return super.getDimensions(pose).scaled(getSpellSlot().get(SpellType.IS_PLACED).map(spell -> spell.getScale(1)).orElse(1F)); + return super.getDimensions(pose).scaled(getScale(1)); } @Override @@ -154,23 +232,42 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster CAST_SPELL = register("cast_spell", FabricEntityTypeBuilder.create(SpawnGroup.MISC, CastSpellEntity::new) + EntityType CAST_SPELL = register("cast_spell", FabricEntityTypeBuilder.create(SpawnGroup.MISC, CastSpellEntity::new) .trackRangeBlocks(200) .disableSummon() .dimensions(EntityDimensions.changing(4, 4))); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java index d9e270ba..b5f18c77 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -892,7 +892,7 @@ public class Pony extends Living implements Copyable, Update oldSuppressedRace = Race.UNSET; Channel.SERVER_SELECT_TRIBE.sendToPlayer(new MsgTribeSelect(Race.allPermitted(entity), "gui.unicopia.tribe_selection.respawn"), (ServerPlayerEntity)entity); } else { - oldPlayer.getSpellSlot().stream().filter(SpellPredicate.IS_PLACED).forEach(getSpellSlot()::put); + oldPlayer.getSpellSlot().stream().filter(SpellType.PLACE_CONTROL_SPELL).forEach(getSpellSlot()::put); } // putting it here instead of adding another injection point into ServerPlayerEntity.copyFrom() diff --git a/src/main/java/com/minelittlepony/unicopia/item/EnchantedStaffItem.java b/src/main/java/com/minelittlepony/unicopia/item/EnchantedStaffItem.java index 04868e0b..fdf89f0c 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/EnchantedStaffItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/EnchantedStaffItem.java @@ -9,7 +9,6 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.Caster; -import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod; import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; @@ -47,7 +46,7 @@ public class EnchantedStaffItem extends StaffItem implements EnchantableItem, Ch public static SpellType getSpellType(Entity entity, boolean remove) { if (entity instanceof CastSpellEntity cast) { - return cast.getSpellSlot().get(c -> !SpellPredicate.IS_PLACED.test(c)) + return cast.getSpellSlot().get(SpellType.PLACE_CONTROL_SPELL.negate()) .map(Spell::getTypeAndTraits) .map(CustomisedSpellType::type) .orElse(SpellType.empty()); diff --git a/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java b/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java index 13707da6..21980091 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java @@ -168,13 +168,14 @@ public class Ether extends PersistentState { private WeakReference spell; private boolean removed; - private boolean taken; private float pitch; private final AtomicBoolean changed = new AtomicBoolean(true); private float yaw; private float radius; + private final Set claimants = new HashSet<>(); + private Entry(NbtElement nbt) { this.entity = new EntityReference<>(); this.spell = new WeakReference<>(null); @@ -227,7 +228,7 @@ public class Ether extends PersistentState { markDirty(); } - boolean isAlive() { + public boolean isAlive() { return !isDead(); } @@ -246,6 +247,7 @@ public class Ether extends PersistentState { public void markDead() { Unicopia.LOGGER.debug("Marking " + entity.getTarget().orElse(null) + " as dead"); removed = true; + claimants.clear(); markDirty(); } @@ -253,25 +255,22 @@ public class Ether extends PersistentState { return entity.getTarget().filter(target -> uuid.equals(target.uuid())).isPresent(); } - public boolean isAvailable() { - return !isDead() && !taken && entity.isSet(); - } - - public void setTaken(boolean taken) { - this.taken = taken; + public void claim(UUID claimant) { + claimants.add(claimant); markDirty(); } - public void release() { - setTaken(false); + public void release(UUID claimant) { + claimants.remove(claimant); + markDirty(); } - public boolean claim() { - if (isAvailable()) { - setTaken(true); - return true; - } - return false; + public boolean isClaimedBy(UUID claimant) { + return claimants.contains(claimant); + } + + public boolean hasClaimant() { + return !claimants.isEmpty(); } @Nullable @@ -315,24 +314,34 @@ public class Ether extends PersistentState { public void toNBT(NbtCompound compound) { entity.toNBT(compound); compound.putBoolean("removed", removed); - compound.putBoolean("taken", taken); compound.putFloat("pitch", pitch); compound.putFloat("yaw", yaw); compound.putFloat("radius", radius); if (spellId != null) { compound.putUuid("spellId", spellId); } + NbtList list = new NbtList(); + claimants.forEach(claimant -> { + list.add(NbtHelper.fromUuid(claimant)); + }); + compound.put("claimants", list); } @Override public void fromNBT(NbtCompound compound) { entity.fromNBT(compound); removed = compound.getBoolean("removed"); - taken = compound.getBoolean("taken"); pitch = compound.getFloat("pitch"); yaw = compound.getFloat("yaw"); radius = compound.getFloat("radius"); spellId = compound.containsUuid("spellid") ? compound.getUuid("spellId") : null; + + claimants.clear(); + if (compound.contains("claimants", NbtElement.LIST_TYPE)) { + compound.getList("claimants", NbtElement.INT_ARRAY_TYPE).forEach(el -> { + claimants.add(NbtHelper.toUuid(el)); + }); + } } @Override