mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-23 13:37:58 +01:00
Move placed spell logic into the cast spell entity and fix portals mislinking/multilinking
This commit is contained in:
parent
8a093a8a8b
commit
1ecb28e511
19 changed files with 326 additions and 400 deletions
|
@ -12,14 +12,12 @@ import net.minecraft.entity.Entity;
|
|||
public interface SpellPredicate<T extends Spell> extends Predicate<Spell> {
|
||||
SpellPredicate<?> ALL = spell -> true;
|
||||
SpellPredicate<IllusionarySpell> CAN_SUPPRESS = s -> s instanceof IllusionarySpell;
|
||||
SpellPredicate<PlaceableSpell> IS_PLACED = s -> s instanceof PlaceableSpell;
|
||||
SpellPredicate<AbstractDisguiseSpell> IS_DISGUISE = s -> s instanceof AbstractDisguiseSpell;
|
||||
SpellPredicate<MimicSpell> IS_MIMIC = s -> s instanceof MimicSpell;
|
||||
SpellPredicate<ShieldSpell> IS_SHIELD_LIKE = spell -> spell instanceof ShieldSpell;
|
||||
SpellPredicate<TimedSpell> IS_TIMED = spell -> spell instanceof TimedSpell;
|
||||
SpellPredicate<OrientedSpell> 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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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");
|
||||
}
|
||||
}
|
|
@ -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<Vec3d> position = Optional.empty();
|
||||
private Optional<Vec3d> orientation = Optional.empty();
|
||||
|
||||
private SpellReference<Spell> 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<Vec3d> 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<World> 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();
|
||||
|
|
|
@ -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 <T extends Spell> Spell copy(T spell) {
|
||||
return readNbt(writeNbt(spell));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<EntityReference.EntityValues<Entity>> getTarget() {
|
||||
return teleportationTarget.getTarget();
|
||||
public EntityReference<Entity> getDestinationReference() {
|
||||
return teleportationTarget;
|
||||
}
|
||||
|
||||
public float getPitch() {
|
||||
|
@ -88,13 +81,16 @@ public class PortalSpell extends AbstractSpell implements PlacementControlSpell.
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Optional<Ether.Entry<PortalSpell>> getDestination(Caster<?> source) {
|
||||
return getTarget().map(target -> Ether.get(source.asWorld()).get((SpellType<PortalSpell>)getType(), target, targetPortalId));
|
||||
private Ether.Entry<PortalSpell> getDestination(Caster<?> source) {
|
||||
return targetPortalId == null ? null : getDestinationReference()
|
||||
.getTarget()
|
||||
.map(target -> Ether.get(source.asWorld()).get((SpellType<PortalSpell>)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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<T extends Spell> implements Affine, SpellPredicate<
|
|||
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<ThrowableSpell> THROWN_SPELL = register("thrown", builder(ThrowableSpell::new).affinity(Affinity.NEUTRAL).unobtainable().shape(GemstoneItem.Shape.DONUT));
|
||||
|
||||
public static final SpellType<DispersableDisguiseSpell> CHANGELING_DISGUISE = register("disguise", builder(DispersableDisguiseSpell::new).affinity(Affinity.BAD).color(0x19E48E).unobtainable().shape(GemstoneItem.Shape.ARROW));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<E extends LivingEntity> 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);
|
||||
|
|
|
@ -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<CastSpellEntity> {
|
||||
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<CastSpellEntity> {
|
|||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<PlaceableSpell> {
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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<PortalSpell> {
|
||||
|
@ -29,7 +30,9 @@ public class PortalSpellRenderer extends SpellRenderer<PortalSpell> {
|
|||
SphereModel.DISK.render(matrices, buff, light, 0, 2F * strength, 1, 1, 1, 1);
|
||||
matrices.pop();
|
||||
|
||||
if (Unicopia.getConfig().simplifiedPortals.get() || !spell.isLinked()) {
|
||||
EntityReference<Entity> 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<PortalSpell> {
|
|||
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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<CastSpellEntity>, WeaklyOwned.Mutable<LivingEntity>, MagicImmune {
|
||||
|
@ -32,6 +38,8 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster<CastS
|
|||
private static final TrackedData<Integer> CORRUPTION = DataTracker.registerData(CastSpellEntity.class, TrackedDataHandlerRegistry.INTEGER);
|
||||
private static final TrackedData<Integer> MAX_CORRUPTION = DataTracker.registerData(CastSpellEntity.class, TrackedDataHandlerRegistry.INTEGER);
|
||||
|
||||
private static final TrackedData<Boolean> DEAD = DataTracker.registerData(CastSpellEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
|
||||
|
||||
private final EntityPhysics<CastSpellEntity> physics = new EntityPhysics<>(this);
|
||||
|
||||
private final SpellInventory spells = SpellSlots.ofSingle(this);
|
||||
|
@ -49,6 +57,23 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster<CastS
|
|||
() -> 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<CastS
|
|||
dataTracker.startTracking(CORRUPTION, 0);
|
||||
dataTracker.startTracking(MAX_LEVEL, 1);
|
||||
dataTracker.startTracking(MAX_CORRUPTION, 1);
|
||||
dataTracker.startTracking(DEAD, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePositionAndAngles(double x, double y, double z, float yaw, float pitch) {
|
||||
super.updatePositionAndAngles(x, y, z, yaw, pitch);
|
||||
spells.getSlots().stream(SpellPredicate.IS_ORIENTED).forEach(spell -> 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<CastS
|
|||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
public void baseTick() {
|
||||
prevAge = age;
|
||||
age++;
|
||||
|
||||
if (!isRemoved() && !spells.tick(Situation.GROUND_ENTITY)) {
|
||||
if (!isClient()) {
|
||||
discard();
|
||||
super.baseTick();
|
||||
|
||||
if (!isClient()) {
|
||||
if (!checkConnection()) {
|
||||
kill();
|
||||
}
|
||||
|
||||
spells.getSlots().get().ifPresent(spell -> {
|
||||
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<CastS
|
|||
|
||||
@Override
|
||||
protected void writeCustomDataToNbt(NbtCompound tag) {
|
||||
tag.put("owner", owner.toNBT());
|
||||
tag.put("level", level.toNbt());
|
||||
tag.put("corruption", corruption.toNbt());
|
||||
|
||||
if (controllingEntityUuid != null) {
|
||||
tag.putUuid("owningEntity", controllingEntityUuid);
|
||||
}
|
||||
if (controllingSpellUuid != null) {
|
||||
tag.putUuid("owningSpell", controllingSpellUuid);
|
||||
}
|
||||
|
||||
spells.getSlots().toNBT(tag);
|
||||
tag.putInt("age", age);
|
||||
tag.putInt("prevAge", prevAge);
|
||||
tag.putBoolean("dead", isDead());
|
||||
tag.put("owner", owner.toNBT());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readCustomDataFromNbt(NbtCompound tag) {
|
||||
if (tag.contains("owner")) {
|
||||
owner.fromNBT(tag.getCompound("owner"));
|
||||
}
|
||||
spells.getSlots().fromNBT(tag);
|
||||
var level = Levelled.fromNbt(tag.getCompound("level"));
|
||||
dataTracker.set(MAX_LEVEL, level.getMax());
|
||||
dataTracker.set(LEVEL, level.get());
|
||||
var corruption = Levelled.fromNbt(tag.getCompound("corruption"));
|
||||
dataTracker.set(MAX_CORRUPTION, corruption.getMax());
|
||||
dataTracker.set(CORRUPTION, corruption.get());
|
||||
|
||||
controllingEntityUuid = tag.containsUuid("owningEntity") ? tag.getUuid("owningEntity") : null;
|
||||
controllingSpellUuid = tag.containsUuid("owningSpell") ? tag.getUuid("owningSpell") : null;
|
||||
|
||||
spells.getSlots().fromNBT(tag);
|
||||
age = tag.getInt("age");
|
||||
prevAge = tag.getInt("prevAge");
|
||||
setDead(tag.getBoolean("dead"));
|
||||
|
||||
if (tag.contains("owner")) {
|
||||
owner.fromNBT(tag.getCompound("owner"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ public interface UEntities {
|
|||
.trackRangeBlocks(200)
|
||||
.disableSummon()
|
||||
.dimensions(EntityDimensions.fixed(1, 1)));
|
||||
EntityType<CastSpellEntity> CAST_SPELL = register("cast_spell", FabricEntityTypeBuilder.create(SpawnGroup.MISC, CastSpellEntity::new)
|
||||
EntityType<CastSpellEntity> CAST_SPELL = register("cast_spell", FabricEntityTypeBuilder.<CastSpellEntity>create(SpawnGroup.MISC, CastSpellEntity::new)
|
||||
.trackRangeBlocks(200)
|
||||
.disableSummon()
|
||||
.dimensions(EntityDimensions.changing(4, 4)));
|
||||
|
|
|
@ -892,7 +892,7 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, 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()
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -168,13 +168,14 @@ public class Ether extends PersistentState {
|
|||
private WeakReference<T> 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<UUID> 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
|
||||
|
|
Loading…
Reference in a new issue