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);
} else {
player.setAnimation(Animation.ARMS_UP);
if (s instanceof HomingSpell) {
RayTraceHelper.doTrace(player.getMaster(), 600, 1, EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.and(EntityPredicates.VALID_ENTITY)).getEntity().ifPresent(((HomingSpell)s)::setTarget);
if (s instanceof HomingSpell homer) {
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);
}

View file

@ -76,7 +76,7 @@ public class UnicornDispellAbility implements Ability<Pos> {
@Override
public void apply(Pony player, Pos data) {
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();
});
}

View file

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

View file

@ -1,10 +1,18 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.ability.magic.Caster;
import net.minecraft.entity.Entity;
/**
* A spell that's capable of homing in on a pre-defined target.
*/
public interface HomingSpell extends Spell {
int DEFAULT_RANGE = 600;
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;
public float pitch;
public float yaw;
public PlaceableSpell(CustomisedSpellType<?> type) {
super(type);
}
@ -84,7 +87,11 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
CastSpellEntity entity = UEntities.CAST_SPELL.create(source.getReferenceWorld());
Vec3d pos = castEntity.getPosition().orElse(source.getOriginVector());
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.world.spawnEntity(entity);
Ether.get(entity.world).put(getType(), entity);
@ -113,9 +120,11 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
}
}
particlEffect.update(getUuid(), source, spawner -> {
spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.MAGIC_RUNES, 90, 0), source.getOriginVector(), Vec3d.ZERO);
}).ifPresent(p -> {
if (spell instanceof PlacementDelegate delegate) {
delegate.updatePlacement(source, this);
}
getParticleEffectAttachment(source).ifPresent(p -> {
p.setAttribute(Attachment.ATTR_COLOR, spell.getType().getColor());
});
@ -145,6 +154,13 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
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) {
return Optional.ofNullable(dimension)
.map(dim -> source.getReferenceWorld().getServer().getWorld(dim));
@ -153,6 +169,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
compound.putFloat("pitch", pitch);
compound.putFloat("yaw", yaw);
if (dimension != null) {
compound.putString("dimension", dimension.getValue().toString());
}
@ -163,6 +181,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
pitch = compound.getFloat("pitch");
yaw = compound.getFloat("yaw");
if (compound.contains("dimension", NbtElement.STRING_TYPE)) {
Identifier id = Identifier.tryParse(compound.getString("dimension"));
if (id != null) {
@ -182,10 +202,18 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
@Override
protected void saveDelegates(NbtCompound compound) {
compound.put("spell", Spell.SERIALIZER.write(spell));
}
@Override
public PlaceableSpell toPlaceable() {
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;
import java.util.Optional;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.Unicopia;
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.entity.CastSpellEntity;
import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.particle.*;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.util.shape.Sphere;
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.particle.ParticleEffect;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.WorldEvents;
public class PortalSpell extends AbstractSpell {
public static final int MAX_COOLDOWN = 20;
private final EntityReference<CastSpellEntity> teleportationTarget = new EntityReference<>();
public class PortalSpell extends AbstractSpell implements PlaceableSpell.PlacementDelegate {
public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder()
.with(Trait.LIFE, 10)
.with(Trait.KNOWLEDGE, 1)
.with(Trait.ORDER, 25)
.build();
private final EntityReference<Entity> teleportationTarget = new EntityReference<>();
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) {
super(type);
@ -34,36 +48,17 @@ public class PortalSpell extends AbstractSpell {
@Override
public boolean apply(Caster<?> caster) {
pitch = caster.getEntity().getPitch(1);
yaw = caster.getEntity().getYaw(1);
return toPlaceable().apply(caster);
}
@Override
public boolean tick(Caster<?> source, Situation situation) {
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(
targetPos -> tickWithTargetLink(source, targetPos),
() -> findLink(source)
);
if (source.isClient()) {
if (!publishedPosition) {
publishedPosition = true;
Ether.get(source.getReferenceWorld()).put(getType(), source);
}
if (source.isClient() && cooldown <= 0) {
Vec3d origin = source.getOriginVector();
ParticleEffect effect = teleportationTarget.getPosition()
@ -71,51 +66,78 @@ public class PortalSpell extends AbstractSpell {
getType();
return new FollowingParticleEffect(UParticles.HEALTH_DRAIN, target, 0.2F).withChild(ParticleTypes.ELECTRIC_SPARK);
})
.orElseGet(() -> {
new MagicParticleEffect(getType().getColor());
return ParticleTypes.ELECTRIC_SPARK;
});
.orElse(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);
});
}
}
return true;
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()));
}
private void tickWithTargetLink(Caster<?> source, Vec3d targetPos) {
particlEffect.update(getUuid(), source, spawner -> {
spawner.addParticle(new SphereParticleEffect(UParticles.DISK, getType().getColor(), 0.8F, 2), source.getOriginVector(), Vec3d.ZERO);
});
if (cooldown > 0) {
cooldown--;
setDirty();
return;
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 !isDead();
}
private void tickWithTargetLink(Caster<?> source, Ether.Entry destination) {
destination.entity.getPosition().ifPresent(targetPos -> {
Vec3d center = source.getOriginVector();
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;
Vec3d offset = entity.getPos().subtract(center);
Vec3d dest = targetPos;
float yawDifference = pitch < 15 ? (180 - yaw + destination.yaw) : 0;
entity.playSound(USounds.ENTITY_PLAYER_UNICORN_TELEPORT, 1, 1);
entity.teleport(destination.x, destination.y, destination.z);
entity.playSound(USounds.ENTITY_PLAYER_UNICORN_TELEPORT, 1, 1);
dest = dest.add(offset.rotateY(yawDifference * MathHelper.RADIANS_PER_DEGREE)).add(0, 0.05, 0);
entity.resetPortalCooldown();
entity.timeUntilRegen = 20;
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) {
if (source.isClient()) {
return;
}
@ -123,20 +145,50 @@ public class PortalSpell extends AbstractSpell {
Ether ether = Ether.get(source.getReferenceWorld());
ether.getEntries(getType())
.stream()
.filter(entry -> entry.isAvailable() && !entry.entity.referenceEquals(source.getEntity()))
.filter(entry -> entry.isAvailable() && !entry.entity.referenceEquals(source.getEntity()) && entry.entity.getId().isPresent())
.findAny()
.ifPresent(entry -> {
entry.setTaken(true);
teleportationTarget.copyFrom((EntityReference<CastSpellEntity>)entry.entity);
teleportationTarget.copyFrom(entry.entity);
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
public void onDestroyed(Caster<?> caster) {
Ether ether = Ether.get(caster.getReferenceWorld());
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
@ -144,7 +196,8 @@ public class PortalSpell extends AbstractSpell {
super.toNBT(compound);
compound.putBoolean("publishedPosition", publishedPosition);
compound.put("teleportationTarget", teleportationTarget.toNBT());
compound.putInt("cooldown", cooldown);
compound.putFloat("pitch", pitch);
compound.putFloat("yaw", yaw);
}
@Override
@ -152,12 +205,13 @@ public class PortalSpell extends AbstractSpell {
super.fromNBT(compound);
publishedPosition = compound.getBoolean("publishedPosition");
teleportationTarget.fromNBT(compound.getCompound("teleportationTarget"));
cooldown = compound.getInt("cooldown");
pitch = compound.getFloat("pitch");
yaw = compound.getFloat("yaw");
}
@Override
public void 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<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<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() {}

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

View file

@ -5,15 +5,19 @@ import com.minelittlepony.unicopia.particle.SphereParticleEffect;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.Quaternion;
import net.minecraft.util.math.*;
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) {
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

View file

@ -20,8 +20,8 @@ public abstract class OrientedBillboardParticle extends AbstractBillboardParticl
fixed = effect.isAngleFixed();
if (fixed) {
rotation.hamiltonProduct(Vec3f.POSITIVE_X.getDegreesQuaternion(180 - effect.getYaw()));
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
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")
};
protected float targetSize = 3;
protected float prevBaseSize = 0;
protected float baseSize = 0;
@ -61,22 +63,31 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
velocityY = 0;
velocityZ = 0;
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
public void detach() {
link = Optional.empty();
if (targetSize > 1) {
this.targetSize = 0;
}
}
@Override
public void setAttribute(int key, Object value) {
public void setAttribute(int key, Number value) {
if (key == ATTR_COLOR) {
int tint = (int)value;
int tint = value.intValue();
red = Color.r(tint);
green = Color.g(tint);
blue = Color.b(tint);
}
if (key == ATTR_OPACITY) {
alpha = value.floatValue();
}
if (key == ATTR_RADIUS) {
targetSize = value.floatValue();
}
}
@Override
public float getScale(float tickDelta) {
@ -137,6 +148,8 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
renderQuad(te, buffer, corners, alpha, tickDelta);
}
}
RenderSystem.setShaderColor(1, 1, 1, 1);
}
@Override
@ -153,8 +166,11 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
}, this::detach);
prevBaseSize = baseSize;
if (baseSize < 3) {
baseSize++;
if (baseSize < targetSize) {
baseSize += 0.1F;
}
if (baseSize > targetSize) {
baseSize -= 0.1F;
}
rotationAngle = (rotationAngle + 0.3F) % 360;

View file

@ -74,20 +74,20 @@ public class SphereParticle extends Particle implements Attachment {
}
@Override
public void setAttribute(int key, Object value) {
public void setAttribute(int key, Number value) {
if (key == ATTR_RADIUS) {
toRadius = (float)value;
toRadius = value.floatValue();
steps = 20;
lerpIncrement = (toRadius - radius) / steps;
}
if (key == ATTR_COLOR) {
int tint = (int)value;
int tint = value.intValue();
red = Color.r(tint);
green = Color.g(tint);
blue = Color.b(tint);
}
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);
}
public void copyFrom(EntityReference<T> other) {
public void copyFrom(EntityReference<? extends T> other) {
uuid = other.uuid;
clientId = other.clientId;
pos = other.pos;

View file

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

View file

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

View file

@ -152,7 +152,9 @@
"spell.unicopia.vortex.lore": "Creates a magnetic force that pulls in other targets",
"spell.unicopia.dark_vortex": "Dark Vortex",
"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.necromancy": "Necromancy",
"spell.unicopia.necromancy.lore": "Summons undead minions from beyond the grave",