Portal refinements and add the displacement spell

This commit is contained in:
Sollace 2022-09-17 19:13:39 +02:00
parent 4fde8400c5
commit af7e19ae69
18 changed files with 329 additions and 93 deletions

View file

@ -117,8 +117,8 @@ public class UnicornCastingAbility implements Ability<Hit> {
player.playSound(USounds.SPELL_CAST_FAIL, 1, 0.5F); player.playSound(USounds.SPELL_CAST_FAIL, 1, 0.5F);
} else { } else {
player.setAnimation(Animation.ARMS_UP); player.setAnimation(Animation.ARMS_UP);
if (s instanceof HomingSpell) { if (s instanceof HomingSpell homer) {
RayTraceHelper.doTrace(player.getMaster(), 600, 1, EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.and(EntityPredicates.VALID_ENTITY)).getEntity().ifPresent(((HomingSpell)s)::setTarget); RayTraceHelper.doTrace(player.getMaster(), homer.getRange(player), 1, EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.and(EntityPredicates.VALID_ENTITY)).getEntity().ifPresent(homer::setTarget);
} }
player.playSound(USounds.SPELL_CAST_SUCCESS, 0.05F, 2.2F); player.playSound(USounds.SPELL_CAST_SUCCESS, 0.05F, 2.2F);
} }

View file

@ -76,7 +76,7 @@ public class UnicornDispellAbility implements Ability<Pos> {
@Override @Override
public void apply(Pony player, Pos data) { public void apply(Pony player, Pos data) {
player.setAnimation(Animation.WOLOLO); player.setAnimation(Animation.WOLOLO);
Caster.stream(VecHelper.findInRange(player.getEntity(), player.getReferenceWorld(), data.vec(), 2, EquinePredicates.IS_PLACED_SPELL).stream()).forEach(target -> { Caster.stream(VecHelper.findInRange(player.getEntity(), player.getReferenceWorld(), data.vec(), 3, EquinePredicates.IS_PLACED_SPELL).stream()).forEach(target -> {
target.getSpellSlot().clear(); target.getSpellSlot().clear();
}); });
} }

View file

@ -3,8 +3,7 @@ package com.minelittlepony.unicopia.ability.data;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import net.minecraft.network.PacketByteBuf; import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.*;
import net.minecraft.util.math.Vec3d;
public class Pos extends Hit { public class Pos extends Hit {
@ -44,7 +43,7 @@ public class Pos extends Hit {
} }
public Vec3d vec() { public Vec3d vec() {
return new Vec3d(x, y, z); return Vec3d.ofCenter(new Vec3i(x, y, z));
} }
public double distanceTo(Caster<?> caster) { public double distanceTo(Caster<?> caster) {

View file

@ -1,10 +1,18 @@
package com.minelittlepony.unicopia.ability.magic.spell; package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.ability.magic.Caster;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
/** /**
* A spell that's capable of homing in on a pre-defined target. * A spell that's capable of homing in on a pre-defined target.
*/ */
public interface HomingSpell extends Spell { public interface HomingSpell extends Spell {
int DEFAULT_RANGE = 600;
boolean setTarget(Entity target); boolean setTarget(Entity target);
default int getRange(Caster<?> caster) {
return DEFAULT_RANGE;
}
} }

View file

@ -50,6 +50,9 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
*/ */
private Spell spell; private Spell spell;
public float pitch;
public float yaw;
public PlaceableSpell(CustomisedSpellType<?> type) { public PlaceableSpell(CustomisedSpellType<?> type) {
super(type); super(type);
} }
@ -84,7 +87,11 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
CastSpellEntity entity = UEntities.CAST_SPELL.create(source.getReferenceWorld()); CastSpellEntity entity = UEntities.CAST_SPELL.create(source.getReferenceWorld());
Vec3d pos = castEntity.getPosition().orElse(source.getOriginVector()); Vec3d pos = castEntity.getPosition().orElse(source.getOriginVector());
entity.updatePositionAndAngles(pos.x, pos.y, pos.z, 0, 0); entity.updatePositionAndAngles(pos.x, pos.y, pos.z, 0, 0);
entity.getSpellSlot().put(spell.toPlaceable()); PlaceableSpell copy = spell.toPlaceable();
if (spell instanceof PlacementDelegate delegate) {
delegate.onPlaced(source, copy, entity);
}
entity.getSpellSlot().put(copy);
entity.setMaster(source); entity.setMaster(source);
entity.world.spawnEntity(entity); entity.world.spawnEntity(entity);
Ether.get(entity.world).put(getType(), entity); Ether.get(entity.world).put(getType(), entity);
@ -113,9 +120,11 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
} }
} }
particlEffect.update(getUuid(), source, spawner -> { if (spell instanceof PlacementDelegate delegate) {
spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.MAGIC_RUNES, 90, 0), source.getOriginVector(), Vec3d.ZERO); delegate.updatePlacement(source, this);
}).ifPresent(p -> { }
getParticleEffectAttachment(source).ifPresent(p -> {
p.setAttribute(Attachment.ATTR_COLOR, spell.getType().getColor()); p.setAttribute(Attachment.ATTR_COLOR, spell.getType().getColor());
}); });
@ -145,6 +154,13 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
return getWorld(source).map(castEntity::get); return getWorld(source).map(castEntity::get);
} }
public Optional<Attachment> getParticleEffectAttachment(Caster<?> source) {
return particlEffect.update(getUuid(), source, spawner -> {
source.getOriginVector().add(0, 5, 0);
spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.MAGIC_RUNES, pitch + 90, yaw), Vec3d.ZERO, Vec3d.ZERO);
});
}
protected Optional<World> getWorld(Caster<?> source) { protected Optional<World> getWorld(Caster<?> source) {
return Optional.ofNullable(dimension) return Optional.ofNullable(dimension)
.map(dim -> source.getReferenceWorld().getServer().getWorld(dim)); .map(dim -> source.getReferenceWorld().getServer().getWorld(dim));
@ -153,6 +169,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
@Override @Override
public void toNBT(NbtCompound compound) { public void toNBT(NbtCompound compound) {
super.toNBT(compound); super.toNBT(compound);
compound.putFloat("pitch", pitch);
compound.putFloat("yaw", yaw);
if (dimension != null) { if (dimension != null) {
compound.putString("dimension", dimension.getValue().toString()); compound.putString("dimension", dimension.getValue().toString());
} }
@ -163,6 +181,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
@Override @Override
public void fromNBT(NbtCompound compound) { public void fromNBT(NbtCompound compound) {
super.fromNBT(compound); super.fromNBT(compound);
pitch = compound.getFloat("pitch");
yaw = compound.getFloat("yaw");
if (compound.contains("dimension", NbtElement.STRING_TYPE)) { if (compound.contains("dimension", NbtElement.STRING_TYPE)) {
Identifier id = Identifier.tryParse(compound.getString("dimension")); Identifier id = Identifier.tryParse(compound.getString("dimension"));
if (id != null) { if (id != null) {
@ -182,10 +202,18 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
@Override @Override
protected void saveDelegates(NbtCompound compound) { protected void saveDelegates(NbtCompound compound) {
compound.put("spell", Spell.SERIALIZER.write(spell)); compound.put("spell", Spell.SERIALIZER.write(spell));
} }
@Override @Override
public PlaceableSpell toPlaceable() { public PlaceableSpell toPlaceable() {
return this; return this;
} }
public interface PlacementDelegate {
void onPlaced(Caster<?> source, PlaceableSpell parent, CastSpellEntity entity);
void updatePlacement(Caster<?> source, PlaceableSpell parent);
}
} }

View file

@ -0,0 +1,112 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.*;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.CastSpellEntity;
import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.util.MagicalDamageSource;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.math.Vec3d;
public class DisplacementSpell extends AbstractSpell implements HomingSpell, PlaceableSpell.PlacementDelegate {
private final EntityReference<Entity> target = new EntityReference<>();
private int ticks = 10;
protected DisplacementSpell(CustomisedSpellType<?> type) {
super(type);
}
@Override
public boolean apply(Caster<?> caster) {
return toPlaceable().apply(caster);
}
@Override
public boolean tick(Caster<?> source, Situation situation) {
source.getMaster().setGlowing(true);
ticks--;
if (source.isClient()) {
return !isDead() || ticks >= -10;
}
if (ticks == 0) {
target.ifPresent(source.getReferenceWorld(), target -> {
Vec3d destinationPos = target.getPos();
Vec3d destinationVel = target.getVelocity();
Vec3d sourcePos = source.getMaster().getPos();
Vec3d sourceVel = source.getMaster().getVelocity();
teleport(target, sourcePos, sourceVel);
teleport(source.getMaster(), destinationPos, destinationVel);
source.subtractEnergyCost(destinationPos.distanceTo(sourcePos) / 20F);
});
}
return ticks >= -10;
}
@Override
public void onPlaced(Caster<?> source, PlaceableSpell parent, CastSpellEntity entity) {
}
@Override
public void updatePlacement(Caster<?> caster, PlaceableSpell parent) {
parent.getParticleEffectAttachment(caster).ifPresent(attachment -> {
float r = 3 - (1 - ((ticks + 10) / 20F)) * 3;
attachment.setAttribute(Attachment.ATTR_RADIUS, r);
});
}
private void teleport(Entity entity, Vec3d pos, Vec3d vel) {
entity.teleport(pos.x, pos.y, pos.z);
entity.setVelocity(vel);
entity.setGlowing(false);
entity.playSound(SoundEvents.ENTITY_HUSK_CONVERTED_TO_ZOMBIE, 1, 1);
float damage = getTraits().get(Trait.BLOOD);
if (damage > 0) {
entity.damage(MagicalDamageSource.EXHAUSTION, damage);
}
}
@Override
public boolean setTarget(Entity target) {
this.target.set(target);
return false;
}
@Override
public int getRange(Caster<?> caster) {
return 200 + Math.min(2000, 50 * (1 + caster.getLevel().get()));
}
@Override
public void onDestroyed(Caster<?> caster) {
caster.getMaster().setGlowing(false);
target.ifPresent(caster.getReferenceWorld(), e -> e.setGlowing(false));
}
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
compound.putInt("ticks", ticks);
}
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
ticks = compound.getInt("ticks");
}
}

View file

@ -1,32 +1,46 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect; package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.Optional;
import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Situation; import com.minelittlepony.unicopia.ability.magic.spell.*;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.block.data.Ether; import com.minelittlepony.unicopia.block.data.Ether;
import com.minelittlepony.unicopia.entity.CastSpellEntity; import com.minelittlepony.unicopia.entity.CastSpellEntity;
import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.particle.*; import com.minelittlepony.unicopia.particle.*;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.util.shape.Sphere; import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleEffect; import net.minecraft.particle.ParticleEffect;
import net.minecraft.particle.ParticleTypes; import net.minecraft.particle.ParticleTypes;
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 { public class PortalSpell extends AbstractSpell implements PlaceableSpell.PlacementDelegate {
public static final int MAX_COOLDOWN = 20; public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder()
private final EntityReference<CastSpellEntity> teleportationTarget = new EntityReference<>(); .with(Trait.LIFE, 10)
.with(Trait.KNOWLEDGE, 1)
.with(Trait.ORDER, 25)
.build();
private final EntityReference<Entity> teleportationTarget = new EntityReference<>();
private boolean publishedPosition; private boolean publishedPosition;
private final ParticleHandle particlEffect = new ParticleHandle(); private final ParticleHandle particleEffect = new ParticleHandle();
private int cooldown = MAX_COOLDOWN; private float pitch;
private float yaw;
protected PortalSpell(CustomisedSpellType<?> type) { protected PortalSpell(CustomisedSpellType<?> type) {
super(type); super(type);
@ -34,36 +48,17 @@ public class PortalSpell extends AbstractSpell {
@Override @Override
public boolean apply(Caster<?> caster) { public boolean apply(Caster<?> caster) {
pitch = caster.getEntity().getPitch(1);
yaw = caster.getEntity().getYaw(1);
return toPlaceable().apply(caster); return toPlaceable().apply(caster);
} }
@Override @Override
public boolean tick(Caster<?> source, Situation situation) { public boolean tick(Caster<?> source, Situation situation) {
if (situation == Situation.GROUND) { if (situation == Situation.GROUND) {
if (!source.isClient()) {
teleportationTarget.getId().ifPresent(id -> {
Ether ether = Ether.get(source.getReferenceWorld());
if (ether.getEntry(getType(), id).isEmpty()) {
Unicopia.LOGGER.debug("Lost sibling, breaking connection to " + id);
teleportationTarget.set(null);
setDirty();
source.getReferenceWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, source.getOrigin(), Block.getRawIdFromState(Blocks.GLASS.getDefaultState()));
}
});
}
teleportationTarget.getPosition().ifPresentOrElse( if (source.isClient()) {
targetPos -> tickWithTargetLink(source, targetPos),
() -> findLink(source)
);
if (!publishedPosition) {
publishedPosition = true;
Ether.get(source.getReferenceWorld()).put(getType(), source);
}
if (source.isClient() && cooldown <= 0) {
Vec3d origin = source.getOriginVector(); Vec3d origin = source.getOriginVector();
ParticleEffect effect = teleportationTarget.getPosition() ParticleEffect effect = teleportationTarget.getPosition()
@ -71,51 +66,78 @@ public class PortalSpell extends AbstractSpell {
getType(); getType();
return new FollowingParticleEffect(UParticles.HEALTH_DRAIN, target, 0.2F).withChild(ParticleTypes.ELECTRIC_SPARK); return new FollowingParticleEffect(UParticles.HEALTH_DRAIN, target, 0.2F).withChild(ParticleTypes.ELECTRIC_SPARK);
}) })
.orElseGet(() -> { .orElse(ParticleTypes.ELECTRIC_SPARK);
new MagicParticleEffect(getType().getColor());
return ParticleTypes.ELECTRIC_SPARK;
});
source.spawnParticles(origin, new Sphere(true, 2, 1, 0, 1), 3, pos -> { source.spawnParticles(origin, new Sphere(true, 2, 1, 1, 1), 1, pos -> {
source.addParticle(effect, pos, Vec3d.ZERO); source.addParticle(effect, pos, Vec3d.ZERO);
}); });
teleportationTarget.getPosition().ifPresentOrElse(position -> {
particleEffect.update(getUuid(), source, spawner -> {
spawner.addParticle(new SphereParticleEffect(UParticles.DISK, getType().getColor(), 0.8F, 2, new Vec3d(pitch, yaw, 0)), source.getOriginVector(), Vec3d.ZERO);
});
}, () -> {
particleEffect.destroy();
});
} else {
teleportationTarget.getId().ifPresent(id -> {
if (Ether.get(source.getReferenceWorld()).getEntry(getType(), id).isEmpty()) {
Unicopia.LOGGER.debug("Lost sibling, breaking connection to " + id);
teleportationTarget.set(null);
setDirty();
source.getReferenceWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, source.getOrigin(), Block.getRawIdFromState(Blocks.GLASS.getDefaultState()));
}
});
getTarget(source).ifPresentOrElse(
entry -> tickWithTargetLink(source, entry),
() -> findLink(source)
);
} }
if (!publishedPosition) {
publishedPosition = true;
Ether.Entry entry = Ether.get(source.getReferenceWorld()).put(getType(), source);
entry.pitch = pitch;
entry.yaw = yaw;
}
} }
return true; return !isDead();
} }
private void tickWithTargetLink(Caster<?> source, Vec3d targetPos) { private void tickWithTargetLink(Caster<?> source, Ether.Entry destination) {
particlEffect.update(getUuid(), source, spawner -> {
spawner.addParticle(new SphereParticleEffect(UParticles.DISK, getType().getColor(), 0.8F, 2), source.getOriginVector(), Vec3d.ZERO);
});
if (cooldown > 0) { destination.entity.getPosition().ifPresent(targetPos -> {
cooldown--; Vec3d center = source.getOriginVector();
setDirty(); source.findAllEntitiesInRange(1).filter(e -> true).forEach(entity -> {
return; if (!entity.hasPortalCooldown() && entity.timeUntilRegen <= 0) {
} Vec3d offset = entity.getPos().subtract(center);
Vec3d dest = targetPos;
float yawDifference = pitch < 15 ? (180 - yaw + destination.yaw) : 0;
Vec3d center = source.getOriginVector(); dest = dest.add(offset.rotateY(yawDifference * MathHelper.RADIANS_PER_DEGREE)).add(0, 0.05, 0);
source.findAllEntitiesInRange(1).filter(e -> true).forEach(entity -> {
if (!entity.hasPortalCooldown() && entity.timeUntilRegen <= 0) {
Vec3d destination = entity.getPos().subtract(center).add(targetPos);
entity.resetPortalCooldown();
entity.timeUntilRegen = 100;
entity.playSound(USounds.ENTITY_PLAYER_UNICORN_TELEPORT, 1, 1); entity.resetPortalCooldown();
entity.teleport(destination.x, destination.y, destination.z); entity.timeUntilRegen = 20;
entity.playSound(USounds.ENTITY_PLAYER_UNICORN_TELEPORT, 1, 1);
setDirty();
}
ParticleUtils.spawnParticles(new MagicParticleEffect(getType().getColor()), entity, 7); entity.setYaw(entity.getYaw() + yawDifference);
entity.setVelocity(entity.getVelocity().rotateY(yawDifference * MathHelper.RADIANS_PER_DEGREE));
entity.world.playSoundFromEntity(null, entity, USounds.ENTITY_PLAYER_UNICORN_TELEPORT, entity.getSoundCategory(), 1, 1);
entity.teleport(dest.x, dest.y, dest.z);
entity.world.playSoundFromEntity(null, entity, USounds.ENTITY_PLAYER_UNICORN_TELEPORT, entity.getSoundCategory(), 1, 1);
setDirty();
}
ParticleUtils.spawnParticles(new MagicParticleEffect(getType().getColor()), entity, 7);
});
}); });
} }
@SuppressWarnings("unchecked")
private void findLink(Caster<?> source) { private void findLink(Caster<?> source) {
if (source.isClient()) { if (source.isClient()) {
return; return;
} }
@ -123,20 +145,50 @@ public class PortalSpell extends AbstractSpell {
Ether ether = Ether.get(source.getReferenceWorld()); Ether ether = Ether.get(source.getReferenceWorld());
ether.getEntries(getType()) ether.getEntries(getType())
.stream() .stream()
.filter(entry -> entry.isAvailable() && !entry.entity.referenceEquals(source.getEntity())) .filter(entry -> entry.isAvailable() && !entry.entity.referenceEquals(source.getEntity()) && entry.entity.getId().isPresent())
.findAny() .findAny()
.ifPresent(entry -> { .ifPresent(entry -> {
entry.setTaken(true); entry.setTaken(true);
teleportationTarget.copyFrom((EntityReference<CastSpellEntity>)entry.entity); teleportationTarget.copyFrom(entry.entity);
setDirty(); setDirty();
}); });
} }
private Optional<Ether.Entry> getTarget(Caster<?> source) {
return teleportationTarget.getId().flatMap(id -> Ether.get(source.getReferenceWorld()).getEntry(getType(), id));
}
@Override
public void onPlaced(Caster<?> source, PlaceableSpell parent, CastSpellEntity entity) {
LivingEntity caster = source.getMaster();
Vec3d targetPos = caster.getRotationVector().multiply(3).add(caster.getEyePos());
parent.pitch = -90 - pitch;
parent.yaw = -yaw;
for (Spell delegate : parent.getDelegates()) {
if (delegate instanceof PortalSpell copy) {
copy.pitch = pitch;
copy.yaw = yaw;
}
}
entity.setPos(targetPos.x, caster.getY() + 1.5, targetPos.z);
}
@Override
public void updatePlacement(Caster<?> source, PlaceableSpell parent) {
parent.getParticleEffectAttachment(source).ifPresent(attachment -> {
attachment.setAttribute(Attachment.ATTR_RADIUS, 2);
attachment.setAttribute(Attachment.ATTR_OPACITY, 0.92F);
});
}
@Override @Override
public void onDestroyed(Caster<?> caster) { public void onDestroyed(Caster<?> caster) {
Ether ether = Ether.get(caster.getReferenceWorld()); Ether ether = Ether.get(caster.getReferenceWorld());
ether.remove(getType(), caster.getEntity().getUuid()); ether.remove(getType(), caster.getEntity().getUuid());
teleportationTarget.getId().flatMap(id -> ether.getEntry(getType(), id)).ifPresent(e -> e.setTaken(false)); getTarget(caster).ifPresent(e -> e.setTaken(false));
} }
@Override @Override
@ -144,7 +196,8 @@ public class PortalSpell extends AbstractSpell {
super.toNBT(compound); super.toNBT(compound);
compound.putBoolean("publishedPosition", publishedPosition); compound.putBoolean("publishedPosition", publishedPosition);
compound.put("teleportationTarget", teleportationTarget.toNBT()); compound.put("teleportationTarget", teleportationTarget.toNBT());
compound.putInt("cooldown", cooldown); compound.putFloat("pitch", pitch);
compound.putFloat("yaw", yaw);
} }
@Override @Override
@ -152,12 +205,13 @@ public class PortalSpell extends AbstractSpell {
super.fromNBT(compound); super.fromNBT(compound);
publishedPosition = compound.getBoolean("publishedPosition"); publishedPosition = compound.getBoolean("publishedPosition");
teleportationTarget.fromNBT(compound.getCompound("teleportationTarget")); teleportationTarget.fromNBT(compound.getCompound("teleportationTarget"));
cooldown = compound.getInt("cooldown"); pitch = compound.getFloat("pitch");
yaw = compound.getFloat("yaw");
} }
@Override @Override
public void setDead() { public void setDead() {
super.setDead(); super.setDead();
particlEffect.destroy(); particleEffect.destroy();
} }
} }

View file

@ -60,7 +60,8 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
public static final SpellType<CatapultSpell> CATAPULT = register("catapult", Affinity.GOOD, 0x22FF00, true, CatapultSpell.DEFAULT_TRAITS, CatapultSpell::new); public static final SpellType<CatapultSpell> CATAPULT = register("catapult", Affinity.GOOD, 0x22FF00, true, CatapultSpell.DEFAULT_TRAITS, CatapultSpell::new);
public static final SpellType<FireBoltSpell> FIRE_BOLT = register("fire_bolt", Affinity.GOOD, 0xFF8811, true, FireBoltSpell.DEFAULT_TRAITS, FireBoltSpell::new); public static final SpellType<FireBoltSpell> FIRE_BOLT = register("fire_bolt", Affinity.GOOD, 0xFF8811, true, FireBoltSpell.DEFAULT_TRAITS, FireBoltSpell::new);
public static final SpellType<LightSpell> LIGHT = register("light", Affinity.GOOD, 0xEEFFAA, true, LightSpell.DEFAULT_TRAITS, LightSpell::new); public static final SpellType<LightSpell> LIGHT = register("light", Affinity.GOOD, 0xEEFFAA, true, LightSpell.DEFAULT_TRAITS, LightSpell::new);
public static final SpellType<PortalSpell> PORTAL = register("portal", Affinity.GOOD, 0x99FFFF, true, LightSpell.DEFAULT_TRAITS, PortalSpell::new); public static final SpellType<DisplacementSpell> DISPLACEMENT = register("displacement", Affinity.NEUTRAL, 0x9900FF, true, PortalSpell.DEFAULT_TRAITS, DisplacementSpell::new);
public static final SpellType<PortalSpell> PORTAL = register("portal", Affinity.GOOD, 0x99FFFF, true, PortalSpell.DEFAULT_TRAITS, PortalSpell::new);
public static void bootstrap() {} public static void bootstrap() {}

View file

@ -61,11 +61,12 @@ public class Ether extends PersistentState {
} }
} }
public void put(SpellType<?> spellType, Caster<?> caster) { public Entry put(SpellType<?> spellType, Caster<?> caster) {
synchronized (locker) { synchronized (locker) {
getEntries(spellType.getId()).add(new Entry(caster)); getEntries(spellType.getId()).add(new Entry(caster));
markDirty();
return getEntry(spellType, caster).get();
} }
markDirty();
} }
public void remove(SpellType<?> spellType, UUID id) { public void remove(SpellType<?> spellType, UUID id) {
@ -120,6 +121,9 @@ public class Ether extends PersistentState {
private boolean removed; private boolean removed;
private boolean taken; private boolean taken;
public float pitch;
public float yaw;
public Entry() { public Entry() {
entity = new EntityReference<>(); entity = new EntityReference<>();
} }
@ -156,6 +160,8 @@ public class Ether extends PersistentState {
entity.toNBT(compound); entity.toNBT(compound);
compound.putBoolean("removed", removed); compound.putBoolean("removed", removed);
compound.putBoolean("taken", taken); compound.putBoolean("taken", taken);
compound.putFloat("pitch", pitch);
compound.putFloat("yaw", yaw);
} }
@Override @Override
@ -163,6 +169,8 @@ public class Ether extends PersistentState {
entity.fromNBT(compound); entity.fromNBT(compound);
removed = compound.getBoolean("removed"); removed = compound.getBoolean("removed");
taken = compound.getBoolean("taken"); taken = compound.getBoolean("taken");
pitch = compound.getFloat("pitch");
yaw = compound.getFloat("yaw");
} }
@Override @Override

View file

@ -5,15 +5,19 @@ import com.minelittlepony.unicopia.particle.SphereParticleEffect;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.Quaternion; import net.minecraft.util.math.*;
public class DiskParticle extends SphereParticle { public class DiskParticle extends SphereParticle {
private final Quaternion rotation; private final Quaternion rotation = new Quaternion(0, 0, 0, 1);
public DiskParticle(SphereParticleEffect effect, ClientWorld w, double x, double y, double z, double rX, double rY, double rZ) { public DiskParticle(SphereParticleEffect effect, ClientWorld w, double x, double y, double z, double rX, double rY, double rZ) {
super(effect, w, x, y, z, 0, 0, 0); super(effect, w, x, y, z, 0, 0, 0);
rotation = new Quaternion((float)effect.getOffset().x, (float)effect.getOffset().y, (float)effect.getOffset().z, true);
rotation.hamiltonProduct(Vec3f.POSITIVE_Y.getDegreesQuaternion((float)effect.getOffset().y));
rotation.hamiltonProduct(Vec3f.POSITIVE_X.getDegreesQuaternion(90 - (float)effect.getOffset().x));
effect.setOffset(Vec3d.ZERO);
} }
@Override @Override

View file

@ -20,8 +20,8 @@ public abstract class OrientedBillboardParticle extends AbstractBillboardParticl
fixed = effect.isAngleFixed(); fixed = effect.isAngleFixed();
if (fixed) { if (fixed) {
rotation.hamiltonProduct(Vec3f.POSITIVE_X.getDegreesQuaternion(180 - effect.getYaw()));
rotation.hamiltonProduct(Vec3f.POSITIVE_Y.getDegreesQuaternion(effect.getPitch())); rotation.hamiltonProduct(Vec3f.POSITIVE_Y.getDegreesQuaternion(effect.getPitch()));
rotation.hamiltonProduct(Vec3f.POSITIVE_X.getDegreesQuaternion(180 - effect.getYaw()));
} }
} }

View file

@ -53,7 +53,7 @@ public class RainbowTrailParticle extends AbstractBillboardParticle implements A
} }
@Override @Override
public void setAttribute(int key, Object value) { public void setAttribute(int key, Number value) {
} }

View file

@ -30,6 +30,8 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
Unicopia.id("textures/particles/runes_5.png") Unicopia.id("textures/particles/runes_5.png")
}; };
protected float targetSize = 3;
protected float prevBaseSize = 0; protected float prevBaseSize = 0;
protected float baseSize = 0; protected float baseSize = 0;
@ -61,22 +63,31 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
velocityY = 0; velocityY = 0;
velocityZ = 0; velocityZ = 0;
Vec3d pos = link.get().map(Caster::getOriginVector).orElse(Vec3d.ZERO); Vec3d pos = link.get().map(Caster::getOriginVector).orElse(Vec3d.ZERO);
setPos(pos.x, pos.y, pos.z); setPos(pos.x, pos.y + 0.25, pos.z);
} }
@Override @Override
public void detach() { public void detach() {
link = Optional.empty(); link = Optional.empty();
if (targetSize > 1) {
this.targetSize = 0;
}
} }
@Override @Override
public void setAttribute(int key, Object value) { public void setAttribute(int key, Number value) {
if (key == ATTR_COLOR) { if (key == ATTR_COLOR) {
int tint = (int)value; int tint = value.intValue();
red = Color.r(tint); red = Color.r(tint);
green = Color.g(tint); green = Color.g(tint);
blue = Color.b(tint); blue = Color.b(tint);
} }
if (key == ATTR_OPACITY) {
alpha = value.floatValue();
}
if (key == ATTR_RADIUS) {
targetSize = value.floatValue();
}
} }
@Override @Override
public float getScale(float tickDelta) { public float getScale(float tickDelta) {
@ -137,6 +148,8 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
renderQuad(te, buffer, corners, alpha, tickDelta); renderQuad(te, buffer, corners, alpha, tickDelta);
} }
} }
RenderSystem.setShaderColor(1, 1, 1, 1);
} }
@Override @Override
@ -153,8 +166,11 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
}, this::detach); }, this::detach);
prevBaseSize = baseSize; prevBaseSize = baseSize;
if (baseSize < 3) { if (baseSize < targetSize) {
baseSize++; baseSize += 0.1F;
}
if (baseSize > targetSize) {
baseSize -= 0.1F;
} }
rotationAngle = (rotationAngle + 0.3F) % 360; rotationAngle = (rotationAngle + 0.3F) % 360;

View file

@ -74,20 +74,20 @@ public class SphereParticle extends Particle implements Attachment {
} }
@Override @Override
public void setAttribute(int key, Object value) { public void setAttribute(int key, Number value) {
if (key == ATTR_RADIUS) { if (key == ATTR_RADIUS) {
toRadius = (float)value; toRadius = value.floatValue();
steps = 20; steps = 20;
lerpIncrement = (toRadius - radius) / steps; lerpIncrement = (toRadius - radius) / steps;
} }
if (key == ATTR_COLOR) { if (key == ATTR_COLOR) {
int tint = (int)value; int tint = value.intValue();
red = Color.r(tint); red = Color.r(tint);
green = Color.g(tint); green = Color.g(tint);
blue = Color.b(tint); blue = Color.b(tint);
} }
if (key == ATTR_OPACITY) { if (key == ATTR_OPACITY) {
alpha = (float)value; alpha = value.floatValue();
} }
} }

View file

@ -33,7 +33,7 @@ public class EntityReference<T extends Entity> implements NbtSerialisable {
fromNBT(nbt); fromNBT(nbt);
} }
public void copyFrom(EntityReference<T> other) { public void copyFrom(EntityReference<? extends T> other) {
uuid = other.uuid; uuid = other.uuid;
clientId = other.clientId; clientId = other.clientId;
pos = other.pos; pos = other.pos;

View file

@ -95,7 +95,7 @@ public class ParticleHandle {
void detach(); void detach();
void setAttribute(int key, Object value); void setAttribute(int key, Number value);
} }
public static final class Link { public static final class Link {

View file

@ -60,6 +60,10 @@ public class SphereParticleEffect implements ParticleEffect {
return offset; return offset;
} }
public void setOffset(Vec3d offset) {
this.offset = offset;
}
public Vec3f getColor() { public Vec3f getColor() {
return color; return color;
} }

View file

@ -152,7 +152,9 @@
"spell.unicopia.vortex.lore": "Creates a magnetic force that pulls in other targets", "spell.unicopia.vortex.lore": "Creates a magnetic force that pulls in other targets",
"spell.unicopia.dark_vortex": "Dark Vortex", "spell.unicopia.dark_vortex": "Dark Vortex",
"spell.unicopia.dark_vortex.lore": "Creates a black hole from which nothing can escape", "spell.unicopia.dark_vortex.lore": "Creates a black hole from which nothing can escape",
"spell.unicopia.portal": "Teleportation", "spell.unicopia.displacement": "Displacement",
"spell.unicopia.displacement.lore": "Swaps the caster's locatin with that of another entity",
"spell.unicopia.portal": "Arcane Rift",
"spell.unicopia.portal.lore": "Connects two points in space for fast travel between", "spell.unicopia.portal.lore": "Connects two points in space for fast travel between",
"spell.unicopia.necromancy": "Necromancy", "spell.unicopia.necromancy": "Necromancy",
"spell.unicopia.necromancy.lore": "Summons undead minions from beyond the grave", "spell.unicopia.necromancy.lore": "Summons undead minions from beyond the grave",