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

View file

@ -29,7 +29,7 @@ public interface TimedSpell extends Spell {
} }
public float getPercentTimeRemaining(float tickDelta) { public float getPercentTimeRemaining(float tickDelta) {
return MathHelper.lerp(tickDelta, prevDuration, duration) / maxDuration; return MathHelper.lerp(tickDelta, prevDuration, duration) / (float)maxDuration;
} }
public int getTicksRemaining() { 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.Living;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes; import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect; import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate; 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.Entity;
import net.minecraft.entity.ItemEntity; import net.minecraft.entity.ItemEntity;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@ -44,9 +44,10 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp
if (timer.getTicksRemaining() <= 0) { if (timer.getTicksRemaining() <= 0) {
return false; return false;
} }
setDirty();
} }
setDirty();
target.getOrEmpty(caster.asWorld()) target.getOrEmpty(caster.asWorld())
.filter(entity -> entity.distanceTo(caster.asEntity()) > getDrawDropOffRange(caster)) .filter(entity -> entity.distanceTo(caster.asEntity()) > getDrawDropOffRange(caster))
.ifPresent(entity -> { .ifPresent(entity -> {
@ -59,12 +60,13 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp
@Override @Override
public void generateParticles(Caster<?> source) { 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( source.addParticle(
new FollowingParticleEffect(UParticles.HEALTH_DRAIN, source.asEntity(), 0.4F) new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F)
.withChild(new MagicParticleEffect(getType().getColor())), .withChild(ParticleTypes.AMBIENT_ENTITY_EFFECT),
p, p,
Vec3d.ZERO 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.*;
import com.minelittlepony.unicopia.entity.mob.UEntityAttributes; import com.minelittlepony.unicopia.entity.mob.UEntityAttributes;
import com.minelittlepony.unicopia.entity.player.Pony; 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.UParticles;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate; import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
@ -48,11 +46,11 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
.with(Trait.POWER, 1) .with(Trait.POWER, 1)
.build(); .build();
protected final ParticleHandle particlEffect = new ParticleHandle();
private final Timer timer; private final Timer timer;
private int struggles; private int struggles;
private float prevRadius;
private float radius; private float radius;
protected BubbleSpell(CustomisedSpellType<?> type) { protected BubbleSpell(CustomisedSpellType<?> type) {
@ -66,6 +64,10 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
return timer; return timer;
} }
public float getRadius(float tickDelta) {
return MathHelper.lerp(tickDelta, prevRadius, radius);
}
@Override @Override
public boolean apply(Caster<?> source) { public boolean apply(Caster<?> source) {
@ -95,14 +97,19 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
public boolean tick(Caster<?> source, Situation situation) { public boolean tick(Caster<?> source, Situation situation) {
if (situation == Situation.PROJECTILE) { if (situation == Situation.PROJECTILE) {
source.spawnParticles(UParticles.BUBBLE, 2);
source.spawnParticles(ParticleTypes.BUBBLE, 2);
return true; return true;
} }
timer.tick(); 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; return false;
} }
@ -116,7 +123,7 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
source.asEntity().fallDistance = 0; source.asEntity().fallDistance = 0;
Vec3d origin = source.getOriginVector(); prevRadius = radius;
if (source instanceof Pony pony && pony.sneakingChanged() && pony.asEntity().isSneaking()) { if (source instanceof Pony pony && pony.sneakingChanged() && pony.asEntity().isSneaking()) {
setDirty(); 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(); return !isDead();
} }
@Override @Override
protected void onDestroyed(Caster<?> source) { protected void onDestroyed(Caster<?> source) {
particlEffect.destroy();
if (source.asEntity() instanceof LivingEntity l) { if (source.asEntity() instanceof LivingEntity l) {
MODIFIERS.forEach((attribute, modifier) -> { MODIFIERS.forEach((attribute, modifier) -> {
if (l.getAttributes().hasAttribute(attribute)) { 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.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; 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.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.LightningBoltParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.particle.SphereParticleEffect;
import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate; 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 static final Vec3d SPHERE_OFFSET = new Vec3d(0, 2, 0);
private int age = 0;
private float accumulatedMass = 0; private float accumulatedMass = 0;
protected DarkVortexSpell(CustomisedSpellType<?> type) { protected DarkVortexSpell(CustomisedSpellType<?> type) {
@ -80,10 +79,7 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
return true; return true;
} }
age++; if (source.asEntity().age % 20 == 0) {
setDirty();
if (age % 20 == 0) {
source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_ADDITIONS, SoundCategory.AMBIENT, 1, 1); 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) { public void generateParticles(Caster<?> source) {
super.generateParticles(source); super.generateParticles(source);
float radius = (float)getEventHorizonRadius(); if (getEventHorizonRadius() > 0.3) {
double range = getDrawDropOffRange(source);
particlEffect.update(getUuid(), source, spawner -> { Vec3d origin = getOrigin(source);
spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, 0x000000, 0.99F, radius, SPHERE_OFFSET), source.getOriginVector(), Vec3d.ZERO); source.spawnParticles(origin, new Sphere(false, range), 1, p -> {
}).ifPresent(p -> { if (!source.asWorld().isAir(BlockPos.ofFloored(p))) {
p.setAttribute(Attachment.ATTR_RADIUS, radius); source.addParticle(
p.setAttribute(Attachment.ATTR_OPACITY, 2F); new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F)
}); .withChild(ParticleTypes.CAMPFIRE_SIGNAL_SMOKE),
particlEffect.update(getUuid(), "_ring", source, spawner -> { p,
spawner.addParticle(new SphereParticleEffect(UParticles.DISK, 0xAAAAAA, 0.4F, radius + 1, SPHERE_OFFSET), getOrigin(source), Vec3d.ZERO); Vec3d.ZERO
}).ifPresent(p -> { );
p.setAttribute(Attachment.ATTR_RADIUS, radius * 0F); }
}); });
}
source.spawnParticles(ParticleTypes.SMOKE, 3);
} }
@Override @Override
@ -162,7 +157,6 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
applyRadialEffect(source, e, e.getPos().distanceTo(origin), radius); 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 // 2. max force (at dist 0) is taken from accumulated mass
// 3. force reaches 0 at distance of drawDropOffRange // 3. force reaches 0 at distance of drawDropOffRange
private double getEventHorizonRadius() { public double getEventHorizonRadius() {
return Math.sqrt(Math.max(0.001, getMass() - 12)); return Math.sqrt(Math.max(0.001, getMass() / 3F));
} }
private double getAttractiveForce(Caster<?> source, Entity target) { private double getAttractiveForce(Caster<?> source, Entity target) {
@ -189,8 +183,7 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
} }
private double getMass() { private double getMass() {
float pulse = (float)Math.sin(age * 8) / 1F; return Math.min(15, 0.1F + accumulatedMass / 10F);
return 10 + Math.min(15, Math.min(0.5F + pulse, (float)Math.exp(age) / 8F - 90) + accumulatedMass / 10F) + pulse;
} }
@Override @Override
@ -202,6 +195,11 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
if (distance <= getEventHorizonRadius() + 0.5) { if (distance <= getEventHorizonRadius() + 0.5) {
target.setVelocity(target.getVelocity().multiply(distance / (2 * radius))); target.setVelocity(target.getVelocity().multiply(distance / (2 * radius)));
if (distance < 1) {
target.setVelocity(target.getVelocity().multiply(distance));
}
Living.updateVelocity(target);
@Nullable @Nullable
Entity master = source.getMaster(); Entity master = source.getMaster();
@ -221,13 +219,19 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
double massOfTarget = AttractionUtils.getMass(target); double massOfTarget = AttractionUtils.getMass(target);
accumulatedMass += massOfTarget; if (!source.isClient() && massOfTarget != 0) {
setDirty(); accumulatedMass += massOfTarget;
setDirty();
}
target.damage(source.damageOf(UDamageTypes.GAVITY_WELL_RECOIL, source), Integer.MAX_VALUE); target.damage(source.damageOf(UDamageTypes.GAVITY_WELL_RECOIL, source), Integer.MAX_VALUE);
if (!(target instanceof PlayerEntity)) { if (!(target instanceof PlayerEntity)) {
target.discard(); target.discard();
source.asWorld().playSound(null, source.getOrigin(), USounds.ENCHANTMENT_CONSUMPTION_CONSUME, SoundCategory.AMBIENT, 2, 0.02F); 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.subtractEnergyCost(-massOfTarget * 10);
source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_MOOD, SoundCategory.AMBIENT, 2, 0.02F); 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 @Override
public void toNBT(NbtCompound compound) { public void toNBT(NbtCompound compound) {
super.toNBT(compound); super.toNBT(compound);
compound.putInt("age", age);
compound.putFloat("accumulatedMass", accumulatedMass); compound.putFloat("accumulatedMass", accumulatedMass);
} }
@Override @Override
public void fromNBT(NbtCompound compound) { public void fromNBT(NbtCompound compound) {
super.fromNBT(compound); super.fromNBT(compound);
age = compound.getInt("age");
accumulatedMass = compound.getFloat("accumulatedMass"); 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.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes; 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.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate; 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.hit.EntityHitResult;
import net.minecraft.util.math.Vec3d; 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<>(); 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); 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) { private void teleport(Caster<?> source, Entity entity, Vec3d pos, Vec3d vel) {
entity.teleport(pos.x, pos.y, pos.z); entity.teleport(pos.x, pos.y, pos.z);
entity.setVelocity(vel); 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.EntityReference;
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
import com.minelittlepony.unicopia.particle.*; import com.minelittlepony.unicopia.particle.*;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.server.world.Ether;
import com.minelittlepony.unicopia.util.shape.*; 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); 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
protected void onDestroyed(Caster<?> caster) { protected void onDestroyed(Caster<?> caster) {
particleEffect.destroy(); particleEffect.destroy();

View file

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

View file

@ -10,7 +10,6 @@ import java.util.stream.Stream;
import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.ability.magic.Affine; import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.Caster; 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.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem; 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) { public Stream<Entity> getEntities(Caster<?> source, double radius, BiPredicate<Caster<?>, Entity> filter) {
targets.values().removeIf(Target::tick); targets.values().removeIf(Target::tick);
return source.findAllEntitiesInRange(radius) 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(EquinePredicates.EXCEPT_MAGIC_IMMUNE)
.filter(e -> filter.test(source, e)) .filter(e -> filter.test(source, e))
.map(i -> { .map(i -> {
@ -57,6 +56,11 @@ public class TargetSelecter {
public static <T extends Entity> boolean isOwnerOrFriend(Affine affine, Caster<?> source, Entity target) { public static <T extends Entity> boolean isOwnerOrFriend(Affine affine, Caster<?> source, Entity target) {
Entity owner = source.getMaster(); Entity owner = source.getMaster();
var equine = Pony.of(target);
if (equine.isPresent() && !affine.isFriendlyTogether(equine.get())) {
return false;
}
if (affine.isEnemy(source)) { if (affine.isEnemy(source)) {
return FriendshipBraceletItem.isComrade(source, target); 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.ChangelingMagicParticle;
import com.minelittlepony.unicopia.client.particle.CloudsEscapingParticle; import com.minelittlepony.unicopia.client.particle.CloudsEscapingParticle;
import com.minelittlepony.unicopia.client.particle.DiskParticle; 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.GroundPoundParticle;
import com.minelittlepony.unicopia.client.particle.HealthDrainParticle; import com.minelittlepony.unicopia.client.particle.HealthDrainParticle;
import com.minelittlepony.unicopia.client.particle.LightningBoltParticle; import com.minelittlepony.unicopia.client.particle.LightningBoltParticle;
@ -65,6 +66,7 @@ public interface URenderers {
static void bootstrap() { static void bootstrap() {
ParticleFactoryRegistry.getInstance().register(UParticles.UNICORN_MAGIC, createFactory(MagicParticle::new)); ParticleFactoryRegistry.getInstance().register(UParticles.UNICORN_MAGIC, createFactory(MagicParticle::new));
ParticleFactoryRegistry.getInstance().register(UParticles.CHANGELING_MAGIC, createFactory(ChangelingMagicParticle::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.RAIN_DROPS, createFactory(RaindropsParticle::new));
ParticleFactoryRegistry.getInstance().register(UParticles.HEALTH_DRAIN, createFactory(HealthDrainParticle::create)); ParticleFactoryRegistry.getInstance().register(UParticles.HEALTH_DRAIN, createFactory(HealthDrainParticle::create));
ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_RING, RainboomParticle::new); 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.*;
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; 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.client.sound.*;
import com.minelittlepony.unicopia.entity.ItemTracker; import com.minelittlepony.unicopia.entity.ItemTracker;
import com.minelittlepony.unicopia.entity.effect.EffectUtils; 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) { 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); boolean hasEffect = client.player.hasStatusEffect(UEffects.SUN_BLINDNESS);
ItemStack glasses = GlassesItem.getForEntity(client.player); ItemStack glasses = GlassesItem.getForEntity(client.player);

View file

@ -30,7 +30,7 @@ public class CloudsEscapingParticle extends GroundPoundParticle {
); );
double columnHeight = 1 + age / 30; double columnHeight = 1 + age / 30;
new Sphere(true, columnHeight, 1, 1, 1) new Sphere(true, columnHeight)
.translate(center) .translate(center)
.randomPoints(random) .randomPoints(random)
.forEach(point -> { .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(); fixed = effect.fixed();
if (fixed) { if (fixed) {
// Was hamiltonianProduct (CHECK THIS!!)
rotation.mul(RotationAxis.POSITIVE_Y.rotationDegrees(effect.pitch())); rotation.mul(RotationAxis.POSITIVE_Y.rotationDegrees(effect.pitch()));
rotation.mul(RotationAxis.POSITIVE_X.rotationDegrees(180 - effect.yaw())); 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.Identifier;
import net.minecraft.util.math.*; import net.minecraft.util.math.*;
@Deprecated
public class RunesParticle extends OrientedBillboardParticle implements Attachment { public class RunesParticle extends OrientedBillboardParticle implements Attachment {
private static final Identifier[] TEXTURES = new Identifier[] { 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.ability.magic.Caster;
import com.minelittlepony.unicopia.client.FirstPersonRendererOverrides.ArmRenderer; import com.minelittlepony.unicopia.client.FirstPersonRendererOverrides.ArmRenderer;
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
import com.minelittlepony.unicopia.client.render.spell.SpellEffectsRenderDispatcher; import com.minelittlepony.unicopia.client.render.spell.SpellEffectsRenderDispatcher;
import net.minecraft.client.MinecraftClient; 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.VertexConsumerProvider;
import net.minecraft.client.render.entity.feature.FeatureRenderer; import net.minecraft.client.render.entity.feature.FeatureRenderer;
import net.minecraft.client.render.entity.feature.FeatureRendererContext; 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.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Arm; import net.minecraft.util.Arm;
public class AccessoryFeatureRenderer< public class AccessoryFeatureRenderer<
T extends LivingEntity, 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<>(); private static final List<FeatureFactory<?>> REGISTRY = new ArrayList<>();
@ -40,6 +41,10 @@ public class AccessoryFeatureRenderer<
@Override @Override
public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, T entity, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { 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)); features.forEach(feature -> feature.render(matrices, vertexConsumers, light, entity, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch));
Caster.of(entity).ifPresent(caster -> { Caster.of(entity).ifPresent(caster -> {
@ -60,7 +65,7 @@ public class AccessoryFeatureRenderer<
} }
public interface FeatureFactory<T extends LivingEntity> { 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> { public interface Feature<T extends LivingEntity> {
@ -75,11 +80,11 @@ public class AccessoryFeatureRenderer<
public interface FeatureRoot< public interface FeatureRoot<
T extends LivingEntity, T extends LivingEntity,
M extends BipedEntityModel<T>> { M extends EntityModel<T>> {
AccessoryFeatureRenderer<T, M> getAccessories(); AccessoryFeatureRenderer<T, M> getAccessories();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Nullable @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); var renderer = MinecraftClient.getInstance().getEntityRenderDispatcher().getRenderer(entity);
if (renderer instanceof FeatureRoot) { if (renderer instanceof FeatureRoot) {
return (FeatureRoot<T, M>)renderer; return (FeatureRoot<T, M>)renderer;

View file

@ -39,6 +39,15 @@ public final class RenderLayers extends RenderLayer {
.target(TRANSLUCENT_TARGET) .target(TRANSLUCENT_TARGET)
.build(false)); .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 -> { private static final Function<Integer, RenderLayer> MAGIC_COLORIN_FUNC = Util.memoize(color -> {
return of("magic_colored_" + color, return of("magic_colored_" + color,
VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL, VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL,
@ -75,6 +84,10 @@ public final class RenderLayers extends RenderLayer {
return MAGIC_NO_COLOR; return MAGIC_NO_COLOR;
} }
public static RenderLayer getMagicShield() {
return MAGIC_SHIELD;
}
public static RenderLayer getMagicColored() { public static RenderLayer getMagicColored() {
return MAGIC_COLORED; return MAGIC_COLORED;
} }

View file

@ -1,6 +1,5 @@
package com.minelittlepony.unicopia.client.render; package com.minelittlepony.unicopia.client.render;
import org.joml.Matrix4f;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.joml.Vector4f; import org.joml.Vector4f;
@ -11,7 +10,7 @@ import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
public class RenderUtil { 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[] { public static final Vertex[] UNIT_FACE = new Vertex[] {
new Vertex(new Vector3f(0, 0, 0), 1, 1), new Vertex(new Vector3f(0, 0, 0), 1, 1),
new Vertex(new Vector3f(0, 1, 0), 1, 0), 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) { 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); 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) { for (Vertex vertex : UNIT_FACE) {
transformation.transform(TEMP_VECTOR.set(vertex.position(), 1)); Vector4f position = vertex.position(matrices);
buffer.vertex(TEMP_VECTOR.x, TEMP_VECTOR.y, TEMP_VECTOR.z).texture(vertex.u(), vertex.v()).color(r, g, b, a).light(light).next(); buffer.vertex(position.x, position.y, position.z).texture(vertex.u(), vertex.v()).color(r, g, b, a).light(light).next();
} }
te.draw(); 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; import net.minecraft.util.Identifier;
public class CastSpellEntityRenderer extends EntityRenderer<CastSpellEntity> { public class CastSpellEntityRenderer extends EntityRenderer<CastSpellEntity> {
private final SpellEffectsRenderDispatcher spellRenderDispatcher = new SpellEffectsRenderDispatcher();
public CastSpellEntityRenderer(EntityRendererFactory.Context ctx) { public CastSpellEntityRenderer(EntityRendererFactory.Context ctx) {
super(ctx); super(ctx);
} }
@ -24,7 +22,7 @@ public class CastSpellEntityRenderer extends EntityRenderer<CastSpellEntity> {
@Override @Override
public void render(CastSpellEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light) { 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) { 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.ArrayList;
import java.util.List; import java.util.List;
import org.joml.Matrix4f; import org.joml.Vector3f;
import org.joml.Vector4f; import org.joml.Vector4f;
import com.minelittlepony.unicopia.client.render.RenderUtil;
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;
public class BakedModel { public class BakedModel {
private static final Vector4f drawVert = new Vector4f(); protected final List<RenderUtil.Vertex> vertices = new ArrayList<>();
protected final List<Vertex> vertices = new ArrayList<>();
protected void addVertex(Vector4f vertex) { protected void addVertex(Vector4f vertex) {
addVertex(vertex.x, vertex.y, vertex.z, 0, 0); addVertex(vertex.x, vertex.y, vertex.z, 0, 0);
} }
protected void addVertex(float x, float y, float z, float u, float v) { 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); scale = Math.abs(scale);
if (scale < 0.001F) { if (scale < 0.001F) {
return; return;
} }
Matrix4f model = matrices.peek().getPositionMatrix(); matrices.push();
for (Vertex vertex : vertices) { matrices.scale(scale, scale, scale);
drawVert.set(vertex.x() * scale, vertex.y() * scale, vertex.z() * scale, 1); for (RenderUtil.Vertex vertex : vertices) {
drawVert.mul(model); Vector4f pos = vertex.position(matrices);
vertexWriter.vertex(drawVert.x, drawVert.y, drawVert.z, r, g, b, a, vertex.u(), vertex.v(), overlay, light, 0, 0, 0); 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 org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.EquinePredicates;
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.SpellContainer.Operation; import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation;
import com.minelittlepony.unicopia.ability.magic.spell.Spell; 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.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.client.gui.DrawableUtil;
import com.minelittlepony.unicopia.entity.Living; 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.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
import net.minecraft.client.MinecraftClient; 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.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.WorldRenderer; import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.render.model.json.ModelTransformationMode;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
import net.minecraft.resource.ResourceManager; import net.minecraft.resource.ResourceManager;
@ -31,6 +38,7 @@ import net.minecraft.util.Colors;
import net.minecraft.util.Formatting; import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.Box; import net.minecraft.util.math.Box;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RotationAxis; import net.minecraft.util.math.RotationAxis;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@ -43,10 +51,19 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader
REGISTRY.put(type, rendererFactory); 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 @Nullable
private Map<SpellType<?>, SpellRenderer<?>> renderers = Map.of(); private Map<SpellType<?>, SpellRenderer<?>> renderers = Map.of();
private final MinecraftClient client = MinecraftClient.getInstance(); private final MinecraftClient client = MinecraftClient.getInstance();
private SpellEffectsRenderDispatcher() {}
@Override @Override
public Identifier getFabricId() { public Identifier getFabricId() {
return ID; return ID;
@ -57,12 +74,23 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader
return (SpellRenderer<S>)renderers.get(spell.getType()); 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) { 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 -> { caster.getSpellSlot().forEach(spell -> {
var renderer = getRenderer(spell); render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch);
if (renderer != null) {
renderer.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch);
}
return Operation.SKIP; return Operation.SKIP;
}, false); }, false);
@ -77,6 +105,38 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader
renderers = REGISTRY.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().create())); 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) { private void renderSpellDebugInfo(MatrixStack matrices, VertexConsumerProvider vertices, Caster<?> caster, int light) {
matrices.push(); matrices.push();
matrices.multiply(client.getEntityRenderDispatcher().getRotation()); 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) { public CastSpellEntity(EntityType<?> type, World world) {
super(type, world); super(type, world);
ignoreCameraFrustum = true;
} }
@Override @Override

View file

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

View file

@ -78,7 +78,7 @@ public class FriendshipBraceletItem extends WearableItem implements DyeableItem,
@Override @Override
public EquipmentSlot getSlotType(ItemStack stack) { 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) { 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(); return ArmorMaterials.LEATHER.getEquipSound();
} }
@Deprecated
@Override @Override
public final EquipmentSlot getSlotType() { public final EquipmentSlot getSlotType() {
return getSlotType(getDefaultStack()); return getSlotType(getDefaultStack());

View file

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

View file

@ -1,17 +1,25 @@
package com.minelittlepony.unicopia.mixin.client; 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.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.At.Shift; import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 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.AnimalPoser;
import com.minelittlepony.unicopia.client.render.PlayerPoser; 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.VertexConsumerProvider;
import net.minecraft.client.render.entity.EntityRenderer; import net.minecraft.client.render.entity.EntityRenderer;
import net.minecraft.client.render.entity.LivingEntityRenderer; 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.feature.FeatureRendererContext;
import net.minecraft.client.render.entity.model.BipedEntityModel; import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.render.entity.model.EntityModel; import net.minecraft.client.render.entity.model.EntityModel;
@ -21,8 +29,31 @@ import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
@Mixin(LivingEntityRenderer.class) @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); } 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", @Inject(method = "render",
at = @At( at = @At(
@ -36,6 +67,7 @@ abstract class MixinLivingEntityRenderer<T extends LivingEntity, M extends Entit
VertexConsumerProvider vertices, VertexConsumerProvider vertices,
int light, int light,
CallbackInfo into) { CallbackInfo into) {
getAccessories();
if (entity instanceof PlayerEntity player) { if (entity instanceof PlayerEntity player) {
PlayerPoser.INSTANCE.applyPosing(matrices, player, (BipedEntityModel<?>)getModel(), PlayerPoser.Context.THIRD_PERSON); PlayerPoser.INSTANCE.applyPosing(matrices, player, (BipedEntityModel<?>)getModel(), PlayerPoser.Context.THIRD_PERSON);
} }

View file

@ -1,13 +1,11 @@
package com.minelittlepony.unicopia.mixin.client; package com.minelittlepony.unicopia.mixin.client;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.At.Shift; import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 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.PlayerPoser;
import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer.FeatureRoot; import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer.FeatureRoot;
@ -21,31 +19,14 @@ import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Arm; import net.minecraft.util.Arm;
@Mixin(PlayerEntityRenderer.class) @Mixin(PlayerEntityRenderer.class)
abstract class MixinPlayerEntityRenderer abstract class MixinPlayerEntityRenderer extends LivingEntityRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>> {
extends LivingEntityRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>>
implements FeatureRoot<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>> {
@Nullable
private AccessoryFeatureRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>> accessories;
MixinPlayerEntityRenderer() { super(null, null, 0); } MixinPlayerEntityRenderer() { super(null, null, 0); }
@Override
@SuppressWarnings("unchecked") @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")) @Inject(method = "renderArm", at = @At("RETURN"))
private void onRenderArm(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, AbstractClientPlayerEntity player, ModelPart arm, ModelPart sleeve, CallbackInfo info) { 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; 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", @Inject(method = "renderArm",

View file

@ -136,7 +136,7 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private <T extends Spell> Stream<T> read(@Nullable SpellPredicate<T> type, boolean synchronize, boolean sendUpdate) { 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) { if (synchronize && spells.fromNbt(owner.asEntity().getDataTracker().get(param)) && sendUpdate) {
owner.asEntity().getDataTracker().set(param, spells.toNbt()); write();
} }
if (type == null) { 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. * A connection class for updating and persisting an attached particle effect.
*/ */
@Deprecated
public class ParticleHandle { public class ParticleHandle {
private final Map<String, Attachment> loadedEffects = new WeakHashMap<>(); 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)); ParticleType<MagicParticleEffect> UNICORN_MAGIC = register("unicorn_magic", FabricParticleTypes.complex(MagicParticleEffect.FACTORY));
DefaultParticleType CHANGELING_MAGIC = register("changeling_magic", FabricParticleTypes.simple()); 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)); ParticleType<OrientedBillboardParticleEffect> RAINBOOM_RING = register("rainboom_ring", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY));
DefaultParticleType RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.simple()); DefaultParticleType RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.simple());
@Deprecated
ParticleType<OrientedBillboardParticleEffect> MAGIC_RUNES = register("magic_runes", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY)); ParticleType<OrientedBillboardParticleEffect> MAGIC_RUNES = register("magic_runes", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY));
DefaultParticleType RAIN_DROPS = register("rain_drops", FabricParticleTypes.simple()); DefaultParticleType RAIN_DROPS = register("rain_drops", FabricParticleTypes.simple());

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.util.shape; 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.MathHelper;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.random.Random; import net.minecraft.util.math.random.Random;
@ -102,6 +104,19 @@ public class Sphere implements Shape {
return stretch.multiply(rad); 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) { public static double computeEllipsoidArea(double rad, Vec3d stretch) {
double p = 1.6075; double p = 1.6075;
double result = Math.pow(rad * stretch.x, p) * Math.pow(rad * stretch.y, p); 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", "parent": "unicopia:unicopia/earth/born_on_a_rock_farm",
"display": { "display": {
"icon": { "icon": {
"item": "unicopia:rock" "item": "unicopia:iron_horse_shoe"
}, },
"title": { "title": {
"translate": "advancements.unicopia.sticks_and_stones.title" "translate": "advancements.unicopia.dead_ringer.title"
}, },
"description": { "description": {
"translate": "advancements.unicopia.sticks_and_stones.description" "translate": "advancements.unicopia.dead_ringer.description"
}, },
"frame": "task", "frame": "task",
"show_toast": true, "show_toast": true,
@ -16,13 +16,13 @@
"hidden": true "hidden": true
}, },
"criteria": { "criteria": {
"killed_entity_with_rock": { "killed_entity_with_horseshoe": {
"trigger": "minecraft:player_killed_entity", "trigger": "minecraft:player_killed_entity",
"conditions": { "conditions": {
"killing_blow": { "killing_blow": {
"tags": [ "tags": [
{ {
"id": "unicopia:from_rocks", "id": "unicopia:from_horseshoes",
"expected": true "expected": true
} }
] ]
@ -31,6 +31,6 @@
} }
}, },
"requirements": [ "requirements": [
[ "killed_entity_with_rock" ] [ "killed_entity_with_horseshoe" ]
] ]
} }

View file

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