package at.petrak.hexcasting.common.network; import at.petrak.hexcasting.api.casting.ParticleSpray; import at.petrak.hexcasting.api.pigment.FrozenPigment; import at.petrak.hexcasting.common.particles.ConjureParticleOptions; import io.netty.buffer.ByteBuf; import net.minecraft.client.Minecraft; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec3; import java.util.Random; import static at.petrak.hexcasting.api.HexAPI.modLoc; /** * Sent server->client to spray particles everywhere. */ public record MsgCastParticleAck(ParticleSpray spray, FrozenPigment colorizer) implements IMessage { public static final ResourceLocation ID = modLoc("cprtcl"); @Override public ResourceLocation getFabricId() { return ID; } public static MsgCastParticleAck deserialize(ByteBuf buffer) { var buf = new FriendlyByteBuf(buffer); var posX = buf.readDouble(); var posY = buf.readDouble(); var posZ = buf.readDouble(); var velX = buf.readDouble(); var velY = buf.readDouble(); var velZ = buf.readDouble(); var fuzziness = buf.readDouble(); var spread = buf.readDouble(); var count = buf.readInt(); var tag = buf.readAnySizeNbt(); var colorizer = FrozenPigment.fromNBT(tag); return new MsgCastParticleAck( new ParticleSpray(new Vec3(posX, posY, posZ), new Vec3(velX, velY, velZ), fuzziness, spread, count), colorizer); } @Override public void serialize(FriendlyByteBuf buf) { buf.writeDouble(this.spray.getPos().x); buf.writeDouble(this.spray.getPos().y); buf.writeDouble(this.spray.getPos().z); buf.writeDouble(this.spray.getVel().x); buf.writeDouble(this.spray.getVel().y); buf.writeDouble(this.spray.getVel().z); buf.writeDouble(this.spray.getFuzziness()); buf.writeDouble(this.spray.getSpread()); buf.writeInt(this.spray.getCount()); buf.writeNbt(this.colorizer.serializeToNBT()); } private static final Random RANDOM = new Random(); // https://math.stackexchange.com/questions/44689/how-to-find-a-random-axis-or-unit-vector-in-3d private static Vec3 randomInCircle(double maxTh) { var th = RANDOM.nextDouble(0.0, maxTh + 0.001); var z = RANDOM.nextDouble(-1.0, 1.0); return new Vec3(Math.sqrt(1.0 - z * z) * Math.cos(th), Math.sqrt(1.0 - z * z) * Math.sin(th), z); } public static void handle(MsgCastParticleAck msg) { Minecraft.getInstance().execute(new Runnable() { @Override public void run() { var colProvider = msg.colorizer().getColorProvider(); for (int i = 0; i < msg.spray().getCount(); i++) { // For the colors, pick any random time to get a mix of colors var color = colProvider.getColor(RANDOM.nextFloat() * 256f, Vec3.ZERO); var offset = randomInCircle(Mth.TWO_PI).normalize() .scale(RANDOM.nextFloat() * msg.spray().getFuzziness() / 2); var pos = msg.spray().getPos().add(offset); var phi = Math.acos(1.0 - RANDOM.nextDouble() * (1.0 - Math.cos(msg.spray().getSpread()))); var theta = Math.PI * 2.0 * RANDOM.nextDouble(); var v = msg.spray().getVel().normalize(); // pick any old vector to get a vector normal to v with Vec3 k; if (v.x == 0.0 && v.y == 0.0) { // oops, pick a *different* normal k = new Vec3(1.0, 0.0, 0.0); } else { k = v.cross(new Vec3(0.0, 0.0, 1.0)); } var velUnlen = v.scale(Math.cos(phi)) .add(k.scale(Math.sin(phi) * Math.cos(theta))) .add(v.cross(k).scale(Math.sin(phi) * Math.sin(theta))); var vel = velUnlen.scale(msg.spray().getVel().length() / 20); Minecraft.getInstance().level.addParticle( new ConjureParticleOptions(color), pos.x, pos.y, pos.z, vel.x, vel.y, vel.z ); } } }); } }