Split the placed spell into separate types

This commit is contained in:
Sollace 2024-05-22 21:21:34 +01:00
parent 575c1f7438
commit 77de42dd09
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
13 changed files with 261 additions and 211 deletions

View file

@ -1,11 +1,12 @@
package com.minelittlepony.unicopia.ability.magic.spell; package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.ability.data.Rot; import com.minelittlepony.unicopia.ability.data.Rot;
import com.minelittlepony.unicopia.ability.magic.Caster;
public interface OrientedSpell extends Spell { public interface OrientedSpell extends Spell {
void setOrientation(float pitch, float yaw); void setOrientation(Caster<?> caster, float pitch, float yaw);
default void setOrientation(Rot rotation) { default void setOrientation(Caster<?> caster, Rot rotation) {
setOrientation(rotation.pitch(), rotation.yaw()); setOrientation(caster, rotation.pitch(), rotation.yaw());
} }
} }

View file

@ -1,28 +1,16 @@
package com.minelittlepony.unicopia.ability.magic.spell; package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.*; import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster; 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.CustomisedSpellType;
import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
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.entity.player.Pony;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.MsgCasterLookRequest;
import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.server.world.Ether;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
import net.minecraft.registry.*;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
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.
@ -33,29 +21,8 @@ import net.minecraft.world.World;
* When cast two copies of this spell are created. One is attached to the player and is the controlling spell, * 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. * the other is attached to a cast spell entity and placed in the world.
* *
* TODO: Split this up into separate classes.
*/ */
public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedSpell { public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedSpell {
/**
* Dimension the spell was originally cast in
*/
@Nullable
private RegistryKey<World> dimension;
/**
* ID of the placed counterpart of this spell.
*/
@Nullable
private UUID placedSpellId;
/**
* The cast spell entity
*/
private final EntityReference<CastSpellEntity> castEntity = new EntityReference<>();
public float pitch;
public float yaw;
private int prevAge; private int prevAge;
private int age; private int age;
@ -63,15 +30,35 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
private int prevDeathTicks; private int prevDeathTicks;
private int deathTicks; private int deathTicks;
private Optional<Vec3d> position = Optional.empty(); private UUID controllingEntityUuid;
private UUID controllingSpellUuid;
public float pitch;
public float yaw;
public PlaceableSpell(CustomisedSpellType<?> type) { public PlaceableSpell(CustomisedSpellType<?> type) {
super(type); super(type);
} }
public PlaceableSpell setSpell(Spell spell) { PlaceableSpell(Caster<?> caster, PlacementControlSpell control, Spell delegate) {
delegate.set(spell); this(SpellType.PLACED_SPELL.withTraits());
return this; this.controllingEntityUuid = caster.asEntity().getUuid();
this.controllingSpellUuid = control.getUuid();
this.delegate.set(delegate);
if (delegate instanceof PlacementDelegate s) {
s.onPlaced(caster, control);
}
}
@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) { public float getAge(float tickDelta) {
@ -86,7 +73,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
@Override @Override
public boolean isDying() { public boolean isDying() {
return dead && deathTicks > 0; return dead && deathTicks > 0 || super.isDying();
} }
@Override @Override
@ -94,6 +81,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
super.setDead(); super.setDead();
dead = true; dead = true;
deathTicks = 20; deathTicks = 20;
setDirty();
} }
@Override @Override
@ -103,32 +91,10 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
@Override @Override
public boolean tick(Caster<?> source, Situation situation) { public boolean tick(Caster<?> source, Situation situation) {
if (situation == Situation.BODY) { if (!source.isClient() && !checkConnection(source)) {
if (!source.isClient()) {
if (dimension == null) {
dimension = source.asWorld().getRegistryKey();
if (source instanceof Pony) {
Channel.SERVER_REQUEST_PLAYER_LOOK.sendToPlayer(new MsgCasterLookRequest(getUuid()), (ServerPlayerEntity)source.asEntity());
}
setDirty();
}
castEntity.getTarget().ifPresentOrElse(
target -> checkDetachment(source, target),
() -> spawnPlacedEntity(source)
);
}
return !isDead();
}
if (situation == Situation.GROUND_ENTITY) {
if (!source.isClient()) {
if (Ether.get(source.asWorld()).get(this, source) == null) {
setDead(); setDead();
return false; return false;
} }
}
prevAge = age; prevAge = age;
if (age < 25) { if (age < 25) {
@ -138,91 +104,34 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
return super.tick(source, Situation.GROUND); return super.tick(source, Situation.GROUND);
} }
return !isDead(); private boolean checkConnection(Caster<?> source) {
return Ether.get(source.asWorld()).get(SpellType.PLACE_CONTROL_SPELL, controllingEntityUuid, controllingSpellUuid) != null;
} }
@Override @Override
public void tickDying(Caster<?> caster) { public void tickDying(Caster<?> caster) {
super.tickDying(caster);
prevDeathTicks = deathTicks; prevDeathTicks = deathTicks;
deathTicks--; deathTicks--;
} }
private void checkDetachment(Caster<?> source, EntityValues<?> target) {
if (getWorld(source).map(Ether::get).map(ether -> ether.get(getTypeAndTraits().type(), target, placedSpellId)).isEmpty()) {
setDead();
}
}
private void spawnPlacedEntity(Caster<?> source) {
CastSpellEntity entity = UEntities.CAST_SPELL.create(source.asWorld());
Vec3d pos = getPosition().orElse(position.orElse(source.asEntity().getPos()));
entity.updatePositionAndAngles(pos.x, pos.y, pos.z, source.asEntity().getYaw(), source.asEntity().getPitch());
PlaceableSpell copy = delegate.get().toPlaceable();
if (delegate.get() instanceof PlacementDelegate delegate) {
delegate.onPlaced(source, copy, entity);
}
entity.getSpellSlot().put(copy);
entity.setCaster(source);
entity.getWorld().spawnEntity(entity);
placedSpellId = copy.getUuid();
Ether.get(entity.getWorld()).getOrCreate(copy, entity);
castEntity.set(entity);
setDirty();
}
@Override
public void setOrientation(float pitch, float yaw) {
this.pitch = -90 - pitch;
this.yaw = -yaw;
if (delegate.get() instanceof OrientedSpell o) {
o.setOrientation(pitch, yaw);
}
setDirty();
}
public void setPosition(Caster<?> source, Vec3d position) {
this.position = Optional.of(position);
this.dimension = source.asWorld().getRegistryKey();
castEntity.ifPresent(source.asWorld(), entity -> {
entity.updatePositionAndAngles(position.x, position.y, position.z, entity.getYaw(), entity.getPitch());
});
if (delegate.get() instanceof PlaceableSpell o) {
o.setPosition(source, position);
}
setDirty();
}
@Override @Override
protected void onDestroyed(Caster<?> source) { protected void onDestroyed(Caster<?> source) {
if (!source.isClient()) { if (!source.isClient()) {
castEntity.getTarget().ifPresent(target -> {
getWorld(source).map(Ether::get)
.ifPresent(ether -> ether.remove(getTypeAndTraits().type(), target.uuid()));
});
castEntity.set(null);
getSpellEntity(source).ifPresent(e -> {
castEntity.set(null);
});
if (source.asEntity() instanceof CastSpellEntity) {
Ether.get(source.asWorld()).remove(this, source); Ether.get(source.asWorld()).remove(this, source);
} }
}
super.onDestroyed(source); super.onDestroyed(source);
} }
public Optional<CastSpellEntity> getSpellEntity(Caster<?> source) { @Override
return getWorld(source).map(castEntity::get); public void setOrientation(Caster<?> caster, float pitch, float yaw) {
} this.pitch = -pitch - 90;
this.yaw = -yaw;
public Optional<Vec3d> getPosition() { Entity entity = caster.asEntity();
return castEntity.getTarget().map(EntityValues::pos); entity.updatePositionAndAngles(entity.getX(), entity.getY(), entity.getZ(), this.yaw, this.pitch);
} entity.setYaw(this.yaw);
entity.setPitch(this.pitch);
protected Optional<World> getWorld(Caster<?> source) { setDirty();
return Optional.ofNullable(dimension)
.map(dim -> source.asWorld().getServer().getWorld(dim));
} }
@Override @Override
@ -233,17 +142,12 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
compound.putInt("age", age); compound.putInt("age", age);
compound.putFloat("pitch", pitch); compound.putFloat("pitch", pitch);
compound.putFloat("yaw", yaw); compound.putFloat("yaw", yaw);
position.ifPresent(pos -> { if (controllingEntityUuid != null) {
compound.put("position", NbtSerialisable.writeVector(pos)); compound.putUuid("owningEntity", controllingEntityUuid);
});
if (placedSpellId != null) {
compound.putUuid("placedSpellId", placedSpellId);
} }
if (dimension != null) { if (controllingSpellUuid != null) {
compound.putString("dimension", dimension.getValue().toString()); compound.putUuid("owningSpell", controllingSpellUuid);
} }
compound.put("castEntity", castEntity.toNBT());
} }
@Override @Override
@ -252,27 +156,9 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
dead = compound.getBoolean("dead"); dead = compound.getBoolean("dead");
deathTicks = compound.getInt("deathTicks"); deathTicks = compound.getInt("deathTicks");
age = compound.getInt("age"); age = compound.getInt("age");
controllingEntityUuid = compound.containsUuid("owningEntity") ? compound.getUuid("owningEntity") : null;
controllingSpellUuid = compound.containsUuid("owningSpell") ? compound.getUuid("owningSpell") : null;
pitch = compound.getFloat("pitch"); pitch = compound.getFloat("pitch");
yaw = compound.getFloat("yaw"); yaw = compound.getFloat("yaw");
position = compound.contains("position") ? Optional.of(NbtSerialisable.readVector(compound.getList("position", NbtElement.FLOAT_TYPE))) : Optional.empty();
placedSpellId = compound.containsUuid("placedSpellId") ? compound.getUuid("placedSpellId") : null;
if (compound.contains("dimension", NbtElement.STRING_TYPE)) {
Identifier id = Identifier.tryParse(compound.getString("dimension"));
if (id != null) {
dimension = RegistryKey.of(RegistryKeys.WORLD, id);
}
}
if (compound.contains("castEntity")) {
castEntity.fromNBT(compound.getCompound("castEntity"));
}
}
@Override
public PlaceableSpell toPlaceable() {
return this;
}
public interface PlacementDelegate {
void onPlaced(Caster<?> source, PlaceableSpell parent, CastSpellEntity entity);
} }
} }

View file

@ -0,0 +1,165 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.Optional;
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.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;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class PlacementControlSpell extends AbstractDelegatingSpell implements OrientedSpell {
@Nullable
private UUID placedSpellId;
private final EntityReference<CastSpellEntity> castEntity = new EntityReference<>();
private Optional<RegistryKey<World>> dimension = Optional.empty();
private Optional<Vec3d> position = Optional.empty();
private Optional<Vec3d> orientation = Optional.empty();
public PlacementControlSpell(CustomisedSpellType<?> type) {
super(type);
}
PlacementControlSpell(CustomisedSpellType<?> type, Spell delegate) {
super(type);
this.delegate.set(delegate);
}
public Optional<Vec3d> getPosition() {
return position;
}
public void setDimension(RegistryKey<World> dimension) {
this.dimension = Optional.of(dimension);
}
public void setPosition(Vec3d position) {
this.position = Optional.of(position);
}
@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();
}
@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();
}
private void spawnPlacedEntity(Caster<?> source) {
PlaceableSpell copy = new PlaceableSpell(source, this, getDelegate());
Vec3d pos = position.orElse(source.asEntity().getPos());
Vec3d rot = orientation.orElse(Vec3d.ZERO);
CastSpellEntity entity = UEntities.CAST_SPELL.create(source.asWorld());
entity.setCaster(source);
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();
castEntity.set(entity);
setDirty();
}
private boolean checkConnection(Caster<?> source, EntityValues<?> target) {
return getWorld(source)
.map(Ether::get)
.map(ether -> ether.get(SpellType.PLACED_SPELL, target, 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);
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());
}
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
placedSpellId = compound.containsUuid("placedSpellId") ? compound.getUuid("placedSpellId") : 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 {
void onPlaced(Caster<?> source, PlacementControlSpell parent);
}
}

View file

@ -123,8 +123,8 @@ public interface Spell extends NbtSerialisable, Affine {
/** /**
* Converts this spell into a placeable spell. * Converts this spell into a placeable spell.
*/ */
default PlaceableSpell toPlaceable() { default PlacementControlSpell toPlaceable() {
return SpellType.PLACED_SPELL.withTraits().create().setSpell(this); return new PlacementControlSpell(SpellType.PLACE_CONTROL_SPELL.withTraits(), this);
} }
/** /**

View file

@ -13,7 +13,9 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.MsgCasterLookRequest;
import com.minelittlepony.unicopia.particle.*; import com.minelittlepony.unicopia.particle.*;
import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.server.world.Ether;
import com.minelittlepony.unicopia.util.shape.*; import com.minelittlepony.unicopia.util.shape.*;
@ -25,13 +27,14 @@ import net.minecraft.entity.LivingEntity;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.packet.s2c.play.PositionFlag; import net.minecraft.network.packet.s2c.play.PositionFlag;
import net.minecraft.particle.ParticleTypes; import net.minecraft.particle.ParticleTypes;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.WorldEvents; import net.minecraft.world.WorldEvents;
public class PortalSpell extends AbstractSpell implements PlaceableSpell.PlacementDelegate, OrientedSpell { public class PortalSpell extends AbstractSpell implements PlacementControlSpell.PlacementDelegate, OrientedSpell {
public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder() public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder()
.with(Trait.LIFE, 10) .with(Trait.LIFE, 10)
.with(Trait.KNOWLEDGE, 1) .with(Trait.KNOWLEDGE, 1)
@ -91,7 +94,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
@Override @Override
public boolean apply(Caster<?> caster) { public boolean apply(Caster<?> caster) {
setOrientation(caster.asEntity().getPitch(), caster.asEntity().getYaw()); setOrientation(caster, caster.asEntity().getPitch(), caster.asEntity().getYaw());
return toPlaceable().apply(caster); return toPlaceable().apply(caster);
} }
@ -195,22 +198,25 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
} }
@Override @Override
public void setOrientation(float pitch, float yaw) { public void setOrientation(Caster<?> caster, float pitch, float yaw) {
this.pitch = pitch; this.pitch = pitch;
this.yaw = yaw; this.yaw = yaw;
particleArea = PARTICLE_AREA.rotate( particleArea = PARTICLE_AREA.rotate(
pitch * MathHelper.RADIANS_PER_DEGREE, pitch * MathHelper.RADIANS_PER_DEGREE,
(180 - yaw) * MathHelper.RADIANS_PER_DEGREE yaw * MathHelper.RADIANS_PER_DEGREE
); );
setDirty(); setDirty();
} }
@Override @Override
public void onPlaced(Caster<?> source, PlaceableSpell parent, CastSpellEntity entity) { public void onPlaced(Caster<?> source, PlacementControlSpell parent) {
parent.setOrientation(source, source.asEntity().getPitch(), source.asEntity().getYaw());
LivingEntity caster = source.getMaster(); LivingEntity caster = source.getMaster();
Vec3d targetPos = caster.getRotationVector().multiply(3).add(caster.getEyePos()); Vec3d targetPos = caster.getRotationVector().multiply(3).add(caster.getEyePos());
parent.setOrientation(pitch, yaw); parent.setPosition(new Vec3d(targetPos.x, caster.getPos().y, targetPos.z));
entity.setPos(targetPos.x, Math.abs(pitch) > 15 ? targetPos.y : caster.getPos().y, targetPos.z); if (source instanceof Pony pony) {
Channel.SERVER_REQUEST_PLAYER_LOOK.sendToPlayer(new MsgCasterLookRequest(parent.getUuid()), (ServerPlayerEntity)pony.asEntity());
}
} }
@Override @Override

View file

@ -12,6 +12,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.ChangelingFeedingSpell;
import com.minelittlepony.unicopia.ability.magic.spell.DispersableDisguiseSpell; import com.minelittlepony.unicopia.ability.magic.spell.DispersableDisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.RainboomAbilitySpell; import com.minelittlepony.unicopia.ability.magic.spell.RainboomAbilitySpell;
import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell; 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.RageAbilitySpell;
import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.ThrowableSpell; import com.minelittlepony.unicopia.ability.magic.spell.ThrowableSpell;
@ -43,6 +44,7 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
private static final DynamicCommandExceptionType UNKNOWN_SPELL_TYPE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("spell_type.unknown", id)); private static final DynamicCommandExceptionType UNKNOWN_SPELL_TYPE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("spell_type.unknown", id));
public static final SpellType<PlacementControlSpell> PLACE_CONTROL_SPELL = register("place_controller", SpellType.<PlacementControlSpell>builder(PlacementControlSpell::new).affinity(Affinity.NEUTRAL).unobtainable().stackable().shape(GemstoneItem.Shape.DONUT));
public static final SpellType<PlaceableSpell> PLACED_SPELL = register("placed", builder(PlaceableSpell::new).affinity(Affinity.NEUTRAL).unobtainable().stackable().shape(GemstoneItem.Shape.DONUT)); public static final SpellType<PlaceableSpell> PLACED_SPELL = register("placed", builder(PlaceableSpell::new).affinity(Affinity.NEUTRAL).unobtainable().stackable().shape(GemstoneItem.Shape.DONUT));
public static final SpellType<ThrowableSpell> THROWN_SPELL = register("thrown", builder(ThrowableSpell::new).affinity(Affinity.NEUTRAL).unobtainable().shape(GemstoneItem.Shape.DONUT)); public static final SpellType<ThrowableSpell> THROWN_SPELL = register("thrown", builder(ThrowableSpell::new).affinity(Affinity.NEUTRAL).unobtainable().shape(GemstoneItem.Shape.DONUT));

View file

@ -40,11 +40,11 @@ public class DismissSpellScreen extends GameGui {
double azimuth = 0; double azimuth = 0;
double ring = 2; double ring = 2;
List<PlaceableSpell> placeableSpells = new ArrayList<>(); List<PlacementControlSpell> placeableSpells = new ArrayList<>();
for (Spell spell : pony.getSpellSlot().stream().filter(SpellPredicate.IS_VISIBLE).toList()) { for (Spell spell : pony.getSpellSlot().stream().filter(SpellPredicate.IS_VISIBLE).toList()) {
if (spell instanceof PlaceableSpell placeable) { if (spell instanceof PlacementControlSpell placeable) {
if (placeable.getPosition().isPresent()) { if (placeable.getPosition().isPresent()) {
placeableSpells.add(placeable); placeableSpells.add(placeable);
continue; continue;

View file

@ -6,7 +6,6 @@ import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell; import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.client.render.model.PlaneModel; import com.minelittlepony.unicopia.client.render.model.PlaneModel;
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
@ -27,12 +26,7 @@ public class PlacedSpellRenderer extends SpellRenderer<PlaceableSpell> {
@Override @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) { 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) {
if (!(caster.asEntity() instanceof CastSpellEntity castSpell)) {
return;
}
matrices.push(); matrices.push();
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-castSpell.getYaw()));
Spell delegate = spell.getDelegate(); Spell delegate = spell.getDelegate();
@ -42,8 +36,8 @@ public class PlacedSpellRenderer extends SpellRenderer<PlaceableSpell> {
matrices.push(); matrices.push();
float height = caster.asEntity().getHeight(); float height = caster.asEntity().getHeight();
matrices.translate(0, (-spell.pitch / 90F) * height * 0.5F, 0); 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(-spell.pitch));
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180 - spell.yaw));
SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertices, delegate, caster, light, spell.getScale(tickDelta), limbDistance, tickDelta, animationProgress, headYaw, headPitch); SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertices, delegate, caster, light, spell.getScale(tickDelta), limbDistance, tickDelta, animationProgress, headYaw, headPitch);
matrices.pop(); matrices.pop();
} }
@ -58,8 +52,8 @@ public class PlacedSpellRenderer extends SpellRenderer<PlaceableSpell> {
float height = caster.asEntity().getHeight(); float height = caster.asEntity().getHeight();
matrices.translate(0, (-spell.pitch / 90F) * height * 0.5F, 0); 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(-spell.pitch));
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180 - spell.yaw));
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90)); matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90));
float scale = spell.getScale(tickDelta) * 3; float scale = spell.getScale(tickDelta) * 3;

View file

@ -4,7 +4,7 @@ import java.util.Optional;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod; import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell; import com.minelittlepony.unicopia.ability.magic.spell.PlacementControlSpell;
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
@ -98,11 +98,11 @@ public class CastCommand {
private static int placed(CommandContext<ServerCommandSource> source, TraitsFunc traits, Optional<Vec3d> position, Vec2f rotation) throws CommandSyntaxException { private static int placed(CommandContext<ServerCommandSource> source, TraitsFunc traits, Optional<Vec3d> position, Vec2f rotation) throws CommandSyntaxException {
ServerPlayerEntity player = source.getSource().getPlayerOrThrow(); ServerPlayerEntity player = source.getSource().getPlayerOrThrow();
PlaceableSpell spell = getSpell(source, traits).create().toPlaceable(); PlacementControlSpell spell = getSpell(source, traits).create().toPlaceable();
Caster<?> caster = Caster.of(player).orElseThrow(); Caster<?> caster = Caster.of(player).orElseThrow();
spell.setOrientation(rotation.x, rotation.y); spell.setOrientation(caster, rotation.x, rotation.y);
position.ifPresent(pos -> spell.setPosition(caster, pos)); position.ifPresent(spell::setPosition);
spell.apply(caster); spell.apply(caster);
return 0; return 0;

View file

@ -81,14 +81,12 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster<CastS
public void tick() { public void tick() {
super.tick(); super.tick();
if (isRemoved()) { if (!isRemoved() && !spells.tick(Situation.GROUND_ENTITY)) {
return; if (!isClient()) {
}
if (!spells.tick(Situation.GROUND_ENTITY)) {
discard(); discard();
} }
} }
}
@Override @Override
public EntityDimensions getDimensions(EntityPose pose) { public EntityDimensions getDimensions(EntityPose pose) {

View file

@ -49,11 +49,10 @@ public record MsgCasterLookRequest (UUID spellId) implements Packet<PlayerEntity
@Override @Override
public void handle(ServerPlayerEntity sender) { public void handle(ServerPlayerEntity sender) {
Pony.of(sender).getSpellSlot() Pony pony = Pony.of(sender);
pony.getSpellSlot()
.get(SpellPredicate.IS_ORIENTED.withId(spellId)) .get(SpellPredicate.IS_ORIENTED.withId(spellId))
.ifPresent(spell -> { .ifPresent(spell -> spell.setOrientation(pony, rotation));
spell.setOrientation(rotation);
});
} }
} }
} }

View file

@ -113,7 +113,7 @@ public class Ether extends PersistentState {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Nullable @Nullable
private <T extends Spell> Entry<T> get(SpellType<T> spell, UUID entityId, @Nullable UUID spellId) { public <T extends Spell> Entry<T> get(SpellType<T> spell, UUID entityId, @Nullable UUID spellId) {
if (spellId == null) { if (spellId == null) {
return null; return null;
} }

View file

@ -46,7 +46,6 @@ public interface NbtSerialisable {
} }
static Vec3d readVector(NbtList list) { static Vec3d readVector(NbtList list) {
return new Vec3d(list.getDouble(0), list.getDouble(1), list.getDouble(2)); return new Vec3d(list.getDouble(0), list.getDouble(1), list.getDouble(2));
} }