Merge branch '1.20.2' into 1.20.4

This commit is contained in:
Sollace 2024-04-23 14:52:08 +01:00
commit 95abda651d
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
36 changed files with 1163 additions and 68 deletions

File diff suppressed because one or more lines are too long

66
assets/models/mimic.java Normal file
View file

@ -0,0 +1,66 @@
// Made with Blockbench 4.9.4
// Exported for Minecraft version 1.17+ for Yarn
// Paste this class into your mod and generate all required imports
package com.example.mod;
public class mimic extends EntityModel<Entity> {
private final ModelPart lid;
private final ModelPart tongue_r1;
private final ModelPart upper_teeth;
private final ModelPart cube_r1;
private final ModelPart lower_teeth;
private final ModelPart cube_r2;
private final ModelPart right_leg;
private final ModelPart left_leg;
public mimic(ModelPart root) {
this.lid = root.getChild("lid");
this.lower_teeth = root.getChild("lower_teeth");
this.right_leg = root.getChild("right_leg");
this.left_leg = root.getChild("left_leg");
}
public static TexturedModelData getTexturedModelData() {
ModelData modelData = new ModelData();
ModelPartData modelPartData = modelData.getRoot();
ModelPartData lid = modelPartData.addChild("lid", ModelPartBuilder.create(), ModelTransform.of(0.0F, 17.0F, -7.0F, -0.829F, 0.0F, -3.1416F));
ModelPartData tongue_r1 = lid.addChild("tongue_r1", ModelPartBuilder.create().uv(11, 34).cuboid(-3.0F, -10.0F, 1.0F, 6.0F, 1.0F, 8.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 7.0F, 7.0F, 0.5236F, 0.0F, 0.0F));
ModelPartData upper_teeth = lid.addChild("upper_teeth", ModelPartBuilder.create().uv(0, 0).cuboid(-1.0F, -8.0F, 5.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
.uv(0, 0).cuboid(-4.0F, -8.0F, 5.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
.uv(0, 0).cuboid(2.0F, -8.0F, 5.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F)), ModelTransform.pivot(0.0F, 7.0F, 7.0F));
ModelPartData cube_r1 = upper_teeth.addChild("cube_r1", ModelPartBuilder.create().uv(0, 0).cuboid(-6.0F, -1.0F, -6.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
.uv(0, 0).cuboid(-9.0F, -1.0F, -6.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
.uv(0, 0).cuboid(-12.0F, -1.0F, -6.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
.uv(0, 0).cuboid(-6.0F, -1.0F, 5.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
.uv(0, 0).cuboid(-9.0F, -1.0F, 5.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
.uv(0, 0).cuboid(-12.0F, -1.0F, 5.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, -7.0F, -7.0F, 0.0F, 1.5708F, 0.0F));
ModelPartData lower_teeth = modelPartData.addChild("lower_teeth", ModelPartBuilder.create().uv(0, 0).cuboid(-1.0F, -1.0F, 12.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
.uv(0, 0).cuboid(-4.0F, -1.0F, 12.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
.uv(0, 0).cuboid(2.0F, -1.0F, 12.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F)), ModelTransform.pivot(0.0F, 16.0F, -7.0F));
ModelPartData cube_r2 = lower_teeth.addChild("cube_r2", ModelPartBuilder.create().uv(0, 0).cuboid(-6.0F, -1.0F, -6.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
.uv(0, 0).cuboid(-9.0F, -1.0F, -6.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
.uv(0, 0).cuboid(-12.0F, -1.0F, -6.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
.uv(0, 0).cuboid(-6.0F, -1.0F, 5.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
.uv(0, 0).cuboid(-9.0F, -1.0F, 5.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F))
.uv(0, 0).cuboid(-12.0F, -1.0F, 5.0F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 0.0F, 1.5708F, 0.0F));
ModelPartData right_leg = modelPartData.addChild("right_leg", ModelPartBuilder.create().uv(7, 30).cuboid(-2.5F, -1.5F, -3.0F, 5.0F, 7.0F, 6.0F, new Dilation(0.0F)), ModelTransform.pivot(3.5F, 25.5F, 1.0F));
ModelPartData left_leg = modelPartData.addChild("left_leg", ModelPartBuilder.create().uv(7, 30).mirrored().cuboid(-9.5F, -1.5F, -3.0F, 5.0F, 7.0F, 6.0F, new Dilation(0.0F)).mirrored(false), ModelTransform.pivot(3.5F, 25.5F, 1.0F));
return TexturedModelData.of(modelData, 64, 64);
}
@Override
public void setAngles(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) {
}
@Override
public void render(MatrixStack matrices, VertexConsumer vertexConsumer, int light, int overlay, float red, float green, float blue, float alpha) {
lid.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha);
lower_teeth.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha);
right_leg.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha);
left_leg.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha);
}
}

View file

@ -104,6 +104,7 @@ public interface UTags {
TagKey<Block> KICKS_UP_DUST = block("kicks_up_dust"); TagKey<Block> KICKS_UP_DUST = block("kicks_up_dust");
TagKey<Block> POLEARM_MINEABLE = block("mineable/polearm"); TagKey<Block> POLEARM_MINEABLE = block("mineable/polearm");
TagKey<Block> MIMIC_CHESTS = block("mimic_chests");
TagKey<Block> BUTTERFLIES_SPAWNABLE_ON = block("butterflies_spawn_on"); TagKey<Block> BUTTERFLIES_SPAWNABLE_ON = block("butterflies_spawn_on");

View file

@ -3,6 +3,8 @@ package com.minelittlepony.unicopia.block;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.server.world.WeatherConditions; import com.minelittlepony.unicopia.server.world.WeatherConditions;
import com.mojang.serialization.MapCodec; import com.mojang.serialization.MapCodec;
@ -58,6 +60,9 @@ public class WeatherVaneBlock extends BlockWithEntity {
private float clientAngle; private float clientAngle;
private float prevAngle; private float prevAngle;
private float lastAngle;
private Vec3d airflow = Vec3d.ZERO;
public WeatherVane(BlockPos pos, BlockState state) { public WeatherVane(BlockPos pos, BlockState state) {
super(UBlockEntities.WEATHER_VANE, pos, state); super(UBlockEntities.WEATHER_VANE, pos, state);
@ -70,11 +75,14 @@ public class WeatherVaneBlock extends BlockWithEntity {
@Override @Override
public void readNbt(NbtCompound nbt) { public void readNbt(NbtCompound nbt) {
angle = nbt.getFloat("angle"); angle = nbt.getFloat("angle");
airflow = new Vec3d(nbt.getDouble("windX"), 0, nbt.getDouble("windZ"));
} }
@Override @Override
protected void writeNbt(NbtCompound nbt) { protected void writeNbt(NbtCompound nbt) {
nbt.putFloat("angle", angle); nbt.putFloat("angle", angle);
nbt.putDouble("windX", airflow.x);
nbt.putDouble("windZ", airflow.z);
} }
@Override @Override
@ -89,23 +97,26 @@ public class WeatherVaneBlock extends BlockWithEntity {
public static void serverTick(World world, BlockPos pos, BlockState state, WeatherVane entity) { public static void serverTick(World world, BlockPos pos, BlockState state, WeatherVane entity) {
Vec3d airflow = WeatherConditions.get(world).getWindDirection(); Vec3d airflow = WeatherConditions.get(world).getWindDirection();
float angle = (float)Math.atan2(airflow.x, airflow.z) + MathHelper.PI; float angle = (WeatherConditions.get(world).getWindYaw() % MathHelper.PI);
if (Math.signum(entity.angle) != Math.signum(angle)) {
angle = MathHelper.PI - angle;
}
angle %= MathHelper.PI;
entity.lastAngle = entity.prevAngle;
entity.prevAngle = entity.angle;
if (angle != entity.angle) { if (angle != entity.angle) {
entity.angle = angle; entity.angle = angle;
entity.airflow = airflow;
entity.markDirty(); entity.markDirty();
if (world instanceof ServerWorld serverWorld) { if (world instanceof ServerWorld sw) {
serverWorld.getChunkManager().markForUpdate(pos); sw.getChunkManager().markForUpdate(pos);
} }
if (entity.lastAngle == entity.prevAngle) {
world.playSound(null, pos.getX(), pos.getY(), pos.getZ(), USounds.BLOCK_WEATHER_VANE_ROTATE, SoundCategory.BLOCKS, 1, 0.5F + (float)world.random.nextGaussian()); world.playSound(null, pos.getX(), pos.getY(), pos.getZ(), USounds.BLOCK_WEATHER_VANE_ROTATE, SoundCategory.BLOCKS, 1, 0.5F + (float)world.random.nextGaussian());
} }
} }
}
public static void clientTick(World world, BlockPos pos, BlockState state, WeatherVane entity) { public static void clientTick(World world, BlockPos pos, BlockState state, WeatherVane entity) {
entity.prevAngle = entity.clientAngle; entity.prevAngle = entity.clientAngle;
@ -118,6 +129,18 @@ public class WeatherVaneBlock extends BlockWithEntity {
} else if (entity.clientAngle > angle) { } else if (entity.clientAngle > angle) {
entity.clientAngle -= step; entity.clientAngle -= step;
} }
if (world.random.nextInt(3) == 0) {
float radius = 10;
for (int i = 0; i < 5; i++) {
world.addImportantParticle(new TargetBoundParticleEffect(UParticles.WIND, null),
world.getRandom().nextTriangular(pos.getX(), radius),
world.getRandom().nextTriangular(pos.getY(), radius),
world.getRandom().nextTriangular(pos.getZ(), radius),
entity.airflow.x / 10F, 0, entity.airflow.z / 10F
);
}
}
} }
} }
} }

View file

@ -104,7 +104,7 @@ public class KeyBindingsHandler {
changePage(client, maxPage, -1); changePage(client, maxPage, -1);
} else if (page < maxPage && pageUp.getState() == PressedState.PRESSED) { } else if (page < maxPage && pageUp.getState() == PressedState.PRESSED) {
changePage(client, maxPage, 1); changePage(client, maxPage, 1);
} else { } else if (!client.player.isSpectator()) {
for (Binding i : keys.keySet()) { for (Binding i : keys.keySet()) {
AbilitySlot slot = keys.get(i); AbilitySlot slot = keys.get(i);
if (slot == AbilitySlot.PRIMARY && client.options.sneakKey.isPressed() && abilities.isFilled(AbilitySlot.PASSIVE)) { if (slot == AbilitySlot.PRIMARY && client.options.sneakKey.isPressed() && abilities.isFilled(AbilitySlot.PASSIVE)) {

View file

@ -22,6 +22,8 @@ import com.minelittlepony.unicopia.client.particle.RainbowTrailParticle;
import com.minelittlepony.unicopia.client.particle.RaindropsParticle; import com.minelittlepony.unicopia.client.particle.RaindropsParticle;
import com.minelittlepony.unicopia.client.particle.ShockwaveParticle; import com.minelittlepony.unicopia.client.particle.ShockwaveParticle;
import com.minelittlepony.unicopia.client.particle.SphereParticle; import com.minelittlepony.unicopia.client.particle.SphereParticle;
import com.minelittlepony.unicopia.client.particle.SpiralParticle;
import com.minelittlepony.unicopia.client.particle.WindParticle;
import com.minelittlepony.unicopia.client.render.*; import com.minelittlepony.unicopia.client.render.*;
import com.minelittlepony.unicopia.client.render.entity.*; import com.minelittlepony.unicopia.client.render.entity.*;
import com.minelittlepony.unicopia.client.render.shader.UShaders; import com.minelittlepony.unicopia.client.render.shader.UShaders;
@ -79,8 +81,10 @@ public interface URenderers {
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.FOOTPRINT, createFactory(FootprintParticle::new)); ParticleFactoryRegistry.getInstance().register(UParticles.FOOTPRINT, createFactory(FootprintParticle::new));
ParticleFactoryRegistry.getInstance().register(UParticles.SPIRAL, createFactory(SpiralParticle::new));
ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_RING, RainboomParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_RING, RainboomParticle::new);
ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_TRAIL, RainbowTrailParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_TRAIL, RainbowTrailParticle::new);
ParticleFactoryRegistry.getInstance().register(UParticles.WIND, WindParticle::new);
ParticleFactoryRegistry.getInstance().register(UParticles.SHOCKWAVE, ShockwaveParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.SHOCKWAVE, ShockwaveParticle::new);
ParticleFactoryRegistry.getInstance().register(UParticles.SPHERE, SphereParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.SPHERE, SphereParticle::new);
ParticleFactoryRegistry.getInstance().register(UParticles.DISK, DiskParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.DISK, DiskParticle::new);
@ -112,6 +116,7 @@ public interface URenderers {
EntityRendererRegistry.register(UEntities.TENTACLE, TentacleEntityRenderer::new); EntityRendererRegistry.register(UEntities.TENTACLE, TentacleEntityRenderer::new);
EntityRendererRegistry.register(UEntities.IGNOMINIOUS_BULB, IgnominiousBulbEntityRenderer::new); EntityRendererRegistry.register(UEntities.IGNOMINIOUS_BULB, IgnominiousBulbEntityRenderer::new);
EntityRendererRegistry.register(UEntities.SPECTER, EmptyEntityRenderer::new); EntityRendererRegistry.register(UEntities.SPECTER, EmptyEntityRenderer::new);
EntityRendererRegistry.register(UEntities.MIMIC, MimicEntityRenderer::new);
BlockEntityRendererFactories.register(UBlockEntities.WEATHER_VANE, WeatherVaneBlockEntityRenderer::new); BlockEntityRendererFactories.register(UBlockEntities.WEATHER_VANE, WeatherVaneBlockEntityRenderer::new);
BlockEntityRendererFactories.register(UBlockEntities.FANCY_BED, CloudBedBlockEntityRenderer::new); BlockEntityRendererFactories.register(UBlockEntities.FANCY_BED, CloudBedBlockEntityRenderer::new);

View file

@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.client.gui;
import java.util.List; import java.util.List;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import com.minelittlepony.unicopia.*; import com.minelittlepony.unicopia.*;
import com.minelittlepony.unicopia.ability.*; import com.minelittlepony.unicopia.ability.*;
@ -24,6 +25,8 @@ import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer; import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.InGameHud; import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@ -303,6 +306,34 @@ public class UHud {
context.fill(0, 0, scaledWidth, scaledHeight, 0x00FF0000 | (alpha << 24)); context.fill(0, 0, scaledWidth, scaledHeight, 0x00FF0000 | (alpha << 24));
context.fillGradient(0, (int)(scaledHeight / 1.5), scaledWidth, scaledHeight, 0x00FF0000, 0xAAFF0000); context.fillGradient(0, (int)(scaledHeight / 1.5), scaledWidth, scaledHeight, 0x00FF0000, 0xAAFF0000);
} }
if (pony.getPhysics().isFlyingSurvival) {
float effectStrength = (float)MathHelper.clamp(pony.getPhysics().getClientVelocity().length() / 15F, 0, 1);
VertexConsumer vertexConsumer = context.getVertexConsumers().getBuffer(RenderLayer.getGui());
float innerRadiusPulse = MathHelper.cos((pony.asEntity().age + tickDelta) / 2F) * 6 + (effectStrength * scaledHeight / 2F);
double points = 22;
float wedgeAngle = 0.05F + MathHelper.sin((pony.asEntity().age + tickDelta) / 3F) * 0.01F;
float outerRadius = Math.max(scaledWidth, scaledHeight);
float alpha = effectStrength * (0.6F + Math.abs(MathHelper.sin((pony.asEntity().age + tickDelta) / 10F)));
context.getMatrices().push();
context.getMatrices().translate(scaledWidth / 2F, scaledHeight / 2F, 0);
Matrix4f matrix4f = context.getMatrices().peek().getPositionMatrix();
for (int i = 0; i < points; i++) {
float angle = (MathHelper.TAU * i / (float)points) - wedgeAngle * 0.5F;
float innerRadius = Math.max(0, (scaledHeight / 2F) + (i % 2) * 72 + 14 * (1 - effectStrength) - innerRadiusPulse);
float centerX = MathHelper.sin(angle) * innerRadius;
float centerY = MathHelper.cos(angle) * innerRadius;
vertexConsumer.vertex(matrix4f, centerX, centerY, 0).color(1F, 1F, 1F, alpha * 0.3F).next();
vertexConsumer.vertex(matrix4f, MathHelper.sin(angle - wedgeAngle) * outerRadius, MathHelper.cos(angle - wedgeAngle) * outerRadius, 0).color(1F, 1F, 1F, alpha).next();
vertexConsumer.vertex(matrix4f, MathHelper.sin(angle + wedgeAngle) * outerRadius, MathHelper.cos(angle + wedgeAngle) * outerRadius, 0).color(1F, 1F, 1F, alpha).next();
vertexConsumer.vertex(matrix4f, centerX, centerY, 0).color(1F, 1F, 1F, alpha * 0.3F).next();
}
context.getMatrices().pop();
}
} }
private void renderVignette(DrawContext context, int color, float alpha, float radius, int scaledWidth, int scaledHeight) { private void renderVignette(DrawContext context, int color, float alpha, float radius, int scaledWidth, int scaledHeight) {

View file

@ -29,7 +29,7 @@ public class RainbowTrailParticle extends AbstractBillboardParticle {
public RainbowTrailParticle(TargetBoundParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) { public RainbowTrailParticle(TargetBoundParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
super(world, x, y, z, velocityX, velocityY, velocityZ); super(world, x, y, z, velocityX, velocityY, velocityZ);
trail = new Trail(new Vec3d(x, y, z)); trail = new Trail(new Vec3d(x, y, z), 1);
setMaxAge(300); setMaxAge(300);
this.velocityX = velocityX; this.velocityX = velocityX;
this.velocityY = velocityY; this.velocityY = velocityY;

View file

@ -0,0 +1,80 @@
package com.minelittlepony.unicopia.client.particle;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.particle.SpiralParticleEffect;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.particle.NoRenderParticle;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.SpriteProvider;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public class SpiralParticle extends NoRenderParticle {
private final SpiralParticleEffect parameters;
@Nullable
private final Particle particle;
private float scale;
public SpiralParticle(SpiralParticleEffect parameters, SpriteProvider provider, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
super(world, x, y, z, velocityX, velocityY, velocityZ);
this.scale = 0.1f * (random.nextFloat() * 0.5f + 0.5f) * 2.0f;
setMaxAge(3);
scale(0.125F);
this.parameters = parameters;
this.collidesWithWorld = false;
this.particle = MinecraftClient.getInstance().particleManager.addParticle(parameters.effect(), x, y, z, velocityX, velocityY, velocityZ);
this.particle.setMaxAge(1000);
}
@Override
public Particle scale(float scale) {
this.scale *= scale;
super.scale(scale);
return this;
}
@Override
public void move(double dx, double dy, double dz) {
super.move(dx, dy, dz);
if (particle != null) {
particle.setPos(x, y, z);
}
}
@Override
public void tick() {
if (particle == null || !particle.isAlive()) {
markDead();
}
super.tick();
Vec3d target = parameters.centerPoint().getPosition(world);
Vec3d pos = new Vec3d(x, y, z);
if (scale * 1.5F < 0.5F) {
scale(1.5F);
}
double distance = pos.distanceTo(target);
if (distance > 0) {
age = 0;
}
Vec3d radial = target.subtract(pos).normalize();
Vec3d tangent = radial.rotateY(MathHelper.HALF_PI).multiply(parameters.angularVelocity() * 0.9F);
Vec3d motion = radial.multiply(parameters.angularVelocity() * 0.1F).add(tangent);
move(motion.x, motion.y, motion.z);
}
@Override
public String toString() {
return super.toString() + ", Angular Velocity " + parameters.angularVelocity() + ", Target (" + parameters.centerPoint() + ") Sub-Particle (" + particle + ")";
}
}

View file

@ -0,0 +1,104 @@
package com.minelittlepony.unicopia.client.particle;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.client.render.bezier.BezierSegment;
import com.minelittlepony.unicopia.client.render.bezier.Trail;
import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.Tessellator;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public class WindParticle extends AbstractBillboardParticle {
private static final Identifier TEXTURE = Unicopia.id("textures/particle/wind.png");
private final Trail trail;
@Nullable
private Entity target;
private int attachmentTicks;
private final Vec3d offset;
private final boolean passive;
public WindParticle(TargetBoundParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
super(world, x, y, z, velocityX, velocityY, velocityZ);
trail = new Trail(new Vec3d(x, y, z), 0.02F);
setMaxAge(300);
this.alpha = 0.15F;
this.velocityX = velocityX;
this.velocityY = velocityY;
this.velocityZ = velocityZ;
this.attachmentTicks = (int)world.random.nextTriangular(15, 12);
this.passive = effect.getTargetId() <= 0;
this.collidesWithWorld = false;
if (effect.getTargetId() > 0) {
this.target = world.getEntityById(effect.getTargetId());
}
offset = target == null ? Vec3d.ZERO : new Vec3d(x, y, z).subtract(target.getPos());
}
@Override
protected Identifier getTexture() {
return TEXTURE;
}
@Override
public boolean isAlive() {
return age < getMaxAge() && (!dead || !trail.getSegments().isEmpty());
}
@Override
protected void renderQuads(Tessellator te, BufferBuilder buffer, float x, float y, float z, float tickDelta) {
float alpha = this.alpha * (1 - (float)age / maxAge);
List<Trail.Segment> segments = trail.getSegments();
for (int i = 0; i < segments.size() - 1; i++) {
BezierSegment corners = segments.get(i).getPlane(segments.get(i + 1));
float scale = getScale(tickDelta);
corners.forEachCorner(corner -> {
corner.position().mul(scale).add(x, y, z);
});
renderQuad(te, buffer, corners.corners(), segments.get(i).getAlpha() * alpha, tickDelta);
}
}
@Override
public void tick() {
super.tick();
float animationFrame = age + MinecraftClient.getInstance().getTickDelta();
float sin = MathHelper.sin(animationFrame / 5F) * 0.1F;
float cos = MathHelper.cos(animationFrame / 10F) * 0.2F;
if (passive) {
trail.update(new Vec3d(x + cos, y + sin, z - cos));
} else {
if (target != null && target.isAlive()) {
trail.update(target.getPos().add(offset).add(cos, sin, -cos));
if (attachmentTicks > 0 && --attachmentTicks <= 0) {
target = null;
}
}
}
if (trail.tick()) {
markDead();
}
}
}

View file

@ -7,7 +7,8 @@ import com.minelittlepony.unicopia.compat.pehkui.PehkUtil;
import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.behaviour.Disguise; import com.minelittlepony.unicopia.entity.behaviour.Disguise;
import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance; import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance;
import com.minelittlepony.unicopia.entity.behaviour.FallingBlockBehaviour; import com.minelittlepony.unicopia.mixin.MixinBlockEntity;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
@ -86,7 +87,7 @@ class EntityDisguiseRenderer {
if (blockEntity != null) { if (blockEntity != null) {
BlockEntityRenderer<BlockEntity> r = MinecraftClient.getInstance().getBlockEntityRenderDispatcher().get(blockEntity); BlockEntityRenderer<BlockEntity> r = MinecraftClient.getInstance().getBlockEntityRenderDispatcher().get(blockEntity);
if (r != null) { if (r != null) {
((FallingBlockBehaviour.Positioned)blockEntity).setPos(e.getBlockPos()); ((MixinBlockEntity)blockEntity).setPos(e.getBlockPos());
blockEntity.setWorld(e.getWorld()); blockEntity.setWorld(e.getWorld());
matrices.push(); matrices.push();

View file

@ -12,8 +12,11 @@ public class Trail {
public final Vec3d pos; public final Vec3d pos;
public Trail(Vec3d pos) { private final float height;
public Trail(Vec3d pos, float height) {
this.pos = pos; this.pos = pos;
this.height = height;
segments.add(new Segment(pos)); segments.add(new Segment(pos));
} }
@ -61,7 +64,7 @@ public class Trail {
} }
public BezierSegment getPlane(Segment to) { public BezierSegment getPlane(Segment to) {
return new BezierSegment(offset, to.offset, 1); return new BezierSegment(offset, to.offset, height);
} }
} }
} }

View file

@ -59,7 +59,7 @@ public class CloudChestBlockEntityRenderer extends ChestBlockEntityRenderer<Ches
matrices.pop(); matrices.pop();
} }
private DoubleBlockProperties.PropertySource<? extends ChestBlockEntity> getProperties(BlockState state, ChestBlockEntity entity) { public static DoubleBlockProperties.PropertySource<? extends ChestBlockEntity> getProperties(BlockState state, ChestBlockEntity entity) {
return entity.getWorld() != null return entity.getWorld() != null
? ((AbstractChestBlock<?>)state.getBlock()).getBlockEntitySource(state, entity.getWorld(), entity.getPos(), true) ? ((AbstractChestBlock<?>)state.getBlock()).getBlockEntitySource(state, entity.getWorld(), entity.getPos(), true)
: DoubleBlockProperties.PropertyRetriever::getFallback; : DoubleBlockProperties.PropertyRetriever::getFallback;

View file

@ -9,10 +9,8 @@ import net.minecraft.client.render.OverlayTexture;
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.EntityRendererFactory; import net.minecraft.client.render.entity.EntityRendererFactory;
import net.minecraft.client.render.model.ModelLoader;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RotationAxis; import net.minecraft.util.math.RotationAxis;
public class CrystalShardsEntityRenderer extends EntityRenderer<CrystalShardsEntity> { public class CrystalShardsEntityRenderer extends EntityRenderer<CrystalShardsEntity> {
@ -30,6 +28,8 @@ public class CrystalShardsEntityRenderer extends EntityRenderer<CrystalShardsEnt
@Override @Override
public void render(CrystalShardsEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light) { public void render(CrystalShardsEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light) {
vertices = FloatingArtefactEntityRenderer.getDestructionOverlayProvider(matrices, vertices, 4, FloatingArtefactEntityRenderer.getDestructionStage(entity));
matrices.push(); matrices.push();
matrices.multiply(entity.getAttachmentFace().getRotationQuaternion()); matrices.multiply(entity.getAttachmentFace().getRotationQuaternion());
matrices.scale(-1, -1, 1); matrices.scale(-1, -1, 1);
@ -39,11 +39,6 @@ public class CrystalShardsEntityRenderer extends EntityRenderer<CrystalShardsEnt
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw)); matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw));
model.setAngles(entity, 0, 0, 0, 0, 0); model.setAngles(entity, 0, 0, 0, 0, 0);
int destructionStage = (int)(MathHelper.clamp(1F - (entity.getHealth() / entity.getMaxHealth()), 0F, 1F) * (ModelLoader.field_32983 - 1F));
vertices = FloatingArtefactEntityRenderer.getDestructionOverlayProvider(matrices, vertices, destructionStage);
model.render(matrices, vertices.getBuffer(model.getLayer(getTexture(entity))), light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1); model.render(matrices, vertices.getBuffer(model.getLayer(getTexture(entity))), light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1);
matrices.pop(); matrices.pop();
super.render(entity, yaw, tickDelta, matrices, vertices, light); super.render(entity, yaw, tickDelta, matrices, vertices, light);

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.client.render.entity;
import com.minelittlepony.unicopia.client.render.RenderLayers; import com.minelittlepony.unicopia.client.render.RenderLayers;
import com.minelittlepony.unicopia.entity.mob.FloatingArtefactEntity; import com.minelittlepony.unicopia.entity.mob.FloatingArtefactEntity;
import com.minelittlepony.unicopia.entity.mob.StationaryObjectEntity;
import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.item.UItems;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.OverlayTexture; import net.minecraft.client.render.OverlayTexture;
@ -15,6 +16,7 @@ import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.ModelLoader; import net.minecraft.client.render.model.ModelLoader;
import net.minecraft.client.render.model.json.ModelTransformationMode; 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.entity.LivingEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.screen.PlayerScreenHandler; import net.minecraft.screen.PlayerScreenHandler;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
@ -52,9 +54,7 @@ public class FloatingArtefactEntityRenderer extends EntityRenderer<FloatingArtef
matrices.translate(0, verticalOffset + variance * modelScaleY, 0); matrices.translate(0, verticalOffset + variance * modelScaleY, 0);
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(entity.getRotation(timeDelta))); matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(entity.getRotation(timeDelta)));
int destructionStage = (int)(MathHelper.clamp(1F - (entity.getHealth() / entity.getMaxHealth()), 0F, 1F) * (ModelLoader.field_32983 - 1F)); itemRenderer.renderItem(stack, ModelTransformationMode.GROUND, false, matrices, getDestructionOverlayProvider(matrices, vertices, 4F, getDestructionStage(entity)), lightUv, OverlayTexture.DEFAULT_UV, model);
itemRenderer.renderItem(stack, ModelTransformationMode.GROUND, false, matrices, getDestructionOverlayProvider(matrices, vertices, destructionStage), lightUv, OverlayTexture.DEFAULT_UV, model);
matrices.pop(); matrices.pop();
@ -66,16 +66,24 @@ public class FloatingArtefactEntityRenderer extends EntityRenderer<FloatingArtef
return PlayerScreenHandler.BLOCK_ATLAS_TEXTURE; return PlayerScreenHandler.BLOCK_ATLAS_TEXTURE;
} }
static VertexConsumerProvider getDestructionOverlayProvider(MatrixStack matrices, VertexConsumerProvider vertices, int stage) { static int getDestructionStage(StationaryObjectEntity entity) {
return (int)(MathHelper.clamp(1F - (entity.getHealth() / entity.getMaxHealth()), 0F, 1F) * (ModelLoader.field_32983 - 1F));
}
static int getDestructionStage(LivingEntity entity) {
return (int)(MathHelper.clamp(1F - (entity.getHealth() / entity.getMaxHealth()), 0F, 1F) * (ModelLoader.field_32983 - 1F));
}
static VertexConsumerProvider getDestructionOverlayProvider(MatrixStack matrices, VertexConsumerProvider vertices, float scale, int stage) {
if (stage <= 0) { if (stage <= 0) {
return vertices; return vertices;
} }
final MatrixStack.Entry entry = matrices.peek(); final MatrixStack.Entry entry = matrices.peek();
final OverlayVertexConsumer destructionOverlay = new OverlayVertexConsumer( final OverlayVertexConsumer destructionOverlay = new OverlayVertexConsumer(
MinecraftClient.getInstance().getBufferBuilders().getEffectVertexConsumers().getBuffer(RenderLayers.getCrumbling(stage)), MinecraftClient.getInstance().getBufferBuilders().getEffectVertexConsumers().getBuffer(RenderLayers.getCrumbling(MathHelper.clamp(stage, 0, ModelLoader.field_32983 - 1))),
entry.getPositionMatrix(), entry.getPositionMatrix(),
entry.getNormalMatrix(), entry.getNormalMatrix(),
4F scale
); );
return layer -> layer.hasCrumbling() ? VertexConsumers.union(destructionOverlay, vertices.getBuffer(layer)) : vertices.getBuffer(layer); return layer -> layer.hasCrumbling() ? VertexConsumers.union(destructionOverlay, vertices.getBuffer(layer)) : vertices.getBuffer(layer);
} }

View file

@ -0,0 +1,166 @@
package com.minelittlepony.unicopia.client.render.entity;
import com.minelittlepony.unicopia.entity.mob.MimicEntity;
import net.minecraft.block.ChestBlock;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.ChestBlockEntity;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.model.Dilation;
import net.minecraft.client.model.ModelData;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.model.ModelPartBuilder;
import net.minecraft.client.model.ModelPartData;
import net.minecraft.client.model.ModelTransform;
import net.minecraft.client.model.TexturedModelData;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.*;
import net.minecraft.client.render.entity.feature.FeatureRenderer;
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RotationAxis;
public class MimicEntityRenderer extends MobEntityRenderer<MimicEntity, MimicEntityRenderer.MimicModel> {
private static final Identifier TEXTURE = new Identifier("textures/entity/chest/normal.png");
public MimicEntityRenderer(EntityRendererFactory.Context context) {
super(context, new MimicModel(MimicModel.getTexturedModelData().createModel()), 0);
addFeature(new ChestFeature(this));
}
@Override
public void render(MimicEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light) {
matrices.push();
matrices.translate(0, 0.3F * entity.getPeekAmount(), 0);
float legAngle = entity.limbAnimator.getPos(tickDelta);
float legSpeed = entity.limbAnimator.getSpeed(tickDelta);
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(MathHelper.cos(legAngle * 0.6662F) * 1.4F * legSpeed * 10 * entity.getPeekAmount()));
super.render(entity, yaw, tickDelta, matrices,
FloatingArtefactEntityRenderer.getDestructionOverlayProvider(
matrices,
vertices,
1,
FloatingArtefactEntityRenderer.getDestructionStage(entity)
), light);
matrices.pop();
}
@Override
public Identifier getTexture(MimicEntity entity) {
return TEXTURE;
}
@Override
protected float getLyingAngle(MimicEntity entity) {
return 0;
}
static class ChestFeature extends FeatureRenderer<MimicEntity, MimicModel> {
public ChestFeature(FeatureRendererContext<MimicEntity, MimicModel> context) {
super(context);
}
@Override
public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, MimicEntity entity, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
BlockEntity tileData = entity.getChestData();
if (tileData != null) {
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(180));
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-entity.getPitch(tickDelta)));
matrices.push();
matrices.translate(-0.5, -1.5, -0.5);
MinecraftClient.getInstance().getBlockEntityRenderDispatcher().render(tileData, tickDelta, matrices, vertexConsumers);
matrices.pop();
}
}
}
static class MimicModel extends EntityModel<MimicEntity> {
private ModelPart part;
private ModelPart lid;
private ModelPart leftLeg;
private ModelPart rightLeg;
public MimicModel(ModelPart part) {
this.part = part;
this.lid = part.getChild("lid");
this.leftLeg = part.getChild("left_leg");
this.rightLeg = part.getChild("right_leg");
}
public static TexturedModelData getTexturedModelData() {
ModelData data = new ModelData();
ModelPartData root = data.getRoot();
ModelPartData lid = root.addChild("lid", ModelPartBuilder.create(), ModelTransform.of(0, 15, -7, 0, 0, -3.1416F));
lid.addChild("tongue", ModelPartBuilder.create()
.uv(11, 34).cuboid(-3, -11, 1, 6, 1, 8, Dilation.NONE), ModelTransform.of(0, 6, 9, 0.8F, 0, 0));
lid.addChild("upper_teeth", ModelPartBuilder.create()
//.uv(0, 0).cuboid(-7, 0, 0, 14, 5, 14, Dilation.NONE)
.uv(0, 0).cuboid(-1, -2, 12, 2, 4, 1, Dilation.NONE)
.uv(0, 0).cuboid(-4, -2, 12, 2, 4, 1, Dilation.NONE)
.uv(0, 0).cuboid(2, -2, 12, 2, 4, 1, Dilation.NONE), ModelTransform.NONE)
.addChild("cube_r1", ModelPartBuilder.create()
.uv(0, 0).cuboid(-5, -2, -6, 2, 4, 1, Dilation.NONE)
.uv(0, 0).cuboid(-8, -2, -6, 2, 4, 1, Dilation.NONE)
.uv(0, 0).cuboid(-11, -2, -6, 2, 4, 1, Dilation.NONE)
.uv(0, 0).cuboid(-5, -2, 5, 2, 4, 1, Dilation.NONE)
.uv(0, 0).cuboid(-8, -2, 5, 2, 4, 1, Dilation.NONE)
.uv(0, 0).cuboid(-11, -2, 5, 2, 4, 1, Dilation.NONE), ModelTransform.of(0, 0, 0, 0, 1.5708F, 0));
root.addChild("lower_teeth", ModelPartBuilder.create()
.uv(0, 0).cuboid(-1, -1, 12, 2, 4, 1, Dilation.NONE)
.uv(0, 0).cuboid(-4, -1, 12, 2, 4, 1, Dilation.NONE)
.uv(0, 0).cuboid(2, -1, 12, 2, 4, 1, Dilation.NONE), ModelTransform.pivot(0, 13, -7))
.addChild("cube_r2", ModelPartBuilder.create()
.uv(0, 0).cuboid(-6, -1, -6, 2, 4, 1, Dilation.NONE)
.uv(0, 0).cuboid(-9, -1, -6, 2, 4, 1, Dilation.NONE)
.uv(0, 0).cuboid(-12, -1, -6, 2, 4, 1, Dilation.NONE)
.uv(0, 0).cuboid(-6, -1, 5, 2, 4, 1, Dilation.NONE)
.uv(0, 0).cuboid(-9, -1, 5, 2, 4, 1, Dilation.NONE)
.uv(0, 0).cuboid(-12, -1, 5, 2, 4, 1, Dilation.NONE), ModelTransform.of(0, 0, 0, 0, 1.5708F, 0));
root.addChild("right_leg", ModelPartBuilder.create()
.uv(7, 30).cuboid(-2.5F, -1.5F, -3.5F, 5, 7, 6, Dilation.NONE), ModelTransform.pivot(3.5F, 23.5F, 0));
root.addChild("left_leg", ModelPartBuilder.create()
.uv(7, 30).mirrored().cuboid(-9.5F, -1.5F, -3.5F, 5, 7, 6, Dilation.NONE), ModelTransform.pivot(3.5F, 23.5F, 0));
return TexturedModelData.of(data, 64, 64);
}
@Override
public void animateModel(MimicEntity entity, float limbAngle, float limbDistance, float tickDelta) {
ChestBlockEntity tileData = entity.getChestData();
if (tileData != null) {
var properties = CloudChestBlockEntityRenderer.getProperties(tileData.getCachedState(), tileData);
float progress = 1 - (float)Math.pow(1 - properties.apply(ChestBlock.getAnimationProgressRetriever(tileData)).get(tickDelta), 3);
lid.pitch = -(progress * 1.5707964f);
} else {
lid.pitch = 0;
}
part.yaw = MathHelper.RADIANS_PER_DEGREE * 180;
part.pitch = -entity.getPitch(tickDelta) * MathHelper.RADIANS_PER_DEGREE;
}
@Override
public void setAngles(MimicEntity entity, float limbAngle, float limbDistance, float animationProgress, float headYaw, float headPitch) {
rightLeg.resetTransform();
leftLeg.resetTransform();
rightLeg.pitch = MathHelper.cos(limbAngle * 0.6662F) * 1.4F * limbDistance;
leftLeg.pitch = MathHelper.cos(limbAngle * 0.6662F + (float) Math.PI) * 1.4F * limbDistance;
float revealPercentage = entity.getPeekAmount();
float velocy = (1 - revealPercentage) * -10F;
rightLeg.pivotY += velocy;
leftLeg.pivotY += velocy;
rightLeg.visible = revealPercentage > 0.2F;
leftLeg.visible = revealPercentage > 0.2F;
}
@Override
public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha) {
part.render(matrices, vertices, light, overlay);
}
}
}

View file

@ -81,6 +81,10 @@ public class UBlockTagProvider extends FabricTagProvider.BlockTagProvider {
.forceAddTag(UTags.Blocks.JARS) .forceAddTag(UTags.Blocks.JARS)
.add(Blocks.VINE, Blocks.LILY_PAD); .add(Blocks.VINE, Blocks.LILY_PAD);
getOrCreateTagBuilder(UTags.Blocks.MIMIC_CHESTS).add(
Blocks.CHEST, Blocks.TRAPPED_CHEST, UBlocks.CLOUD_CHEST
);
getOrCreateTagBuilder(UTags.Blocks.INTERESTING).add( getOrCreateTagBuilder(UTags.Blocks.INTERESTING).add(
Blocks.SEA_LANTERN, Blocks.ENDER_CHEST, Blocks.END_PORTAL_FRAME, Blocks.SEA_LANTERN, Blocks.ENDER_CHEST, Blocks.END_PORTAL_FRAME,
Blocks.JUKEBOX, Blocks.SPAWNER Blocks.JUKEBOX, Blocks.SPAWNER

View file

@ -6,6 +6,7 @@ import java.util.Optional;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.mixin.MixinBlockEntity;
import com.minelittlepony.unicopia.mixin.MixinFallingBlock; import com.minelittlepony.unicopia.mixin.MixinFallingBlock;
import com.minelittlepony.unicopia.mixin.MixinFallingBlockEntity; import com.minelittlepony.unicopia.mixin.MixinFallingBlockEntity;
import com.minelittlepony.unicopia.util.Tickable; import com.minelittlepony.unicopia.util.Tickable;
@ -25,7 +26,6 @@ import net.minecraft.entity.FallingBlockEntity;
import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.damage.DamageSource;
import net.minecraft.state.property.Properties; import net.minecraft.state.property.Properties;
import net.minecraft.registry.tag.BlockTags; import net.minecraft.registry.tag.BlockTags;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@ -118,13 +118,9 @@ public class FallingBlockBehaviour extends EntityBehaviour<FallingBlockEntity> {
} }
be.setWorld(entity.getWorld()); be.setWorld(entity.getWorld());
((Positioned)be).setPos(entity.getBlockPos()); ((MixinBlockEntity)be).setPos(entity.getBlockPos());
ceb.tick(); ceb.tick();
be.setWorld(null); be.setWorld(null);
} }
} }
public interface Positioned {
void setPos(BlockPos pos);
}
} }

View file

@ -0,0 +1,403 @@
package com.minelittlepony.unicopia.entity.mob;
import java.util.HashSet;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.item.enchantment.EnchantmentUtil;
import com.minelittlepony.unicopia.mixin.MixinBlockEntity;
import com.minelittlepony.unicopia.util.InventoryUtil;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.ChestBlock;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.ChestBlockEntity;
import net.minecraft.block.enums.ChestType;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.goal.MeleeAttackGoal;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.entity.mob.PathAwareEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.SimpleInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.loot.LootTables;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtOps;
import net.minecraft.screen.GenericContainerScreenHandler;
import net.minecraft.screen.NamedScreenHandlerFactory;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.sound.SoundEvents;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.Identifier;
import net.minecraft.util.ItemScatterer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
public class MimicEntity extends PathAwareEntity {
static final byte OPEN_MOUTH = (byte)200;
static final byte CLOSE_MOUTH = (byte)201;
static final TrackedData<NbtCompound> CHEST_DATA = DataTracker.registerData(MimicEntity.class, TrackedDataHandlerRegistry.NBT_COMPOUND);
static final TrackedData<Boolean> MOUTH_OPEN = DataTracker.registerData(MimicEntity.class, TrackedDataHandlerRegistry.BOOLEAN);
@Nullable
private ChestBlockEntity chestData;
private int openTicks;
private final Set<PlayerEntity> observingPlayers = new HashSet<>();
public static boolean shouldConvert(World world, BlockPos pos, PlayerEntity player, Identifier lootTable) {
if (!shouldGenerateMimic(lootTable)
|| !world.getBlockState(pos).isIn(UTags.Blocks.MIMIC_CHESTS)
|| !(world.getBlockEntity(pos) instanceof ChestBlockEntity be)
|| be.getCachedState().getOrEmpty(ChestBlock.CHEST_TYPE).orElse(ChestType.SINGLE) != ChestType.SINGLE) {
return false;
}
int difficulty = world.getDifficulty().ordinal() - 1;
float threshold = 0.35F * ((EnchantmentUtil.getLuck(0, player) / 20F) + 0.5F);
return difficulty > 0 && world.random.nextFloat() < (difficulty / 3F) * threshold;
}
@SuppressWarnings("deprecation")
@Nullable
public static MimicEntity spawnFromChest(World world, BlockPos pos) {
if (!(world.getBlockEntity(pos) instanceof ChestBlockEntity be)) {
return null;
}
world.removeBlockEntity(pos);
world.setBlockState(pos, Blocks.AIR.getDefaultState());
MimicEntity mimic = UEntities.MIMIC.create(world);
BlockState state = be.getCachedState();
Direction facing = state.getOrEmpty(ChestBlock.FACING).orElse(null);
float yaw = facing.asRotation();
be.setCachedState(be.getCachedState().getBlock().getDefaultState());
mimic.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, yaw, 0);
mimic.setHeadYaw(yaw);
mimic.setBodyYaw(yaw);
mimic.setYaw(yaw);
mimic.setChest(be);
world.spawnEntity(mimic);
return mimic;
}
public static boolean shouldGenerateMimic(@Nullable Identifier lootTable) {
return lootTable != null
&& lootTable.getPath().indexOf("village") == -1
&& lootTable.getPath().indexOf("bastion") == -1
&& lootTable.getPath().indexOf("underwater") == -1
&& lootTable.getPath().indexOf("shipwreck") == -1;
}
MimicEntity(EntityType<? extends MimicEntity> type, World world) {
super(type, world);
ignoreCameraFrustum = true;
}
@Override
protected void initDataTracker() {
super.initDataTracker();
dataTracker.startTracking(CHEST_DATA, new NbtCompound());
dataTracker.startTracking(MOUTH_OPEN, false);
}
@Override
public boolean isCollidable() {
return isAlive();
}
@Override
public boolean canBreatheInWater() {
return true;
}
@Nullable
@Override
public ItemStack getPickBlockStack() {
Item item = chestData.getCachedState().getBlock().asItem();
return item == Items.AIR ? null : item.getDefaultStack();
}
@Override
protected void initGoals() {
goalSelector.add(2, new AttackGoal(this, 0.6F, false));
}
public void setChest(ChestBlock chest) {
if (chest.createBlockEntity(getBlockPos(), chest.getDefaultState()) instanceof ChestBlockEntity be) {
setChest(be);
be.setLootTable(LootTables.ABANDONED_MINESHAFT_CHEST, getWorld().getRandom().nextLong());
if (!getWorld().isClient) {
dataTracker.set(CHEST_DATA, writeChestData(chestData));
}
}
}
public void setChest(ChestBlockEntity chestData) {
this.chestData = chestData;
((MimicGeneratable)chestData).setAllowMimics(false);
chestData.setWorld(getWorld());
if (!getWorld().isClient) {
dataTracker.set(CHEST_DATA, writeChestData(chestData));
}
}
@Nullable
public ChestBlockEntity getChestData() {
return chestData;
}
public boolean isMouthOpen() {
return dataTracker.get(MOUTH_OPEN);
}
public void setMouthOpen(boolean mouthOpen) {
if (mouthOpen == isMouthOpen()) {
return;
}
playSound(mouthOpen ? SoundEvents.BLOCK_CHEST_OPEN : SoundEvents.BLOCK_CHEST_CLOSE, 0.5F, 1F);
dataTracker.set(MOUTH_OPEN, mouthOpen);
if (chestData != null) {
chestData.onSyncedBlockEvent(1, mouthOpen ? 1 : 0);
}
}
public void playChompAnimation() {
openTicks = 5;
setMouthOpen(true);
}
public float getPeekAmount() {
return MathHelper.clamp((float)getVelocity().lengthSquared() * 50F, 0, 1);
}
@Override
public void tick() {
super.tick();
if (!getWorld().isClient) {
if (age < 12 || (getTarget() == null && this.lastAttackedTicks < age - 30)) {
BlockPos pos = getBlockPos();
setPosition(
pos.getX() + 0.5,
getY(),
pos.getZ() + 0.5
);
setBodyYaw(MathHelper.floor(getBodyYaw() / 90) * 90);
setYaw(MathHelper.floor(getYaw() / 90) * 90);
setHeadYaw(MathHelper.floor(getHeadYaw() / 90) * 90);
if (getHealth() < getMaxHealth() && getWorld().random.nextInt(20) == 0) {
heal(1);
} else if (age % 150 == 0 && chestData != null && !isMouthOpen()) {
if (getWorld().getClosestPlayer(this, 15) == null) {
getWorld().setBlockState(getBlockPos(), chestData.getCachedState().withIfExists(ChestBlock.FACING, getHorizontalFacing()));
if (getWorld().getBlockEntity(getBlockPos()) instanceof ChestBlockEntity be) {
InventoryUtil.copyInto(chestData, be);
((MimicGeneratable)be).setMimic(true);
discard();
}
}
}
}
if (!observingPlayers.isEmpty()) {
setMouthOpen(true);
}
}
if (chestData == null) {
setChest((ChestBlock)Blocks.CHEST);
}
if (getWorld().isClient) {
((MixinBlockEntity)chestData).setPos(getBlockPos());
ChestBlockEntity.clientTick(getWorld(), getBlockPos(), chestData.getCachedState(), chestData);
}
if (openTicks > 0 && --openTicks <= 0) {
setMouthOpen(false);
}
if (getTarget() != null) {
if (openTicks <= 0 && age % 20 == 0) {
playChompAnimation();
}
}
}
@Override
protected ActionResult interactMob(PlayerEntity player, Hand hand) {
if (getTarget() == null && chestData != null) {
player.openHandledScreen(new NamedScreenHandlerFactory() {
@Override
public Text getDisplayName() {
return chestData.getDisplayName();
}
@Override
public ScreenHandler createMenu(int syncId, PlayerInventory inv, PlayerEntity player) {
return createScreenHandler(syncId, inv, player);
}
});
return ActionResult.SUCCESS;
}
return ActionResult.PASS;
}
public ScreenHandler createScreenHandler(int syncId, PlayerInventory inv, PlayerEntity player) {
chestData.checkLootInteraction(player);
setChest(chestData);
var inventory = InventoryUtil.copyInto(chestData, new SimpleInventory(chestData.size()) {
@Override
public void onOpen(PlayerEntity player) {
observingPlayers.add(player);
//setMouthOpen(true);
}
@Override
public void onClose(PlayerEntity player) {
observingPlayers.remove(player);
setMouthOpen(!observingPlayers.isEmpty());
}
});
inventory.addListener(sender -> {
if (InventoryUtil.contentEquals(inventory, chestData)) {
return;
}
observingPlayers.clear();
playChompAnimation();
setTarget(player);
if (player instanceof ServerPlayerEntity spe) {
spe.closeHandledScreen();
}
});
return GenericContainerScreenHandler.createGeneric9x3(syncId, inv, inventory);
}
@Override
public void setAttacking(boolean attacking) {
super.setAttacking(attacking);
if (attacking) {
playChompAnimation();
}
}
@Override
protected void playHurtSound(DamageSource source) {
playChompAnimation();
if (source.getAttacker() instanceof LivingEntity l) {
setTarget(l);
}
}
@Override
public void onTrackedDataSet(TrackedData<?> data) {
super.onTrackedDataSet(data);
if (CHEST_DATA.equals(data) && getWorld().isClient) {
setChest(readChestData(dataTracker.get(CHEST_DATA)));
} else if (MOUTH_OPEN.equals(data)) {
if (chestData != null) {
chestData.onSyncedBlockEvent(1, isMouthOpen() ? 1 : 0);
}
}
}
@Override
public void readCustomDataFromNbt(NbtCompound nbt) {
super.readCustomDataFromNbt(nbt);
if (nbt.contains("chest", NbtElement.COMPOUND_TYPE)) {
chestData = readChestData(nbt.getCompound("chest"));
} else {
chestData = null;
}
}
@Nullable
private ChestBlockEntity readChestData(NbtCompound nbt) {
BlockState state = BlockState.CODEC.decode(NbtOps.INSTANCE, nbt.getCompound("state")).result().get().getFirst();
if (BlockEntity.createFromNbt(getBlockPos(), state, nbt.getCompound("data")) instanceof ChestBlockEntity data) {
data.setWorld(getWorld());
((MimicGeneratable)data).setAllowMimics(false);
return data;
}
return null;
}
@Override
public void writeCustomDataToNbt(NbtCompound nbt) {
super.writeCustomDataToNbt(nbt);
if (chestData != null) {
nbt.put("chest", writeChestData(chestData));
}
}
@Nullable
private NbtCompound writeChestData(ChestBlockEntity chestData) {
NbtCompound chest = new NbtCompound();
chest.put("data", chestData.createNbtWithId());
chest.put("state", BlockState.CODEC.encode(chestData.getCachedState(), NbtOps.INSTANCE, new NbtCompound()).result().get());
return chest;
}
@Override
protected void dropLoot(DamageSource damageSource, boolean causedByPlayer) {
if (chestData != null) {
ItemScatterer.spawn(getWorld(), this, chestData);
ItemScatterer.spawn(getWorld(), getX(), getY(), getZ(), chestData.getCachedState().getBlock().asItem().getDefaultStack());
}
}
public class AttackGoal extends MeleeAttackGoal {
private int ticks;
public AttackGoal(PathAwareEntity mob, double speed, boolean pauseWhenMobIdle) {
super(mob, speed, pauseWhenMobIdle);
}
@Override
public void start() {
super.start();
this.ticks = 0;
}
@Override
public void stop() {
super.stop();
setAttacking(false);
}
@Override
public void tick() {
super.tick();
++ticks;
if (ticks >= 5 && getCooldown() < getMaxCooldown() / 2) {
setAttacking(true);
} else {
setAttacking(false);
}
}
}
public interface MimicGeneratable {
void setAllowMimics(boolean allowMimics);
void setMimic(boolean mimic);
}
}

View file

@ -89,6 +89,10 @@ public interface UEntities {
.fireImmune() .fireImmune()
.spawnableFarFromPlayer() .spawnableFarFromPlayer()
.dimensions(EntityDimensions.fixed(1, 2))); .dimensions(EntityDimensions.fixed(1, 2)));
EntityType<MimicEntity> MIMIC = register("mimic", FabricEntityTypeBuilder.create(SpawnGroup.MONSTER, MimicEntity::new)
.fireImmune()
//.disableSummon()
.dimensions(EntityDimensions.changing(0.875F, 0.875F)));
static <T extends Entity> EntityType<T> register(String name, FabricEntityTypeBuilder<T> builder) { static <T extends Entity> EntityType<T> register(String name, FabricEntityTypeBuilder<T> builder) {
EntityType<T> type = builder.build(); EntityType<T> type = builder.build();
@ -105,6 +109,7 @@ public interface UEntities {
FabricDefaultAttributeRegistry.register(LOOT_BUG, LootBugEntity.createSilverfishAttributes()); FabricDefaultAttributeRegistry.register(LOOT_BUG, LootBugEntity.createSilverfishAttributes());
FabricDefaultAttributeRegistry.register(IGNOMINIOUS_BULB, IgnominiousBulbEntity.createMobAttributes()); FabricDefaultAttributeRegistry.register(IGNOMINIOUS_BULB, IgnominiousBulbEntity.createMobAttributes());
FabricDefaultAttributeRegistry.register(SPECTER, SpecterEntity.createAttributes()); FabricDefaultAttributeRegistry.register(SPECTER, SpecterEntity.createAttributes());
FabricDefaultAttributeRegistry.register(MIMIC, MimicEntity.createMobAttributes());
if (!Unicopia.getConfig().disableButterflySpawning.get()) { if (!Unicopia.getConfig().disableButterflySpawning.get()) {
final Predicate<BiomeSelectionContext> butterflySpawnable = BiomeSelectors.foundInOverworld() final Predicate<BiomeSelectionContext> butterflySpawnable = BiomeSelectors.foundInOverworld()

View file

@ -649,6 +649,14 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
velocity.x += - forward * MathHelper.sin(entity.getYaw() * 0.017453292F); velocity.x += - forward * MathHelper.sin(entity.getYaw() * 0.017453292F);
velocity.z += forward * MathHelper.cos(entity.getYaw() * 0.017453292F); velocity.z += forward * MathHelper.cos(entity.getYaw() * 0.017453292F);
if (pony.isClient()) {
float effectChance = 1F - (float)(MathHelper.clamp(velocity.horizontalLengthSquared(), 0, 1));
if (entity.getWorld().random.nextInt(1 + (int)(120 * effectChance)) == 0) {
pony.spawnParticles(new TargetBoundParticleEffect(UParticles.WIND, pony.asEntity()), 3);
}
}
if (entity.getWorld().hasRain(entity.getBlockPos())) { if (entity.getWorld().hasRain(entity.getBlockPos())) {
applyTurbulance(velocity); applyTurbulance(velocity);
} else { } else {
@ -696,7 +704,10 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
if (manualFlap) { if (manualFlap) {
descentRate -= 0.5; descentRate -= 0.5;
} else { } else {
descentRate = Math.max(0, descentRate / 2); descentRate *= 0.25F;
if (velocity.y < 0) {
velocity.y *= 0.6F;
}
} }
} }
} }
@ -710,9 +721,11 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
Vec3d direction = entity.getRotationVec(1).normalize().multiply(thrustStrength); Vec3d direction = entity.getRotationVec(1).normalize().multiply(thrustStrength);
if (hovering) { if (hovering || !manualFlap) {
if (entity.isSneaking()) { if (entity.isSneaking()) {
velocity.y -= 0.2F; velocity.y -= 0.006F;
} else {
velocity.y *= 1 - thrustScale;
} }
} else { } else {
velocity.x += direction.x * 1.3F; velocity.x += direction.x * 1.3F;
@ -815,6 +828,8 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
pony.updateVelocity(); pony.updateVelocity();
pony.spawnParticles(new TargetBoundParticleEffect(UParticles.WIND, pony.asEntity()), 4);
if (isFlying()) { if (isFlying()) {
playSound(USounds.ENTITY_PLAYER_PEGASUS_DASH, 1, 1); playSound(USounds.ENTITY_PLAYER_PEGASUS_DASH, 1, 1);
} else { } else {

View file

@ -1,23 +1,15 @@
package com.minelittlepony.unicopia.mixin; package com.minelittlepony.unicopia.mixin;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.gen.Accessor;
import com.minelittlepony.unicopia.entity.behaviour.FallingBlockBehaviour;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@Mixin(BlockEntity.class) @Mixin(BlockEntity.class)
abstract class MixinBlockEntity implements FallingBlockBehaviour.Positioned { public interface MixinBlockEntity {
@Shadow
@Mutable @Mutable
private @Final BlockPos pos; @Accessor("pos")
void setPos(BlockPos pos);
@Override
public void setPos(BlockPos pos) {
this.pos = pos;
}
} }

View file

@ -0,0 +1,79 @@
package com.minelittlepony.unicopia.mixin;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.*;
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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.entity.mob.MimicEntity;
import net.minecraft.block.entity.LockableContainerBlockEntity;
import net.minecraft.block.entity.LootableContainerBlockEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.util.Identifier;
@Mixin(LootableContainerBlockEntity.class)
abstract class MixinLootableContainerBlockEntity extends LockableContainerBlockEntity implements MimicEntity.MimicGeneratable {
private Identifier mimicLootTable;
private boolean allowMimics = true;
private boolean isMimic;
@Shadow
@Nullable
private Identifier lootTableId;
MixinLootableContainerBlockEntity() { super(null, null, null); }
@Inject(method = "deserializeLootTable", at = @At("HEAD"))
private void deserializeMimic(NbtCompound nbt, CallbackInfoReturnable<Boolean> info) {
isMimic = nbt.getBoolean("mimic");
}
@Inject(method = "serializeLootTable", at = @At("HEAD"))
private void serializeMimic(NbtCompound nbt, CallbackInfoReturnable<Boolean> info) {
nbt.putBoolean("mimic", isMimic);
}
@Override
public void setAllowMimics(boolean allowMimics) {
this.allowMimics = allowMimics;
this.isMimic &= allowMimics;
markDirty();
}
@Override
public void setMimic(boolean mimic) {
isMimic = mimic;
markDirty();
}
@Inject(method = "checkLootInteraction", at = @At("HEAD"))
private void onCheckLootInteraction(@Nullable PlayerEntity player, CallbackInfo info) {
if (player != null && allowMimics && lootTableId != null) {
mimicLootTable = lootTableId;
}
}
@Inject(
method = "createMenu",
at = @At(
value = "INVOKE",
target = "net/minecraft/block/entity/LootableContainerBlockEntity.checkLootInteraction(Lnet/minecraft/entity/player/PlayerEntity;)V",
shift = Shift.AFTER
), cancellable = true)
private void onCreateMenu(int syncId, PlayerInventory playerInventory, PlayerEntity player, CallbackInfoReturnable<ScreenHandler> info) {
if (player != null && (isMimic || (allowMimics && MimicEntity.shouldConvert(player.getWorld(), getPos(), player, mimicLootTable)))) {
var mimic = MimicEntity.spawnFromChest(player.getWorld(), getPos());
if (mimic != null) {
info.setReturnValue(mimic.createScreenHandler(syncId, playerInventory, player));
}
mimicLootTable = null;
}
}
}

View file

@ -26,10 +26,7 @@ public record FollowingParticleEffect (
this(type, this(type,
new WeakTarget(reader), new WeakTarget(reader),
ParticleFactoryHelper.readFloat(reader), ParticleFactoryHelper.readFloat(reader),
ParticleFactoryHelper.readOptional(reader, r -> { ParticleFactoryHelper.readOptional(reader, r -> ParticleFactoryHelper.read(r)));
r.expect(' ');
return ParticleFactoryHelper.read(r);
}));
} }
protected FollowingParticleEffect(ParticleType<FollowingParticleEffect> type, PacketByteBuf buf) { protected FollowingParticleEffect(ParticleType<FollowingParticleEffect> type, PacketByteBuf buf) {

View file

@ -14,17 +14,11 @@ import net.minecraft.registry.Registries;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
public interface ParticleFactoryHelper { public interface ParticleFactoryHelper {
@SuppressWarnings("deprecation") @SuppressWarnings({ "deprecation", "unchecked", "rawtypes" })
PacketCodec<ParticleEffect> PARTICLE_EFFECT_CODEC = new PacketCodec<>( PacketCodec<ParticleEffect> PARTICLE_EFFECT_CODEC = PacketCodec.ofRegistry(Registries.PARTICLE_TYPE).andThen(
buf -> { (buf, type) -> type.getParametersFactory().read((ParticleType) type, buf),
@SuppressWarnings("unchecked") ParticleEffect::getType,
ParticleType<ParticleEffect> type = (ParticleType<ParticleEffect>)Registries.PARTICLE_TYPE.get(buf.readInt()); (buf, effect) -> effect.write(buf)
return type.getParametersFactory().read(type, buf);
},
(buf, effect) -> {
buf.writeInt(Registries.PARTICLE_TYPE.getRawId(effect.getType()));
effect.write(buf);
}
); );
PacketCodec<Optional<ParticleEffect>> OPTIONAL_PARTICLE_EFFECT_CODEC = PARTICLE_EFFECT_CODEC.asOptional(); PacketCodec<Optional<ParticleEffect>> OPTIONAL_PARTICLE_EFFECT_CODEC = PARTICLE_EFFECT_CODEC.asOptional();
PacketCodec<Vec3d> VECTOR_CODEC = new PacketCodec<>( PacketCodec<Vec3d> VECTOR_CODEC = new PacketCodec<>(
@ -39,6 +33,7 @@ public interface ParticleFactoryHelper {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static <T extends ParticleEffect> T read(StringReader reader) throws CommandSyntaxException { static <T extends ParticleEffect> T read(StringReader reader) throws CommandSyntaxException {
reader.expect(' ');
return (T)ParticleEffectArgumentType.readParameters(reader, Registries.PARTICLE_TYPE.getReadOnlyWrapper()); return (T)ParticleEffectArgumentType.readParameters(reader, Registries.PARTICLE_TYPE.getReadOnlyWrapper());
} }

View file

@ -0,0 +1,48 @@
package com.minelittlepony.unicopia.particle;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.particle.ParticleType;
public record SpiralParticleEffect(
WeakTarget centerPoint,
float angularVelocity,
ParticleEffect effect
) implements ParticleEffect {
@SuppressWarnings("deprecation")
public static final Factory<SpiralParticleEffect> FACTORY = ParticleFactoryHelper.of(SpiralParticleEffect::new, SpiralParticleEffect::new);
protected SpiralParticleEffect(ParticleType<SpiralParticleEffect> type, StringReader reader) throws CommandSyntaxException {
this(new WeakTarget(reader),
ParticleFactoryHelper.readFloat(reader),
ParticleFactoryHelper.read(reader)
);
}
protected SpiralParticleEffect(ParticleType<SpiralParticleEffect> type, PacketByteBuf buf) {
this(new WeakTarget(buf),
buf.readFloat(),
ParticleFactoryHelper.PARTICLE_EFFECT_CODEC.read(buf)
);
}
@Override
public ParticleType<?> getType() {
return UParticles.SPIRAL;
}
@Override
public void write(PacketByteBuf buffer) {
centerPoint.write(buffer);
buffer.writeFloat(angularVelocity);
ParticleFactoryHelper.PARTICLE_EFFECT_CODEC.write(buffer, effect);
}
@Override
public String asString() {
return null;
}
}

View file

@ -3,6 +3,8 @@ package com.minelittlepony.unicopia.particle;
import java.util.Locale; import java.util.Locale;
import org.jetbrains.annotations.Nullable;
import com.mojang.brigadier.StringReader; import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
@ -29,9 +31,9 @@ public class TargetBoundParticleEffect implements ParticleEffect {
this.targetId = buf.readInt(); this.targetId = buf.readInt();
} }
public TargetBoundParticleEffect(ParticleType<TargetBoundParticleEffect> type, Entity target) { public TargetBoundParticleEffect(ParticleType<TargetBoundParticleEffect> type, @Nullable Entity target) {
this.type = type; this.type = type;
this.targetId = target.getId(); this.targetId = target == null ? -1 : target.getId();
} }
public int getTargetId() { public int getTargetId() {

View file

@ -19,6 +19,7 @@ public interface UParticles {
ParticleType<OrientedBillboardParticleEffect> RAINBOOM_RING = register("rainboom_ring", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY)); ParticleType<OrientedBillboardParticleEffect> RAINBOOM_RING = register("rainboom_ring", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY));
ParticleType<TargetBoundParticleEffect> RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.complex(TargetBoundParticleEffect.FACTORY)); ParticleType<TargetBoundParticleEffect> RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.complex(TargetBoundParticleEffect.FACTORY));
ParticleType<TargetBoundParticleEffect> WIND = register("wind", FabricParticleTypes.complex(TargetBoundParticleEffect.FACTORY));
DefaultParticleType RAIN_DROPS = register("rain_drops", FabricParticleTypes.simple()); DefaultParticleType RAIN_DROPS = register("rain_drops", FabricParticleTypes.simple());
@ -26,6 +27,7 @@ public interface UParticles {
ParticleType<SphereParticleEffect> DISK = register("disk", FabricParticleTypes.complex(true, SphereParticleEffect.FACTORY)); ParticleType<SphereParticleEffect> DISK = register("disk", FabricParticleTypes.complex(true, SphereParticleEffect.FACTORY));
ParticleType<FollowingParticleEffect> HEALTH_DRAIN = register("health_drain", FabricParticleTypes.complex(true, FollowingParticleEffect.FACTORY)); ParticleType<FollowingParticleEffect> HEALTH_DRAIN = register("health_drain", FabricParticleTypes.complex(true, FollowingParticleEffect.FACTORY));
ParticleType<SpiralParticleEffect> SPIRAL = register("spiral", FabricParticleTypes.complex(true, SpiralParticleEffect.FACTORY));
DefaultParticleType GROUND_POUND = register("ground_pound", FabricParticleTypes.simple()); DefaultParticleType GROUND_POUND = register("ground_pound", FabricParticleTypes.simple());
DefaultParticleType CLOUDS_ESCAPING = register("clouds_escaping", FabricParticleTypes.simple(true)); DefaultParticleType CLOUDS_ESCAPING = register("clouds_escaping", FabricParticleTypes.simple(true));

View file

@ -114,8 +114,12 @@ public class WeatherConditions extends PersistentState implements Tickable {
return MathHelper.lerp(interpolation / (float)maxInterpolation, prevWindYaw, windYaw); return MathHelper.lerp(interpolation / (float)maxInterpolation, prevWindYaw, windYaw);
} }
public int getWindInterpolation() {
return interpolation;
}
public Vec3d getWindDirection() { public Vec3d getWindDirection() {
return Vec3d.fromPolar(0, windYaw).normalize(); return Vec3d.fromPolar(0, getWindYaw()).normalize();
} }
@Override @Override

View file

@ -1,5 +1,6 @@
package com.minelittlepony.unicopia.util; package com.minelittlepony.unicopia.util;
import java.util.function.Function;
import java.util.stream.Stream; import java.util.stream.Stream;
import net.minecraft.inventory.Inventory; import net.minecraft.inventory.Inventory;
@ -22,4 +23,27 @@ public interface InventoryUtil {
} }
return -1; return -1;
} }
static boolean contentEquals(Inventory a, Inventory b) {
if (a.size() != b.size()) {
return false;
}
for (int i = 0; i < a.size(); i++) {
if (!ItemStack.areEqual(a.getStack(i), b.getStack(i))) {
return false;
}
}
return true;
}
static <I extends Inventory> I copy(Inventory from, Function<Integer, I> factory) {
return copyInto(from, factory.apply(from.size()));
}
static <I extends Inventory> I copyInto(Inventory from, I into) {
for (int i = 0; i < from.size(); i++) {
into.setStack(i, from.getStack(i).copy());
}
return into;
}
} }

View file

@ -1,10 +1,33 @@
package com.minelittlepony.unicopia.util.serialization; package com.minelittlepony.unicopia.util.serialization;
import java.util.Optional; import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import net.minecraft.network.PacketByteBuf; import net.minecraft.network.PacketByteBuf;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
public record PacketCodec<T>(PacketByteBuf.PacketReader<T> reader, PacketByteBuf.PacketWriter<T> writer) { public record PacketCodec<T>(PacketByteBuf.PacketReader<T> reader, PacketByteBuf.PacketWriter<T> writer) {
public static final PacketCodec<Float> FLOAT = new PacketCodec<>(PacketByteBuf::readFloat, PacketByteBuf::writeFloat);
public static final PacketCodec<Integer> INT = new PacketCodec<>(PacketByteBuf::readInt, PacketByteBuf::writeInt);
public static final PacketCodec<Byte> BYTE = new PacketCodec<>(PacketByteBuf::readByte, (b, v) -> b.writeByte(v));
public static final PacketCodec<Long> LONG = new PacketCodec<>(PacketByteBuf::readLong, PacketByteBuf::writeLong);
public static final PacketCodec<String> STRING = new PacketCodec<>(PacketByteBuf::readString, PacketByteBuf::writeString);
public static final PacketCodec<Identifier> IDENTIFIER = STRING.xMap(Identifier::new, Identifier::toString);
public static final <T> PacketCodec<T> ofRegistry(Registry<T> registry) {
return INT.xMap(registry::get, registry::getRawId);
}
public static final <T extends Enum<T>> PacketCodec<T> ofEnum(Supplier<T[]> valuesGetter) {
final T[] values = valuesGetter.get();
return INT.xMap(id -> values[MathHelper.clamp(id, 0, values.length)], Enum::ordinal);
}
public T read(PacketByteBuf buf) { public T read(PacketByteBuf buf) {
return reader().apply(buf); return reader().apply(buf);
@ -17,4 +40,17 @@ public record PacketCodec<T>(PacketByteBuf.PacketReader<T> reader, PacketByteBuf
public PacketCodec<Optional<T>> asOptional() { public PacketCodec<Optional<T>> asOptional() {
return new PacketCodec<>(buf -> buf.readOptional(reader), (buf, v) -> buf.writeOptional(v, writer)); return new PacketCodec<>(buf -> buf.readOptional(reader), (buf, v) -> buf.writeOptional(v, writer));
} }
public <X> PacketCodec<X> xMap(Function<T, X> to, Function<X, T> from) {
return new PacketCodec<>(buf -> to.apply(reader.apply(buf)), (buf, v) -> writer.accept(buf, from.apply(v)));
}
public <X> PacketCodec<X> andThen(BiFunction<PacketByteBuf, T, X> to, Function<X, T> from, BiConsumer<PacketByteBuf, X> write) {
return new PacketCodec<>(buf -> {
return to.apply(buf, reader.apply(buf));
}, (buf, v) -> {
writer.accept(buf, from.apply(v));
write.accept(buf, v);
});
}
} }

View file

@ -360,7 +360,7 @@
"block.unicopia.etched_cloud": "Etched Cloud", "block.unicopia.etched_cloud": "Etched Cloud",
"block.unicopia.etched_cloud_slab": "Etched Cloud Slab", "block.unicopia.etched_cloud_slab": "Etched Cloud Slab",
"block.unicopia.etched_cloud_stairs": "Etched Cloud Stairs", "block.unicopia.etched_cloud_stairs": "Etched Cloud Stairs",
"block.unicopia.compacted_etched_cloud": "Cloud Etched Cloud", "block.unicopia.compacted_etched_cloud": "Etched Cloud",
"block.unicopia.cloud_pillar": "Cloud Pillar", "block.unicopia.cloud_pillar": "Cloud Pillar",
"block.unicopia.cloth_bed": "Fancy Cloth Bed", "block.unicopia.cloth_bed": "Fancy Cloth Bed",
"block.unicopia.cloud_bed": "Cloud Bed", "block.unicopia.cloud_bed": "Cloud Bed",
@ -378,6 +378,7 @@
"entity.unicopia.butterfly": "Butterfly", "entity.unicopia.butterfly": "Butterfly",
"entity.unicopia.twittermite": "Twittermite", "entity.unicopia.twittermite": "Twittermite",
"entity.unicopia.specter": "Specter", "entity.unicopia.specter": "Specter",
"entity.unicopia.mimic": "Mimic",
"entity.unicopia.cast_spell": "Cast Spell", "entity.unicopia.cast_spell": "Cast Spell",
"entity.unicopia.cast_spell.by": "a spell cast by %s", "entity.unicopia.cast_spell.by": "a spell cast by %s",
"entity.unicopia.spellbook": "Spellbook", "entity.unicopia.spellbook": "Spellbook",

View file

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

View file

@ -0,0 +1,2 @@
{
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8 KiB

View file

@ -30,6 +30,7 @@
"MixinItemEntity", "MixinItemEntity",
"MixinItemStack", "MixinItemStack",
"MixinLivingEntity", "MixinLivingEntity",
"MixinLootableContainerBlockEntity",
"MixinMilkBucketItem", "MixinMilkBucketItem",
"MixinMobEntity", "MixinMobEntity",
"MixinPersistentProjectileEntity", "MixinPersistentProjectileEntity",