mirror of
https://github.com/Sollace/Unicopia.git
synced 2025-02-17 10:24:23 +01:00
Portal refinements and add the displacement spell
This commit is contained in:
parent
4fde8400c5
commit
af7e19ae69
18 changed files with 329 additions and 93 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue