Sync all spells onto the ether

This commit is contained in:
Sollace 2024-05-23 12:53:11 +01:00
parent 77de42dd09
commit 8a093a8a8b
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
16 changed files with 154 additions and 111 deletions

View file

@ -6,6 +6,8 @@ import java.util.stream.Stream;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
import com.minelittlepony.unicopia.server.world.Ether;
import net.minecraft.nbt.NbtCompound;
public abstract class AbstractDelegatingSpell implements Spell {
@ -101,6 +103,9 @@ public abstract class AbstractDelegatingSpell implements Spell {
}
protected void onDestroyed(Caster<?> caster) {
if (!caster.isClient()) {
Ether.get(caster.asWorld()).remove(this, caster);
}
if (delegate.get() instanceof Spell s) {
s.destroy(caster);
}

View file

@ -28,10 +28,11 @@ public abstract class AbstractDisguiseSpell extends AbstractSpell implements Dis
@Override
protected void onDestroyed(Caster<?> caster) {
super.onDestroyed(caster);
caster.asEntity().calculateDimensions();
caster.asEntity().setInvisible(false);
if (caster instanceof Pony) {
((Pony) caster).setInvisible(false);
if (caster instanceof Pony pony) {
pony.setInvisible(false);
}
disguise.remove();
}

View file

@ -51,16 +51,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
}
}
@Override
public boolean apply(Caster<?> caster) {
boolean result = super.apply(caster);
if (result && !caster.isClient()) {
Ether.get(caster.asWorld()).getOrCreate(this, caster);
}
setDirty();
return result;
}
public float getAge(float tickDelta) {
return MathHelper.lerp(tickDelta, prevAge, age);
}
@ -91,9 +81,16 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
@Override
public boolean tick(Caster<?> source, Situation situation) {
if (!source.isClient() && !checkConnection(source)) {
if (!source.isClient()) {
if (!checkConnection(source)) {
setDead();
return false;
return true;
}
var entry = Ether.get(source.asWorld()).get(this, source);
if (entry != null && entry.hasChanged()) {
setOrientation(source, entry.getPitch(), entry.getYaw());
}
}
prevAge = age;
@ -115,14 +112,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
deathTicks--;
}
@Override
protected void onDestroyed(Caster<?> source) {
if (!source.isClient()) {
Ether.get(source.asWorld()).remove(this, source);
}
super.onDestroyed(source);
}
@Override
public void setOrientation(Caster<?> caster, float pitch, float yaw) {
this.pitch = -pitch - 90;
@ -131,6 +120,15 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
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();
}

View file

@ -5,11 +5,9 @@ import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
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.EntityReference;
import com.minelittlepony.unicopia.entity.EntityReference.EntityValues;
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
import com.minelittlepony.unicopia.entity.mob.UEntities;
import com.minelittlepony.unicopia.server.world.Ether;
@ -23,15 +21,18 @@ import net.minecraft.util.Identifier;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class PlacementControlSpell extends AbstractDelegatingSpell implements OrientedSpell {
public class PlacementControlSpell extends AbstractSpell implements OrientedSpell {
@Nullable
private UUID placedSpellId;
private final EntityReference<CastSpellEntity> castEntity = new EntityReference<>();
@Nullable
private UUID placedEntityId;
private Optional<RegistryKey<World>> dimension = Optional.empty();
private Optional<Vec3d> position = Optional.empty();
private Optional<Vec3d> orientation = Optional.empty();
private SpellReference<Spell> delegate = new SpellReference<>();
public PlacementControlSpell(CustomisedSpellType<?> type) {
super(type);
}
@ -41,6 +42,11 @@ public class PlacementControlSpell extends AbstractDelegatingSpell implements Or
this.delegate.set(delegate);
}
@Nullable
public Spell getDelegate() {
return delegate.get();
}
public Optional<Vec3d> getPosition() {
return position;
}
@ -56,54 +62,36 @@ public class PlacementControlSpell extends AbstractDelegatingSpell implements Or
@Override
public void setOrientation(Caster<?> caster, float pitch, float yaw) {
this.orientation = Optional.of(new Vec3d(pitch, yaw, 0));
castEntity.ifPresent(caster.asWorld(), entity -> {
entity.getSpellSlot().stream(SpellPredicate.IS_ORIENTED).forEach(spell -> {
if (!getTypeAndTraits().type().test(spell)) {
spell.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);
}
});
});
setDirty();
}
}
@Override
public boolean apply(Caster<?> caster) {
boolean result = super.apply(caster);
if (result) {
if (!caster.isClient()) {
Ether.get(caster.asWorld()).getOrCreate(this, caster);
}
if (dimension.isEmpty()) {
setDimension(caster.asWorld().getRegistryKey());
}
if (position.isEmpty()) {
setPosition(caster.asEntity().getPos());
}
}
return result;
}
@Override
public boolean tick(Caster<?> source, Situation situation) {
if (!source.isClient()) {
Ether.get(source.asWorld()).getOrCreate(this, source);
castEntity.getTarget().ifPresentOrElse(target -> {
if (!checkConnection(source, target)) {
setDead();
}
}, () -> spawnPlacedEntity(source));
}
return !isDead();
}
PlaceableSpell copy = new PlaceableSpell(caster, this, delegate.get());
private void spawnPlacedEntity(Caster<?> source) {
PlaceableSpell copy = new PlaceableSpell(source, this, getDelegate());
Vec3d pos = position.orElse(source.asEntity().getPos());
Vec3d pos = position.orElse(caster.asEntity().getPos());
Vec3d rot = orientation.orElse(Vec3d.ZERO);
CastSpellEntity entity = UEntities.CAST_SPELL.create(source.asWorld());
entity.setCaster(source);
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);
@ -111,52 +99,54 @@ public class PlacementControlSpell extends AbstractDelegatingSpell implements Or
entity.getWorld().spawnEntity(entity);
placedSpellId = copy.getUuid();
castEntity.set(entity);
placedEntityId = entity.getUuid();
setDirty();
}
return result;
}
private boolean checkConnection(Caster<?> source, EntityValues<?> target) {
return getWorld(source)
.map(Ether::get)
.map(ether -> ether.get(SpellType.PLACED_SPELL, target, placedSpellId))
.isPresent();
@Override
public boolean tick(Caster<?> source, Situation situation) {
if (!source.isClient() && !checkConnection(source)) {
setDead();
}
return !isDead();
}
private boolean checkConnection(Caster<?> source) {
return getWorld(source).map(world -> Ether.get(world).get(SpellType.PLACED_SPELL, placedEntityId, placedSpellId)).isPresent();
}
private Optional<World> getWorld(Caster<?> source) {
return dimension.map(source.asWorld().getServer()::getWorld);
}
@Override
protected void onDestroyed(Caster<?> source) {
if (!source.isClient()) {
Ether.get(source.asWorld()).remove(this, source);
}
super.onDestroyed(source);
}
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
compound.put("spell", delegate.toNBT());
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);
}
compound.put("castEntity", castEntity.toNBT());
if (placedEntityId != null) {
compound.putUuid("placedEntityId", placedEntityId);
}
}
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
delegate.fromNBT(compound.getCompound("spell"));
placedSpellId = compound.containsUuid("placedSpellId") ? compound.getUuid("placedSpellId") : null;
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();
if (compound.contains("dimension", NbtElement.STRING_TYPE)) {
dimension = Optional.ofNullable(Identifier.tryParse(compound.getString("dimension"))).map(id -> RegistryKey.of(RegistryKeys.WORLD, id));
}
castEntity.fromNBT(compound.getCompound("castEntity"));
}
public interface PlacementDelegate {

View file

@ -13,6 +13,7 @@ import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
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 com.minelittlepony.unicopia.util.NbtSerialisable;
import net.minecraft.nbt.NbtCompound;
@ -81,6 +82,9 @@ public interface Spell extends NbtSerialisable, Affine {
*/
default boolean apply(Caster<?> caster) {
caster.getSpellSlot().put(this);
if (!caster.isClient()) {
Ether.get(caster.asWorld()).getOrCreate(this, caster);
}
return true;
}

View file

@ -5,6 +5,7 @@ import java.util.UUID;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.server.world.Ether;
import net.minecraft.nbt.NbtCompound;
@ -78,9 +79,6 @@ public abstract class AbstractSpell implements Spell {
this.hidden = hidden;
}
protected void onDestroyed(Caster<?> caster) {
}
@Override
public void tickDying(Caster<?> caster) {
dead = true;
@ -96,6 +94,12 @@ public abstract class AbstractSpell implements Spell {
onDestroyed(caster);
}
protected void onDestroyed(Caster<?> caster) {
if (!caster.isClient()) {
Ether.get(caster.asWorld()).remove(this, caster);
}
}
@Override
public void toNBT(NbtCompound compound) {
compound.putBoolean("dying", dying);

View file

@ -54,11 +54,6 @@ public class AreaProtectionSpell extends AbstractAreaEffectSpell {
return !isDead();
}
@Override
protected void onDestroyed(Caster<?> caster) {
Ether.get(caster.asWorld()).remove(this, caster);
}
/**
* Calculates the maximum radius of the shield. aka The area of effect.
*/

View file

@ -139,6 +139,7 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
@Override
protected void onDestroyed(Caster<?> source) {
super.onDestroyed(source);
if (source.asEntity() instanceof LivingEntity l) {
MODIFIERS.forEach((attribute, modifier) -> {
if (l.getAttributes().hasAttribute(attribute)) {

View file

@ -90,6 +90,7 @@ public class DisplacementSpell extends AbstractSpell implements HomingSpell, Pro
@Override
protected void onDestroyed(Caster<?> caster) {
super.onDestroyed(caster);
caster.getOriginatingCaster().asEntity().setGlowing(false);
target.ifPresent(caster.asWorld(), e -> e.setGlowing(false));
}

View file

@ -92,8 +92,7 @@ public class HydrophobicSpell extends AbstractSpell {
}
double range = getRange(source);
var entry = Ether.get(source.asWorld()).getOrCreate(this, source);
entry.radius = (float)range;
Ether.get(source.asWorld()).getOrCreate(this, source).setRadius((float)range);
source.spawnParticles(new Sphere(true, range), 10, pos -> {
BlockPos bp = BlockPos.ofFloored(pos);
@ -116,7 +115,7 @@ public class HydrophobicSpell extends AbstractSpell {
@Override
protected void onDestroyed(Caster<?> caster) {
Ether.get(caster.asWorld()).remove(this, caster);
super.onDestroyed(caster);
storedFluidPositions.removeIf(entry -> {
if (caster.canModifyAt(entry.pos())) {
entry.restore(caster.asWorld());
@ -175,7 +174,7 @@ public class HydrophobicSpell extends AbstractSpell {
}
public boolean blocksFlow(Ether.Entry<?> entry, Vec3d center, BlockPos pos, FluidState fluid) {
return fluid.isIn(affectedFluid) && pos.isWithinDistance(center, (double)entry.radius + 1);
return fluid.isIn(affectedFluid) && pos.isWithinDistance(center, (double)entry.getRadius() + 1);
}
public static boolean blocksFluidFlow(BlockView world, BlockPos pos, FluidState state) {

View file

@ -91,6 +91,7 @@ public class LightSpell extends AbstractSpell implements TimedSpell, ProjectileD
@Override
protected void onDestroyed(Caster<?> caster) {
super.onDestroyed(caster);
if (caster.isClient()) {
return;
}

View file

@ -153,6 +153,7 @@ public class NecromancySpell extends AbstractAreaEffectSpell implements Projecti
@Override
protected void onDestroyed(Caster<?> caster) {
super.onDestroyed(caster);
if (caster.isClient()) {
return;
}

View file

@ -121,11 +121,9 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell.
);
}
Ether ether = Ether.get(source.asWorld());
var entry = ether.getOrCreate(this, source);
entry.pitch = pitch;
entry.yaw = yaw;
ether.markDirty();
var entry = Ether.get(source.asWorld()).getOrCreate(this, source);
entry.setPitch(pitch);
entry.setYaw(yaw);
}
return !isDead();
@ -133,13 +131,9 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell.
private void tickWithTargetLink(Caster<?> source, Ether.Entry<?> destination) {
if (!MathHelper.approximatelyEquals(targetPortalPitch, destination.pitch)) {
targetPortalPitch = destination.pitch;
setDirty();
}
if (!MathHelper.approximatelyEquals(targetPortalYaw, destination.yaw)) {
targetPortalYaw = destination.yaw;
setDirty();
if (destination.hasChanged()) {
targetPortalPitch = destination.getPitch();
targetPortalYaw = destination.getYaw();
}
destination.entity.getTarget().ifPresent(target -> {
@ -221,9 +215,11 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell.
@Override
protected void onDestroyed(Caster<?> caster) {
Ether.get(caster.asWorld()).remove(getType(), caster);
super.onDestroyed(caster);
if (!caster.isClient()) {
getDestination(caster).ifPresent(Ether.Entry::release);
}
}
@Override
public void toNBT(NbtCompound compound) {

View file

@ -19,6 +19,7 @@ import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.projectile.ProjectileUtil;
import com.minelittlepony.unicopia.server.world.Ether;
import com.minelittlepony.unicopia.util.ColorHelper;
import com.minelittlepony.unicopia.util.Lerp;
import com.minelittlepony.unicopia.util.shape.Sphere;
@ -115,6 +116,8 @@ public class ShieldSpell extends AbstractSpell {
if (source.isClient()) {
generateParticles(source);
} else {
Ether.get(source.asWorld()).getOrCreate(this, source).setRadius(radius.getValue());
}
if (situation == Situation.PROJECTILE) {

View file

@ -136,7 +136,9 @@ public class DismissSpellScreen extends GameGui {
}
private Spell getActualSpell() {
return spell instanceof AbstractDelegatingSpell s && s.getDelegate() instanceof Spell p ? p : spell;
return spell instanceof AbstractDelegatingSpell s && s.getDelegate() instanceof Spell p ? p
: spell instanceof PlacementControlSpell s && s.getDelegate() instanceof Spell p ? p
: spell;
}
@Override
@ -161,11 +163,10 @@ public class DismissSpellScreen extends GameGui {
MatrixStack matrices = context.getMatrices();
var type = actualSpell.getTypeAndTraits();
var affinity = actualSpell.getAffinity();
copy.set(mouseX - width * 0.5F - x * 0.5F, mouseY - height * 0.5F - y * 0.5F, 0, 0);
DrawableUtil.drawLine(matrices, 0, 0, (int)x, (int)y, affinity.getColor().getColorValue());
DrawableUtil.drawLine(matrices, 0, 0, (int)x, (int)y, actualSpell.getAffinity().getColor().getColorValue());
DrawableUtil.renderItemIcon(context, actualSpell.isDead() ? UItems.BOTCHED_GEM.getDefaultStack() : type.getDefaultStack(),
x - 8 - copy.x * 0.2F,
y - 8 - copy.y * 0.2F,

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.server.world;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
@ -15,6 +16,7 @@ import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import net.minecraft.nbt.*;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.PersistentState;
import net.minecraft.world.World;
@ -168,9 +170,10 @@ public class Ether extends PersistentState {
private boolean removed;
private boolean taken;
public float pitch;
public float yaw;
public float radius;
private float pitch;
private final AtomicBoolean changed = new AtomicBoolean(true);
private float yaw;
private float radius;
private Entry(NbtElement nbt) {
this.entity = new EntityReference<>();
@ -184,6 +187,46 @@ public class Ether extends PersistentState {
spellId = spell.getUuid();
}
public boolean hasChanged() {
return changed.getAndSet(false);
}
public float getPitch() {
return pitch;
}
public void setPitch(float pitch) {
if (!MathHelper.approximatelyEquals(this.pitch, pitch)) {
this.pitch = pitch;
changed.set(true);
}
markDirty();
}
public float getYaw() {
return yaw;
}
public void setYaw(float yaw) {
if (!MathHelper.approximatelyEquals(this.yaw, yaw)) {
this.yaw = yaw;
changed.set(true);
}
markDirty();
}
public float getRadius() {
return radius;
}
public void setRadius(float radius) {
if (!MathHelper.approximatelyEquals(this.radius, radius)) {
this.radius = radius;
changed.set(true);
}
markDirty();
}
boolean isAlive() {
return !isDead();
}