Merge branch '1.20.1' of https://github.com/LingVarr/Unicopia into 1.20.1

This commit is contained in:
LingVarr 2024-01-23 10:33:40 +11:00
commit e1cc467b05
43 changed files with 618 additions and 226 deletions

View file

@ -33,7 +33,7 @@ public interface EquinePredicates {
Predicate<Entity> IS_CASTER = e -> !e.isRemoved() && (e instanceof Caster || IS_PLAYER.test(e));
Predicate<Entity> IS_PLACED_SPELL = e -> e instanceof Caster && !e.isRemoved();
Predicate<Entity> IS_MAGIC_IMMUNE = e -> (e instanceof MagicImmune || !(e instanceof LivingEntity)) && !(e instanceof ItemEntity);
Predicate<Entity> IS_MAGIC_IMMUNE = e -> (e instanceof MagicImmune || !(e instanceof LivingEntity)) && !(e instanceof ItemEntity) && !(e instanceof ExperienceOrbEntity);
Predicate<Entity> EXCEPT_MAGIC_IMMUNE = IS_MAGIC_IMMUNE.negate();
Predicate<Entity> VALID_LIVING_AND_NOT_MAGIC_IMMUNE = EntityPredicates.VALID_LIVING_ENTITY.and(EXCEPT_MAGIC_IMMUNE);

View file

@ -13,10 +13,6 @@ import com.minelittlepony.unicopia.entity.mob.UEntities;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.MsgCasterLookRequest;
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleHandle;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.server.world.Ether;
import com.minelittlepony.unicopia.util.NbtSerialisable;
@ -24,6 +20,7 @@ import net.minecraft.nbt.*;
import net.minecraft.registry.*;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
@ -40,11 +37,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
@Nullable
private RegistryKey<World> dimension;
/**
* The visual effect
*/
private final ParticleHandle particlEffect = new ParticleHandle();
/**
* ID of the placed counterpart of this spell.
*/
@ -64,6 +56,9 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
public float pitch;
public float yaw;
private int prevAge;
private int age;
private Optional<Vec3d> position = Optional.empty();
public PlaceableSpell(CustomisedSpellType<?> type) {
@ -75,15 +70,13 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
return this;
}
@Override
public Collection<Spell> getDelegates() {
return List.of(spell);
public float getAge(float tickDelta) {
return MathHelper.lerp(tickDelta, prevAge, age);
}
@Override
public void setDead() {
super.setDead();
particlEffect.destroy();
public Collection<Spell> getDelegates() {
return List.of(spell);
}
@Override
@ -115,14 +108,11 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
}
}
if (spell instanceof PlacementDelegate delegate) {
delegate.updatePlacement(source, this);
prevAge = age;
if (age < 25) {
age++;
}
getParticleEffectAttachment(source).ifPresent(p -> {
p.setAttribute(Attachment.ATTR_COLOR, spell.getType().getColor());
});
return super.tick(source, Situation.GROUND);
}
@ -200,12 +190,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
return castEntity.getTarget().map(EntityValues::pos);
}
public Optional<Attachment> getParticleEffectAttachment(Caster<?> source) {
return particlEffect.update(getUuid(), source, spawner -> {
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.asWorld().getServer().getWorld(dim));
@ -214,11 +198,15 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
compound.putInt("age", age);
compound.putFloat("pitch", pitch);
compound.putFloat("yaw", yaw);
position.ifPresent(pos -> {
compound.put("position", NbtSerialisable.writeVector(pos));
});
if (placedSpellId != null) {
compound.putUuid("placedSpellId", placedSpellId);
}
if (dimension != null) {
compound.putString("dimension", dimension.getValue().toString());
}
@ -229,9 +217,11 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
age = compound.getInt("age");
pitch = compound.getFloat("pitch");
yaw = compound.getFloat("yaw");
position = compound.contains("position") ? Optional.of(NbtSerialisable.readVector(compound.getList("position", NbtElement.FLOAT_TYPE))) : Optional.empty();
placedSpellId = compound.containsUuid("placedSpellId") ? compound.getUuid("placedSpellId") : null;
if (compound.contains("dimension", NbtElement.STRING_TYPE)) {
Identifier id = Identifier.tryParse(compound.getString("dimension"));
if (id != null) {
@ -260,9 +250,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
}
public interface PlacementDelegate {
void onPlaced(Caster<?> source, PlaceableSpell parent, CastSpellEntity entity);
void updatePlacement(Caster<?> source, PlaceableSpell parent);
}
}

View file

@ -54,7 +54,7 @@ public class RainboomAbilitySpell extends AbstractSpell {
});
if (source.isClient()) {
// source.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, source.getPhysics().getMotionAngle()), source.getOriginVector(), Vec3d.ZERO);
//source.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, source.getPhysics().getMotionAngle()), source.getOriginVector(), Vec3d.ZERO);
}
source.findAllEntitiesInRange(RADIUS).forEach(e -> {

View file

@ -29,7 +29,7 @@ public interface TimedSpell extends Spell {
}
public float getPercentTimeRemaining(float tickDelta) {
return MathHelper.lerp(tickDelta, prevDuration, duration) / maxDuration;
return MathHelper.lerp(tickDelta, prevDuration, duration) / (float)maxDuration;
}
public int getTicksRemaining() {

View file

@ -7,7 +7,6 @@ import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
@ -16,6 +15,7 @@ import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ItemEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
@ -44,9 +44,10 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp
if (timer.getTicksRemaining() <= 0) {
return false;
}
setDirty();
}
setDirty();
target.getOrEmpty(caster.asWorld())
.filter(entity -> entity.distanceTo(caster.asEntity()) > getDrawDropOffRange(caster))
.ifPresent(entity -> {
@ -59,12 +60,13 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp
@Override
public void generateParticles(Caster<?> source) {
double range = getDrawDropOffRange(source) + 10;
double range = getDrawDropOffRange(source);
Vec3d origin = getOrigin(source);
source.spawnParticles(getOrigin(source), new Sphere(false, range), 7, p -> {
source.spawnParticles(origin, new Sphere(false, range), 7, p -> {
source.addParticle(
new FollowingParticleEffect(UParticles.HEALTH_DRAIN, source.asEntity(), 0.4F)
.withChild(new MagicParticleEffect(getType().getColor())),
new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F)
.withChild(ParticleTypes.AMBIENT_ENTITY_EFFECT),
p,
Vec3d.ZERO
);

View file

@ -11,12 +11,10 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.*;
import com.minelittlepony.unicopia.entity.mob.UEntityAttributes;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.ParticleHandle;
import com.minelittlepony.unicopia.particle.SphereParticleEffect;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
@ -48,11 +46,11 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
.with(Trait.POWER, 1)
.build();
protected final ParticleHandle particlEffect = new ParticleHandle();
private final Timer timer;
private int struggles;
private float prevRadius;
private float radius;
protected BubbleSpell(CustomisedSpellType<?> type) {
@ -66,6 +64,10 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
return timer;
}
public float getRadius(float tickDelta) {
return MathHelper.lerp(tickDelta, prevRadius, radius);
}
@Override
public boolean apply(Caster<?> source) {
@ -95,14 +97,19 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
public boolean tick(Caster<?> source, Situation situation) {
if (situation == Situation.PROJECTILE) {
source.spawnParticles(ParticleTypes.BUBBLE, 2);
source.spawnParticles(UParticles.BUBBLE, 2);
return true;
}
timer.tick();
if (timer.getTicksRemaining() <= 0) {
boolean done = timer.getTicksRemaining() <= 0;
source.spawnParticles(source.getOriginVector().add(0, 1, 0), new Sphere(true, radius * (done ? 0.25F : 0.5F)), done ? 13 : 1, pos -> {
source.addParticle(done ? ParticleTypes.BUBBLE_POP : UParticles.BUBBLE, pos, Vec3d.ZERO);
});
if (done) {
return false;
}
@ -116,7 +123,7 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
source.asEntity().fallDistance = 0;
Vec3d origin = source.getOriginVector();
prevRadius = radius;
if (source instanceof Pony pony && pony.sneakingChanged() && pony.asEntity().isSneaking()) {
setDirty();
@ -128,18 +135,11 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
}
}
particlEffect.update(getUuid(), source, spawner -> {
spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, 0xFFFFFF, 0.3F, 0, new Vec3d(0, radius / 2F, 0)), origin, Vec3d.ZERO);
}).ifPresent(p -> {
p.setAttribute(Attachment.ATTR_RADIUS, radius);
});
return !isDead();
}
@Override
protected void onDestroyed(Caster<?> source) {
particlEffect.destroy();
if (source.asEntity() instanceof LivingEntity l) {
MODIFIERS.forEach((attribute, modifier) -> {
if (l.getAttributes().hasAttribute(attribute)) {

View file

@ -10,11 +10,11 @@ import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.particle.SphereParticleEffect;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
@ -48,7 +48,6 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
private static final Vec3d SPHERE_OFFSET = new Vec3d(0, 2, 0);
private int age = 0;
private float accumulatedMass = 0;
protected DarkVortexSpell(CustomisedSpellType<?> type) {
@ -80,10 +79,7 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
return true;
}
age++;
setDirty();
if (age % 20 == 0) {
if (source.asEntity().age % 20 == 0) {
source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_ADDITIONS, SoundCategory.AMBIENT, 1, 1);
}
@ -116,21 +112,20 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
public void generateParticles(Caster<?> source) {
super.generateParticles(source);
float radius = (float)getEventHorizonRadius();
particlEffect.update(getUuid(), source, spawner -> {
spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, 0x000000, 0.99F, radius, SPHERE_OFFSET), source.getOriginVector(), Vec3d.ZERO);
}).ifPresent(p -> {
p.setAttribute(Attachment.ATTR_RADIUS, radius);
p.setAttribute(Attachment.ATTR_OPACITY, 2F);
});
particlEffect.update(getUuid(), "_ring", source, spawner -> {
spawner.addParticle(new SphereParticleEffect(UParticles.DISK, 0xAAAAAA, 0.4F, radius + 1, SPHERE_OFFSET), getOrigin(source), Vec3d.ZERO);
}).ifPresent(p -> {
p.setAttribute(Attachment.ATTR_RADIUS, radius * 0F);
});
source.spawnParticles(ParticleTypes.SMOKE, 3);
if (getEventHorizonRadius() > 0.3) {
double range = getDrawDropOffRange(source);
Vec3d origin = getOrigin(source);
source.spawnParticles(origin, new Sphere(false, range), 1, p -> {
if (!source.asWorld().isAir(BlockPos.ofFloored(p))) {
source.addParticle(
new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F)
.withChild(ParticleTypes.CAMPFIRE_SIGNAL_SMOKE),
p,
Vec3d.ZERO
);
}
});
}
}
@Override
@ -162,7 +157,6 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
applyRadialEffect(source, e, e.getPos().distanceTo(origin), radius);
});
}
setDirty();
});
}
}
@ -180,8 +174,8 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
// 2. max force (at dist 0) is taken from accumulated mass
// 3. force reaches 0 at distance of drawDropOffRange
private double getEventHorizonRadius() {
return Math.sqrt(Math.max(0.001, getMass() - 12));
public double getEventHorizonRadius() {
return Math.sqrt(Math.max(0.001, getMass() / 3F));
}
private double getAttractiveForce(Caster<?> source, Entity target) {
@ -189,8 +183,7 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
}
private double getMass() {
float pulse = (float)Math.sin(age * 8) / 1F;
return 10 + Math.min(15, Math.min(0.5F + pulse, (float)Math.exp(age) / 8F - 90) + accumulatedMass / 10F) + pulse;
return Math.min(15, 0.1F + accumulatedMass / 10F);
}
@Override
@ -202,6 +195,11 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
if (distance <= getEventHorizonRadius() + 0.5) {
target.setVelocity(target.getVelocity().multiply(distance / (2 * radius)));
if (distance < 1) {
target.setVelocity(target.getVelocity().multiply(distance));
}
Living.updateVelocity(target);
@Nullable
Entity master = source.getMaster();
@ -221,13 +219,19 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
double massOfTarget = AttractionUtils.getMass(target);
accumulatedMass += massOfTarget;
setDirty();
if (!source.isClient() && massOfTarget != 0) {
accumulatedMass += massOfTarget;
setDirty();
}
target.damage(source.damageOf(UDamageTypes.GAVITY_WELL_RECOIL, source), Integer.MAX_VALUE);
if (!(target instanceof PlayerEntity)) {
target.discard();
source.asWorld().playSound(null, source.getOrigin(), USounds.ENCHANTMENT_CONSUMPTION_CONSUME, SoundCategory.AMBIENT, 2, 0.02F);
}
if (target.isAlive()) {
target.damage(source.asEntity().getDamageSources().outOfWorld(), Integer.MAX_VALUE);
}
source.subtractEnergyCost(-massOfTarget * 10);
source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_MOOD, SoundCategory.AMBIENT, 2, 0.02F);
@ -243,14 +247,12 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
compound.putInt("age", age);
compound.putFloat("accumulatedMass", accumulatedMass);
}
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
age = compound.getInt("age");
accumulatedMass = compound.getFloat("accumulatedMass");
}
}

View file

@ -6,8 +6,6 @@ import com.minelittlepony.unicopia.ability.magic.spell.*;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
@ -16,7 +14,7 @@ import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.math.Vec3d;
public class DisplacementSpell extends AbstractSpell implements HomingSpell, PlaceableSpell.PlacementDelegate, ProjectileDelegate.EntityHitListener {
public class DisplacementSpell extends AbstractSpell implements HomingSpell, ProjectileDelegate.EntityHitListener {
private final EntityReference<Entity> target = new EntityReference<>();
@ -67,19 +65,6 @@ public class DisplacementSpell extends AbstractSpell implements HomingSpell, Pla
originator.subtractEnergyCost(destinationPos.distanceTo(sourcePos) / 20F);
}
@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(Caster<?> source, Entity entity, Vec3d pos, Vec3d vel) {
entity.teleport(pos.x, pos.y, pos.z);
entity.setVelocity(vel);

View file

@ -14,7 +14,6 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
import com.minelittlepony.unicopia.particle.*;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.server.world.Ether;
import com.minelittlepony.unicopia.util.shape.*;
@ -177,14 +176,6 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
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
protected void onDestroyed(Caster<?> caster) {
particleEffect.destroy();

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.Optional;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.Unicopia;
@ -10,11 +12,9 @@ import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleHandle;
import com.minelittlepony.unicopia.particle.SphereParticleEffect;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.projectile.ProjectileUtil;
import com.minelittlepony.unicopia.util.shape.Sphere;
@ -30,6 +30,7 @@ import net.minecraft.entity.passive.PassiveEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.vehicle.AbstractMinecartEntity;
import net.minecraft.entity.vehicle.BoatEntity;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public class ShieldSpell extends AbstractSpell {
@ -40,10 +41,14 @@ public class ShieldSpell extends AbstractSpell {
.with(Trait.AIR, 9)
.build();
protected final ParticleHandle particlEffect = new ParticleHandle();
private final TargetSelecter targetSelecter = new TargetSelecter(this);
private float prevRadius;
private float radius;
private float rangeMultiplier;
private float targetRangeMultiplier;
protected ShieldSpell(CustomisedSpellType<?> type) {
super(type);
}
@ -53,33 +58,27 @@ public class ShieldSpell extends AbstractSpell {
return method == CastingMethod.STAFF || getTraits().get(Trait.GENEROSITY) > 0 ? toPlaceable() : this;
}
@Override
protected void onDestroyed(Caster<?> caster) {
particlEffect.destroy();
}
@Override
public Affinity getAffinity() {
return getTraits().get(Trait.DARKNESS) > 0 ? Affinity.BAD : Affinity.GOOD;
}
protected void generateParticles(Caster<?> source) {
float radius = (float)getDrawDropOffRange(source);
Vec3d origin = getOrigin(source);
source.spawnParticles(origin, new Sphere(true, radius), (int)(radius * 6), pos -> {
source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO);
});
particlEffect.update(getUuid(), source, spawner -> {
spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, getType().getColor(), 0.3F, radius), origin, Vec3d.ZERO);
}).ifPresent(p -> {
p.setAttribute(Attachment.ATTR_RADIUS, radius);
if (source.asWorld().random.nextInt(10) == 0 && source.asWorld().random.nextFloat() < source.getCorruption().getScaled(1)) {
ParticleUtils.spawnParticle(source.asWorld(), new LightningBoltParticleEffect(true, 3, 2, 0.1F, Optional.empty()), pos, Vec3d.ZERO);
}
});
}
@Override
public boolean tick(Caster<?> source, Situation situation) {
prevRadius = radius;
radius = (float)getDrawDropOffRange(source);
if (source.isClient()) {
generateParticles(source);
@ -108,20 +107,32 @@ public class ShieldSpell extends AbstractSpell {
cost *= costMultiplier / ((1 + source.getLevel().get()) * 3F);
cost /= knowledge;
cost += getDrawDropOffRange(source) / 10F;
cost += radius / 10F;
if (!source.subtractEnergyCost(cost)) {
setDead();
}
}
public float getRadius(float tickDelta) {
return MathHelper.lerp(tickDelta, prevRadius, radius);
}
/**
* Calculates the maximum radius of the shield. aka The area of effect.
*/
public double getDrawDropOffRange(Caster<?> source) {
float multiplier = source instanceof Pony pony && pony.asEntity().isSneaking() ? 1 : 2;
targetRangeMultiplier = source instanceof Pony pony && pony.asEntity().isSneaking() ? 1 : 2;
if (rangeMultiplier < targetRangeMultiplier - 0.1F) {
rangeMultiplier += 0.1F;
} else if (rangeMultiplier > targetRangeMultiplier + 0.1) {
rangeMultiplier -= 0.1F;
} else {
rangeMultiplier = targetRangeMultiplier;
}
float min = (source instanceof Pony ? 4 : 6) + getTraits().get(Trait.POWER);
double range = (min + (source.getLevel().getScaled(source instanceof Pony ? 4 : 40) * (source instanceof Pony ? 2 : 10))) / multiplier;
double range = (min + (source.getLevel().getScaled(source instanceof Pony ? 4 : 40) * (source instanceof Pony ? 2 : 10))) / rangeMultiplier;
return range;
}
@ -150,7 +161,7 @@ public class ShieldSpell extends AbstractSpell {
}
protected long applyEntities(Caster<?> source) {
double radius = getDrawDropOffRange(source);
double radius = this.radius;
Vec3d origin = getOrigin(source);

View file

@ -10,7 +10,6 @@ import java.util.stream.Stream;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
@ -29,7 +28,7 @@ public class TargetSelecter {
public Stream<Entity> getEntities(Caster<?> source, double radius, BiPredicate<Caster<?>, Entity> filter) {
targets.values().removeIf(Target::tick);
return source.findAllEntitiesInRange(radius)
.filter(entity -> entity.isAlive() && !entity.isRemoved() && notOwnerOrFriend(spell, source, entity) && !SpellPredicate.IS_SHIELD_LIKE.isOn(entity))
.filter(entity -> entity.isAlive() && !entity.isRemoved() && notOwnerOrFriend(spell, source, entity))
.filter(EquinePredicates.EXCEPT_MAGIC_IMMUNE)
.filter(e -> filter.test(source, e))
.map(i -> {
@ -57,6 +56,11 @@ public class TargetSelecter {
public static <T extends Entity> boolean isOwnerOrFriend(Affine affine, Caster<?> source, Entity target) {
Entity owner = source.getMaster();
var equine = Pony.of(target);
if (equine.isPresent() && !affine.isFriendlyTogether(equine.get())) {
return false;
}
if (affine.isEnemy(source)) {
return FriendshipBraceletItem.isComrade(source, target);
}

View file

@ -10,6 +10,7 @@ import com.minelittlepony.unicopia.block.cloud.CloudChestBlock;
import com.minelittlepony.unicopia.client.particle.ChangelingMagicParticle;
import com.minelittlepony.unicopia.client.particle.CloudsEscapingParticle;
import com.minelittlepony.unicopia.client.particle.DiskParticle;
import com.minelittlepony.unicopia.client.particle.FloatingBubbleParticle;
import com.minelittlepony.unicopia.client.particle.GroundPoundParticle;
import com.minelittlepony.unicopia.client.particle.HealthDrainParticle;
import com.minelittlepony.unicopia.client.particle.LightningBoltParticle;
@ -65,6 +66,7 @@ public interface URenderers {
static void bootstrap() {
ParticleFactoryRegistry.getInstance().register(UParticles.UNICORN_MAGIC, createFactory(MagicParticle::new));
ParticleFactoryRegistry.getInstance().register(UParticles.CHANGELING_MAGIC, createFactory(ChangelingMagicParticle::new));
ParticleFactoryRegistry.getInstance().register(UParticles.BUBBLE, createFactory(FloatingBubbleParticle::new));
ParticleFactoryRegistry.getInstance().register(UParticles.RAIN_DROPS, createFactory(RaindropsParticle::new));
ParticleFactoryRegistry.getInstance().register(UParticles.HEALTH_DRAIN, createFactory(HealthDrainParticle::create));
ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_RING, RainboomParticle::new);

View file

@ -7,6 +7,8 @@ import com.minelittlepony.unicopia.*;
import com.minelittlepony.unicopia.ability.*;
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.client.render.RenderLayers;
import com.minelittlepony.unicopia.client.render.spell.DarkVortexSpellRenderer;
import com.minelittlepony.unicopia.client.sound.*;
import com.minelittlepony.unicopia.entity.ItemTracker;
import com.minelittlepony.unicopia.entity.effect.EffectUtils;
@ -193,6 +195,19 @@ public class UHud {
protected void renderViewEffects(Pony pony, DrawContext context, int scaledWidth, int scaledHeight, float tickDelta) {
float vortexDistortion = DarkVortexSpellRenderer.getCameraDistortion();
if (vortexDistortion > 20) {
context.fill(RenderLayers.getEndPortal(), 0, 0, scaledWidth, scaledHeight, 0);
context.getMatrices().push();
context.getMatrices().translate(scaledWidth / 2, scaledHeight / 2, 0);
DrawableUtil.drawArc(context.getMatrices(), 0, 20, 0, MathHelper.TAU, 0x000000FF, false);
context.getMatrices().pop();
return;
} else if (vortexDistortion > 0) {
context.fill(0, 0, scaledWidth, scaledHeight, (int)((vortexDistortion / 20F) * 255) << 24);
}
boolean hasEffect = client.player.hasStatusEffect(UEffects.SUN_BLINDNESS);
ItemStack glasses = GlassesItem.getForEntity(client.player);

View file

@ -30,7 +30,7 @@ public class CloudsEscapingParticle extends GroundPoundParticle {
);
double columnHeight = 1 + age / 30;
new Sphere(true, columnHeight, 1, 1, 1)
new Sphere(true, columnHeight)
.translate(center)
.randomPoints(random)
.forEach(point -> {

View file

@ -0,0 +1,31 @@
package com.minelittlepony.unicopia.client.particle;
import net.minecraft.client.particle.ParticleTextureSheet;
import net.minecraft.client.particle.SpriteBillboardParticle;
import net.minecraft.client.particle.SpriteProvider;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.particle.ParticleTypes;
public class FloatingBubbleParticle extends SpriteBillboardParticle {
public FloatingBubbleParticle(ParticleEffect effect, SpriteProvider provider, ClientWorld clientWorld, double x, double y, double z, double dX, double dY, double dZ) {
super(clientWorld, x, y, z, dX, dY, dZ);
setSprite(provider);
scale((float)clientWorld.random.nextTriangular(1F, 0.5F));
this.velocityX *= -0.1F;
this.velocityY *= -0.1F;
this.velocityZ *= -0.1F;
this.maxAge *= 3;
}
@Override
public ParticleTextureSheet getType() {
return ParticleTextureSheet.PARTICLE_SHEET_OPAQUE;
}
@Override
public void markDead() {
super.markDead();
world.addParticle(ParticleTypes.BUBBLE_POP, x, y, z, 0, 0, 0);
}
}

View file

@ -22,7 +22,6 @@ public abstract class OrientedBillboardParticle extends AbstractBillboardParticl
fixed = effect.fixed();
if (fixed) {
// Was hamiltonianProduct (CHECK THIS!!)
rotation.mul(RotationAxis.POSITIVE_Y.rotationDegrees(effect.pitch()));
rotation.mul(RotationAxis.POSITIVE_X.rotationDegrees(180 - effect.yaw()));
}

View file

@ -20,6 +20,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.*;
@Deprecated
public class RunesParticle extends OrientedBillboardParticle implements Attachment {
private static final Identifier[] TEXTURES = new Identifier[] {

View file

@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.client.FirstPersonRendererOverrides.ArmRenderer;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
import com.minelittlepony.unicopia.client.render.spell.SpellEffectsRenderDispatcher;
import net.minecraft.client.MinecraftClient;
@ -13,14 +14,14 @@ import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.feature.FeatureRenderer;
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Arm;
public class AccessoryFeatureRenderer<
T extends LivingEntity,
M extends BipedEntityModel<T>> extends FeatureRenderer<T, M> {
M extends EntityModel<T>> extends FeatureRenderer<T, M> {
private static final List<FeatureFactory<?>> REGISTRY = new ArrayList<>();
@ -40,6 +41,10 @@ public class AccessoryFeatureRenderer<
@Override
public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, T entity, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
if (MineLPDelegate.getInstance().getRace(entity).isEquine()) {
return;
}
features.forEach(feature -> feature.render(matrices, vertexConsumers, light, entity, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch));
Caster.of(entity).ifPresent(caster -> {
@ -60,7 +65,7 @@ public class AccessoryFeatureRenderer<
}
public interface FeatureFactory<T extends LivingEntity> {
Feature<T> create(FeatureRendererContext<T, ? extends BipedEntityModel<T>> context);
Feature<T> create(FeatureRendererContext<T, ? extends EntityModel<T>> context);
}
public interface Feature<T extends LivingEntity> {
@ -75,11 +80,11 @@ public class AccessoryFeatureRenderer<
public interface FeatureRoot<
T extends LivingEntity,
M extends BipedEntityModel<T>> {
M extends EntityModel<T>> {
AccessoryFeatureRenderer<T, M> getAccessories();
@SuppressWarnings("unchecked")
@Nullable
static <T extends LivingEntity, M extends BipedEntityModel<T>> FeatureRoot<T, M> of(T entity) {
static <T extends LivingEntity, M extends EntityModel<T>> FeatureRoot<T, M> of(T entity) {
var renderer = MinecraftClient.getInstance().getEntityRenderDispatcher().getRenderer(entity);
if (renderer instanceof FeatureRoot) {
return (FeatureRoot<T, M>)renderer;

View file

@ -39,6 +39,15 @@ public final class RenderLayers extends RenderLayer {
.target(TRANSLUCENT_TARGET)
.build(false));
private static final RenderLayer MAGIC_SHIELD = of("magic_shield", VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL,
VertexFormat.DrawMode.QUADS, 256, true, true, MultiPhaseParameters.builder()
.program(COLOR_PROGRAM)
.transparency(TRANSLUCENT_TRANSPARENCY)
.target(TRANSLUCENT_TARGET)
.cull(DISABLE_CULLING)
.writeMaskState(COLOR_MASK)
.build(false));
private static final Function<Integer, RenderLayer> MAGIC_COLORIN_FUNC = Util.memoize(color -> {
return of("magic_colored_" + color,
VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL,
@ -75,6 +84,10 @@ public final class RenderLayers extends RenderLayer {
return MAGIC_NO_COLOR;
}
public static RenderLayer getMagicShield() {
return MAGIC_SHIELD;
}
public static RenderLayer getMagicColored() {
return MAGIC_COLORED;
}

View file

@ -1,6 +1,5 @@
package com.minelittlepony.unicopia.client.render;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.joml.Vector4f;
@ -11,7 +10,7 @@ import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.util.math.MatrixStack;
public class RenderUtil {
private static final Vector4f TEMP_VECTOR = new Vector4f();
public static final Vector4f TEMP_VECTOR = new Vector4f();
public static final Vertex[] UNIT_FACE = new Vertex[] {
new Vertex(new Vector3f(0, 0, 0), 1, 1),
new Vertex(new Vector3f(0, 1, 0), 1, 0),
@ -21,21 +20,18 @@ public class RenderUtil {
public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light) {
buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT);
Vertex[] UNIT_FACE = new Vertex[] {
new Vertex(new Vector3f(0, 0, 0), 1, 1),
new Vertex(new Vector3f(0, 1, 0), 1, 0),
new Vertex(new Vector3f(1, 1, 0), 0, 0),
new Vertex(new Vector3f(1, 0, 0), 0, 1)
};
Matrix4f transformation = matrices.peek().getPositionMatrix();
for (Vertex vertex : UNIT_FACE) {
transformation.transform(TEMP_VECTOR.set(vertex.position(), 1));
buffer.vertex(TEMP_VECTOR.x, TEMP_VECTOR.y, TEMP_VECTOR.z).texture(vertex.u(), vertex.v()).color(r, g, b, a).light(light).next();
Vector4f position = vertex.position(matrices);
buffer.vertex(position.x, position.y, position.z).texture(vertex.u(), vertex.v()).color(r, g, b, a).light(light).next();
}
te.draw();
}
record Vertex(Vector3f position, float u, float v) {}
public record Vertex(Vector3f position, float u, float v) {
public Vector4f position(MatrixStack matrices) {
matrices.peek().getPositionMatrix().transform(TEMP_VECTOR.set(position, 1));
return TEMP_VECTOR;
}
}
}

View file

@ -11,8 +11,6 @@ import net.minecraft.screen.PlayerScreenHandler;
import net.minecraft.util.Identifier;
public class CastSpellEntityRenderer extends EntityRenderer<CastSpellEntity> {
private final SpellEffectsRenderDispatcher spellRenderDispatcher = new SpellEffectsRenderDispatcher();
public CastSpellEntityRenderer(EntityRendererFactory.Context ctx) {
super(ctx);
}
@ -24,7 +22,7 @@ public class CastSpellEntityRenderer extends EntityRenderer<CastSpellEntity> {
@Override
public void render(CastSpellEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light) {
spellRenderDispatcher.render(matrices, vertexConsumers, light, entity, 0, 0, tickDelta, getAnimationProgress(entity, tickDelta), yaw, 0);
SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertexConsumers, light, entity, 0, 0, tickDelta, getAnimationProgress(entity, tickDelta), yaw, 0);
}
protected float getAnimationProgress(CastSpellEntity entity, float tickDelta) {

View file

@ -3,38 +3,37 @@ package com.minelittlepony.unicopia.client.render.model;
import java.util.ArrayList;
import java.util.List;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.joml.Vector4f;
import com.minelittlepony.unicopia.client.render.RenderUtil;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
public class BakedModel {
private static final Vector4f drawVert = new Vector4f();
protected final List<Vertex> vertices = new ArrayList<>();
protected final List<RenderUtil.Vertex> vertices = new ArrayList<>();
protected void addVertex(Vector4f vertex) {
addVertex(vertex.x, vertex.y, vertex.z, 0, 0);
}
protected void addVertex(float x, float y, float z, float u, float v) {
vertices.add(new Vertex(x, y, z, u, v));
vertices.add(new RenderUtil.Vertex(new Vector3f(x, y, z), u, v));
}
public final void render(MatrixStack matrices, VertexConsumer vertexWriter, int light, int overlay, float scale, float r, float g, float b, float a) {
public final void render(MatrixStack matrices, VertexConsumer buffer, int light, int overlay, float scale, float r, float g, float b, float a) {
scale = Math.abs(scale);
if (scale < 0.001F) {
return;
}
Matrix4f model = matrices.peek().getPositionMatrix();
for (Vertex vertex : vertices) {
drawVert.set(vertex.x() * scale, vertex.y() * scale, vertex.z() * scale, 1);
drawVert.mul(model);
vertexWriter.vertex(drawVert.x, drawVert.y, drawVert.z, r, g, b, a, vertex.u(), vertex.v(), overlay, light, 0, 0, 0);
matrices.push();
matrices.scale(scale, scale, scale);
for (RenderUtil.Vertex vertex : vertices) {
Vector4f pos = vertex.position(matrices);
buffer.vertex(pos.x, pos.y, pos.z, r, g, b, a, vertex.u(), vertex.v(), overlay, light, 0, 0, 0);
}
matrices.pop();
}
record Vertex(float x, float y, float z, float u, float v) {}
}

View file

@ -0,0 +1,12 @@
package com.minelittlepony.unicopia.client.render.model;
public class PlaneModel extends BakedModel {
public static final PlaneModel INSTANCE = new PlaneModel();
private PlaneModel() {
addVertex(-1, -1, 0, 0, 0);
addVertex(-1, 1, 0, 1, 0);
addVertex( 1, 1, 0, 1, 1);
addVertex( 1, -1, 0, 0, 1);
}
}

View file

@ -0,0 +1,41 @@
package com.minelittlepony.unicopia.client.render.spell;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.effect.BubbleSpell;
import com.minelittlepony.unicopia.client.gui.DrawableUtil;
import com.minelittlepony.unicopia.client.render.RenderLayers;
import com.minelittlepony.unicopia.client.render.model.SphereModel;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.RotationAxis;
public class BubbleSpellRenderer implements SpellRenderer<BubbleSpell> {
@Override
public void render(MatrixStack matrices, VertexConsumerProvider vertices, BubbleSpell spell, Caster<?> caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
matrices.push();
double height = caster.asEntity().getEyeY() - caster.getOriginVector().y;
matrices.translate(0, height * 0.5F, 0);
float radius = spell.getRadius(tickDelta) * 0.7F;
VertexConsumer buffer = vertices.getBuffer(RenderLayers.getMagicNoColor());
Entity cameraEntity = MinecraftClient.getInstance().getCameraEntity();
matrices.push();
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-45));
matrices.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(45 + cameraEntity.getYaw(tickDelta)));
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-cameraEntity.getPitch(tickDelta)));
new SphereModel(40, 40, DrawableUtil.PI * 0.25F).render(matrices, buffer, light, 0, radius - 0.1F, 0.9F, 0.9F, 1, 0.3F);
matrices.pop();
SphereModel.SPHERE.render(matrices, buffer, light, 0, radius, 0.9F, 0.9F, 1, 0.25F);
matrices.pop();
}
}

View file

@ -0,0 +1,100 @@
package com.minelittlepony.unicopia.client.render.spell;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.effect.DarkVortexSpell;
import com.minelittlepony.unicopia.client.render.RenderLayers;
import com.minelittlepony.unicopia.client.render.model.PlaneModel;
import com.minelittlepony.unicopia.client.render.model.SphereModel;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RotationAxis;
public class DarkVortexSpellRenderer implements SpellRenderer<DarkVortexSpell> {
private static final Identifier ACCRETION_DISK_TEXTURE = Unicopia.id("textures/spells/dark_vortex/accretion_disk.png");
private static float cameraDistortion;
public static float getCameraDistortion() {
cameraDistortion *= 0.9F;
cameraDistortion = MathHelper.clamp(cameraDistortion, 0, 80);
return cameraDistortion;
}
@Override
public void render(MatrixStack matrices, VertexConsumerProvider vertices, DarkVortexSpell spell, Caster<?> caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
Entity cameraEntity = MinecraftClient.getInstance().getCameraEntity();
float radius = (float)spell.getEventHorizonRadius();
float absDistance = (float)cameraEntity.getEyePos().distanceTo(caster.getOriginVector().add(0, 2, 0));
matrices.push();
matrices.translate(0, 2 + radius, 0);
SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getSolid()), light, 1, Math.min(radius * 0.6F, absDistance * 0.1F), 0, 0, 0, 1);
matrices.push();
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(90));
matrices.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(90 + cameraEntity.getYaw(tickDelta)));
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-cameraEntity.getPitch(tickDelta)));
matrices.scale(0.7F, 1, 1);
float distance = 1F / MathHelper.clamp((absDistance / (radius * 4)), 0.0000001F, 1);
distance *= distance;
if (absDistance < radius * 4) {
cameraDistortion += distance;
}
matrices.scale(distance, distance, distance);
if (absDistance > radius) {
matrices.push();
matrices.translate(0, -0.1F, 0);
for (int i = 0; i < 10; i++) {
matrices.scale(1, 1, 0.796F);
float brightness = i / 10F;
SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getMagicNoColor()), light, 1, radius * (1 + (0.25F * i)) * 0.7F, brightness, brightness, brightness, 0.2F);
}
matrices.pop();
}
SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getEndPortal()), light, 1, radius * 0.5F, 1, 0.5F, 0, 1);
if (radius > 0.3F && absDistance > radius) {
radius *= 3 + radius;
matrices.scale(radius, radius, radius);
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90));
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(animationProgress * 168));
VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(ACCRETION_DISK_TEXTURE));
PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1);
matrices.push();
matrices.scale(0.5F, 0.5F, 0.5F);
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(33));
PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1);
matrices.pop();
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(45));
PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1);
}
matrices.pop();
matrices.pop();
}
}

View file

@ -0,0 +1,72 @@
package com.minelittlepony.unicopia.client.render.spell;
import com.minelittlepony.common.util.Color;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.client.render.model.PlaneModel;
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.RotationAxis;
public class PlacedSpellRenderer implements SpellRenderer<PlaceableSpell> {
private static final Identifier[] TEXTURES = new Identifier[] {
Unicopia.id("textures/particles/runes_0.png"),
Unicopia.id("textures/particles/runes_1.png"),
Unicopia.id("textures/particles/runes_2.png"),
Unicopia.id("textures/particles/runes_3.png"),
Unicopia.id("textures/particles/runes_4.png"),
Unicopia.id("textures/particles/runes_5.png")
};
@Override
public void render(MatrixStack matrices, VertexConsumerProvider vertices, PlaceableSpell spell, Caster<?> caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
if (!(caster.asEntity() instanceof CastSpellEntity)) {
return;
}
for (Spell delegate : spell.getDelegates()) {
matrices.push();
matrices.translate(0, 0.001, 0);
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(spell.pitch));
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90 - spell.yaw));
float scale = (spell.getAge(tickDelta) / 25F) * 3;
matrices.scale(scale, scale, scale);
float angle = (animationProgress / 9F) % 360;
int color = delegate.getType().getColor();
float red = Color.r(color);
float green = Color.g(color);
float blue = Color.b(color);
for (int i = 0; i < TEXTURES.length; i++) {
VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(TEXTURES[i]));
for (int dim = 0; dim < 3; dim++) {
float ringSpeed = (i % 2 == 0 ? i : -1) * i;
matrices.push();
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(angle * ringSpeed));
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(angle * ringSpeed * dim));
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(angle * ringSpeed * dim));
PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, red, green, blue, scale / ((float)(dim * 3) + 1));
matrices.pop();
}
}
matrices.pop();
SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertices, delegate, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch);
}
}
}

View file

@ -0,0 +1,36 @@
package com.minelittlepony.unicopia.client.render.spell;
import com.minelittlepony.common.util.Color;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.effect.ShieldSpell;
import com.minelittlepony.unicopia.client.render.RenderLayers;
import com.minelittlepony.unicopia.client.render.model.SphereModel;
import com.minelittlepony.unicopia.util.ColorHelper;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.MathHelper;
public class ShieldSpellRenderer implements SpellRenderer<ShieldSpell> {
@Override
public void render(MatrixStack matrices, VertexConsumerProvider vertices, ShieldSpell spell, Caster<?> caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
matrices.push();
double height = caster.asEntity().getEyeY() - caster.getOriginVector().y;
matrices.translate(0, height, 0);
int color = ColorHelper.lerp(caster.getCorruption().getScaled(1) * (tickDelta / (1 + caster.asWorld().random.nextFloat())), spell.getType().getColor(), 0xFF000);
float[] colors = ColorHelper.changeSaturation(Color.r(color), Color.g(color), Color.b(color), 4);
float radius = 0.35F + spell.getRadius(tickDelta) + MathHelper.sin(animationProgress / 30F) * 0.01F;
VertexConsumer buffer = vertices.getBuffer(RenderLayers.getMagicShield());
float thickness = 0.02F * MathHelper.sin(animationProgress / 30F);
float alpha = 1 - Math.abs(MathHelper.sin(animationProgress / 20F)) * 0.2F;
SphereModel.SPHERE.render(matrices, buffer, light, 1, radius + thickness, colors[0], colors[1], colors[2], alpha * 0.08F);
SphereModel.SPHERE.render(matrices, buffer, light, 1, radius - thickness, colors[0], colors[1], colors[2], alpha * 0.05F);
SphereModel.SPHERE.render(matrices, buffer, light, 1, radius + thickness * 2, colors[0], colors[1], colors[2], alpha * 0.05F);
matrices.pop();
}
}

View file

@ -8,12 +8,18 @@ import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.TimedSpell;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.client.gui.DrawableUtil;
import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.util.ColorHelper;
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
import net.minecraft.client.MinecraftClient;
@ -22,6 +28,7 @@ import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.render.model.json.ModelTransformationMode;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.registry.Registries;
import net.minecraft.resource.ResourceManager;
@ -31,6 +38,7 @@ import net.minecraft.util.Colors;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RotationAxis;
import net.minecraft.util.math.Vec3d;
@ -43,10 +51,19 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader
REGISTRY.put(type, rendererFactory);
}
static {
register(SpellType.PLACED_SPELL, PlacedSpellRenderer::new);
register(SpellType.SHIELD, ShieldSpellRenderer::new);
register(SpellType.DARK_VORTEX, DarkVortexSpellRenderer::new);
register(SpellType.BUBBLE, BubbleSpellRenderer::new);
}
@Nullable
private Map<SpellType<?>, SpellRenderer<?>> renderers = Map.of();
private final MinecraftClient client = MinecraftClient.getInstance();
private SpellEffectsRenderDispatcher() {}
@Override
public Identifier getFabricId() {
return ID;
@ -57,12 +74,23 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader
return (SpellRenderer<S>)renderers.get(spell.getType());
}
public void render(MatrixStack matrices, VertexConsumerProvider vertices, Spell spell, Caster<?> caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
var renderer = getRenderer(spell);
if (renderer != null) {
client.getBufferBuilders().getEntityVertexConsumers().draw();
renderer.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch);
if (EquinePredicates.IS_CASTER.test(client.player)) {
renderGemstone(matrices, vertices, spell, caster, light, tickDelta, animationProgress);
}
}
}
public void render(MatrixStack matrices, VertexConsumerProvider vertices, int light, Caster<?> caster, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
caster.getSpellSlot().forEach(spell -> {
var renderer = getRenderer(spell);
if (renderer != null) {
renderer.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch);
}
render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch);
return Operation.SKIP;
}, false);
@ -77,6 +105,38 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader
renderers = REGISTRY.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().create()));
}
private void renderGemstone(MatrixStack matrices, VertexConsumerProvider vertices, Spell spell, Caster<?> caster, int light, float tickDelta, float animationProgress) {
matrices.push();
if (!(caster.asEntity() instanceof MagicProjectileEntity)) {
float y = -caster.asEntity().getHeight();
if (caster.asEntity() instanceof CastSpellEntity) {
y = 1F;
}
matrices.translate(0, y + MathHelper.sin(animationProgress / 3F) * 0.2F, 0);
matrices.push();
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(animationProgress));
client.getItemRenderer().renderItem(spell.getType().withTraits(spell.getTraits()).getDefaultStack(), ModelTransformationMode.FIXED, light, 0, matrices, vertices, caster.asWorld(), 0);
matrices.pop();
if (spell instanceof TimedSpell timed && spell.getType() != SpellType.DARK_VORTEX) {
matrices.multiply(client.getEntityRenderDispatcher().getRotation().invert());
float radius = 0.6F;
float timeRemaining = timed.getTimer().getPercentTimeRemaining(tickDelta);
DrawableUtil.drawArc(matrices, radius, radius + 0.3F, 0, DrawableUtil.TAU * timeRemaining,
ColorHelper.lerp(MathHelper.clamp(timeRemaining * 4, 0, 1), 0xFF0000FF, 0xFFFFFFFF),
false
);
}
}
matrices.pop();
}
private void renderSpellDebugInfo(MatrixStack matrices, VertexConsumerProvider vertices, Caster<?> caster, int light) {
matrices.push();
matrices.multiply(client.getEntityRenderDispatcher().getRotation());

View file

@ -37,6 +37,7 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster<CastS
public CastSpellEntity(EntityType<?> type, World world) {
super(type, world);
ignoreCameraFrustum = true;
}
@Override

View file

@ -5,6 +5,7 @@ import java.util.Optional;
import com.minelittlepony.common.util.animation.MotionCompositor;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractDisguiseSpell;
import com.minelittlepony.unicopia.client.render.spell.DarkVortexSpellRenderer;
import net.minecraft.util.math.Vec3d;
@ -62,6 +63,7 @@ public class PlayerCamera extends MotionCompositor {
public double calculateFieldOfView(double fov) {
fov += player.getMagicalReserves().getExertion().get() / 5F;
fov += getEnergyAddition();
fov += DarkVortexSpellRenderer.getCameraDistortion() * 2.5F;
return fov;
}

View file

@ -78,7 +78,7 @@ public class FriendshipBraceletItem extends WearableItem implements DyeableItem,
@Override
public EquipmentSlot getSlotType(ItemStack stack) {
return isSigned(stack) ? EquipmentSlot.CHEST : super.getSlotType();
return isSigned(stack) ? EquipmentSlot.CHEST : super.getSlotType(stack);
}
private boolean checkSignature(ItemStack stack, PlayerEntity player) {

View file

@ -49,6 +49,7 @@ public abstract class WearableItem extends Item implements Equipment {
return ArmorMaterials.LEATHER.getEquipSound();
}
@Deprecated
@Override
public final EquipmentSlot getSlotType() {
return getSlotType(getDefaultStack());

View file

@ -1,19 +1,9 @@
package com.minelittlepony.unicopia.mixin.client;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer;
import net.minecraft.client.render.entity.feature.FeatureRenderer;
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.render.model.BakedModelManager;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity;
@Mixin(ArmorFeatureRenderer.class)
@ -21,13 +11,13 @@ abstract class MixinArmorFeatureRenderer<
T extends LivingEntity,
M extends BipedEntityModel<T>,
A extends BipedEntityModel<T>>
extends FeatureRenderer<T, M> implements AccessoryFeatureRenderer.FeatureRoot<T, M> {
extends FeatureRenderer<T, M>/* implements AccessoryFeatureRenderer.FeatureRoot<T, M>*/ {
private AccessoryFeatureRenderer<T, M> accessories;
//private AccessoryFeatureRenderer<T, M> accessories;
MixinArmorFeatureRenderer() { super(null); }
@Override
/*@Override
public AccessoryFeatureRenderer<T, M> getAccessories() {
return accessories;
}
@ -40,5 +30,5 @@ abstract class MixinArmorFeatureRenderer<
@Inject(method = "render", at = @At("RETURN"))
private void onRender(MatrixStack stack, VertexConsumerProvider renderContext, int lightUv, T entity, float limbDistance, float limbAngle, float tickDelta, float age, float headYaw, float headPitch, CallbackInfo info) {
getAccessories().render(stack, renderContext, lightUv, entity, limbDistance, limbAngle, tickDelta, age, headYaw, headPitch);
}
}*/
}

View file

@ -1,17 +1,25 @@
package com.minelittlepony.unicopia.mixin.client;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer;
import com.minelittlepony.unicopia.client.render.AnimalPoser;
import com.minelittlepony.unicopia.client.render.PlayerPoser;
import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer.FeatureRoot;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.EntityRenderer;
import net.minecraft.client.render.entity.LivingEntityRenderer;
import net.minecraft.client.render.entity.feature.FeatureRenderer;
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.render.entity.model.EntityModel;
@ -21,8 +29,31 @@ import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.player.PlayerEntity;
@Mixin(LivingEntityRenderer.class)
abstract class MixinLivingEntityRenderer<T extends LivingEntity, M extends EntityModel<T>> extends EntityRenderer<T> implements FeatureRendererContext<T, M> {
abstract class MixinLivingEntityRenderer<T extends LivingEntity, M extends EntityModel<T>> extends EntityRenderer<T>
implements FeatureRendererContext<T, M>, FeatureRoot<T, M> {
@Shadow
private @Final List<FeatureRenderer<T, M>> features;
MixinLivingEntityRenderer() { super(null); }
@Nullable
private AccessoryFeatureRenderer<T, M> accessories;
@Override
@SuppressWarnings("unchecked")
public AccessoryFeatureRenderer<T, M> getAccessories() {
if (accessories == null) {
accessories = features.stream()
.filter(a -> a instanceof FeatureRoot)
.map(a -> ((FeatureRoot<T, M>)a).getAccessories())
.findFirst()
.orElseGet(() -> {
var feature = new AccessoryFeatureRenderer<>(this);
features.add(feature);
return feature;
});
}
return accessories;
}
@Inject(method = "render",
at = @At(
@ -36,6 +67,7 @@ abstract class MixinLivingEntityRenderer<T extends LivingEntity, M extends Entit
VertexConsumerProvider vertices,
int light,
CallbackInfo into) {
getAccessories();
if (entity instanceof PlayerEntity player) {
PlayerPoser.INSTANCE.applyPosing(matrices, player, (BipedEntityModel<?>)getModel(), PlayerPoser.Context.THIRD_PERSON);
}

View file

@ -1,13 +1,11 @@
package com.minelittlepony.unicopia.mixin.client;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer;
import com.minelittlepony.unicopia.client.render.PlayerPoser;
import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer.FeatureRoot;
@ -21,31 +19,14 @@ import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Arm;
@Mixin(PlayerEntityRenderer.class)
abstract class MixinPlayerEntityRenderer
extends LivingEntityRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>>
implements FeatureRoot<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>> {
@Nullable
private AccessoryFeatureRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>> accessories;
abstract class MixinPlayerEntityRenderer extends LivingEntityRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>> {
MixinPlayerEntityRenderer() { super(null, null, 0); }
@Override
@SuppressWarnings("unchecked")
public AccessoryFeatureRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>> getAccessories() {
if (accessories == null) {
accessories = features.stream()
.filter(a -> a instanceof FeatureRoot)
.map(a -> ((FeatureRoot<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>>)a).getAccessories())
.findFirst()
.orElseGet(() -> new AccessoryFeatureRenderer<>(this));
}
return accessories;
}
@Inject(method = "renderArm", at = @At("RETURN"))
private void onRenderArm(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, AbstractClientPlayerEntity player, ModelPart arm, ModelPart sleeve, CallbackInfo info) {
Arm a = this.getModel().leftArm == arm ? Arm.LEFT : Arm.RIGHT;
getAccessories().renderArm(matrices, vertexConsumers, light, player, arm, a);
((FeatureRoot<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>>)this).getAccessories().renderArm(matrices, vertexConsumers, light, player, arm, a);
}
@Inject(method = "renderArm",

View file

@ -136,7 +136,7 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
@SuppressWarnings("unchecked")
private <T extends Spell> Stream<T> read(@Nullable SpellPredicate<T> type, boolean synchronize, boolean sendUpdate) {
if (synchronize && spells.fromNbt(owner.asEntity().getDataTracker().get(param)) && sendUpdate) {
owner.asEntity().getDataTracker().set(param, spells.toNbt());
write();
}
if (type == null) {

View file

@ -20,6 +20,7 @@ import net.minecraft.world.World;
/**
* A connection class for updating and persisting an attached particle effect.
*/
@Deprecated
public class ParticleHandle {
private final Map<String, Attachment> loadedEffects = new WeakHashMap<>();

View file

@ -12,10 +12,12 @@ public interface UParticles {
ParticleType<MagicParticleEffect> UNICORN_MAGIC = register("unicorn_magic", FabricParticleTypes.complex(MagicParticleEffect.FACTORY));
DefaultParticleType CHANGELING_MAGIC = register("changeling_magic", FabricParticleTypes.simple());
DefaultParticleType BUBBLE = register("bubble", FabricParticleTypes.simple());
ParticleType<OrientedBillboardParticleEffect> RAINBOOM_RING = register("rainboom_ring", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY));
DefaultParticleType RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.simple());
@Deprecated
ParticleType<OrientedBillboardParticleEffect> MAGIC_RUNES = register("magic_runes", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY));
DefaultParticleType RAIN_DROPS = register("rain_drops", FabricParticleTypes.simple());

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.util.shape;
import org.spongepowered.include.com.google.common.base.Objects;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.random.Random;
@ -102,6 +104,19 @@ public class Sphere implements Shape {
return stretch.multiply(rad);
}
@Override
public boolean equals(Object other) {
return other instanceof Sphere o
&& Objects.equal(stretch, o.stretch)
&& hollow == o.hollow
&& Double.compare(rad, o.rad) == 0;
}
@Override
public int hashCode() {
return Objects.hashCode(stretch, hollow, rad);
}
public static double computeEllipsoidArea(double rad, Vec3d stretch) {
double p = 1.6075;
double result = Math.pow(rad * stretch.x, p) * Math.pow(rad * stretch.y, p);

View file

@ -0,0 +1,5 @@
{
"textures": [
"minecraft:bubble"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View file

@ -2,13 +2,13 @@
"parent": "unicopia:unicopia/earth/born_on_a_rock_farm",
"display": {
"icon": {
"item": "unicopia:rock"
"item": "unicopia:iron_horse_shoe"
},
"title": {
"translate": "advancements.unicopia.sticks_and_stones.title"
"translate": "advancements.unicopia.dead_ringer.title"
},
"description": {
"translate": "advancements.unicopia.sticks_and_stones.description"
"translate": "advancements.unicopia.dead_ringer.description"
},
"frame": "task",
"show_toast": true,
@ -16,13 +16,13 @@
"hidden": true
},
"criteria": {
"killed_entity_with_rock": {
"killed_entity_with_horseshoe": {
"trigger": "minecraft:player_killed_entity",
"conditions": {
"killing_blow": {
"tags": [
{
"id": "unicopia:from_rocks",
"id": "unicopia:from_horseshoes",
"expected": true
}
]
@ -31,6 +31,6 @@
}
},
"requirements": [
[ "killed_entity_with_rock" ]
[ "killed_entity_with_horseshoe" ]
]
}

View file

@ -2,13 +2,13 @@
"parent": "unicopia:unicopia/earth/born_on_a_rock_farm",
"display": {
"icon": {
"item": "unicopia:horseshoe"
"item": "unicopia:rock"
},
"title": {
"translate": "advancements.unicopia.dead_ringer.title"
"translate": "advancements.unicopia.sticks_and_stones.title"
},
"description": {
"translate": "advancements.unicopia.dead_ringer.description"
"translate": "advancements.unicopia.sticks_and_stones.description"
},
"frame": "task",
"show_toast": true,
@ -16,13 +16,13 @@
"hidden": true
},
"criteria": {
"killed_entity_with_horseshoe": {
"killed_entity_with_rock": {
"trigger": "minecraft:player_killed_entity",
"conditions": {
"killing_blow": {
"tags": [
{
"id": "unicopia:from_horseshoes",
"id": "unicopia:from_rocks",
"expected": true
}
]
@ -31,6 +31,6 @@
}
},
"requirements": [
[ "killed_entity_with_horseshoe" ]
[ "killed_entity_with_rock" ]
]
}
}