package net.minecraft.client.particle; import com.mojang.blaze3d.vertex.VertexConsumer; import it.unimi.dsi.fastutil.ints.IntList; import java.util.List; import net.minecraft.Util; import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.particles.SimpleParticleType; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.util.ARGB; import net.minecraft.util.Mth; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.component.FireworkExplosion; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @OnlyIn(Dist.CLIENT) public class FireworkParticles { @OnlyIn(Dist.CLIENT) public static class FlashProvider implements ParticleProvider { private final SpriteSet sprite; public FlashProvider(SpriteSet p_106657_) { this.sprite = p_106657_; } public Particle createParticle( SimpleParticleType p_106668_, ClientLevel p_106669_, double p_106670_, double p_106671_, double p_106672_, double p_106673_, double p_106674_, double p_106675_ ) { FireworkParticles.OverlayParticle fireworkparticles$overlayparticle = new FireworkParticles.OverlayParticle( p_106669_, p_106670_, p_106671_, p_106672_ ); fireworkparticles$overlayparticle.pickSprite(this.sprite); return fireworkparticles$overlayparticle; } } @OnlyIn(Dist.CLIENT) public static class OverlayParticle extends TextureSheetParticle { OverlayParticle(ClientLevel p_106677_, double p_106678_, double p_106679_, double p_106680_) { super(p_106677_, p_106678_, p_106679_, p_106680_); this.lifetime = 4; } @Override public ParticleRenderType getRenderType() { return ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT; } @Override public void render(VertexConsumer p_106688_, Camera p_106689_, float p_106690_) { this.setAlpha(0.6F - ((float)this.age + p_106690_ - 1.0F) * 0.25F * 0.5F); super.render(p_106688_, p_106689_, p_106690_); } @Override public float getQuadSize(float p_106693_) { return 7.1F * Mth.sin(((float)this.age + p_106693_ - 1.0F) * 0.25F * (float) Math.PI); } } @OnlyIn(Dist.CLIENT) static class SparkParticle extends SimpleAnimatedParticle { private boolean trail; private boolean twinkle; private final ParticleEngine engine; private float fadeR; private float fadeG; private float fadeB; private boolean hasFade; SparkParticle( ClientLevel p_106702_, double p_106703_, double p_106704_, double p_106705_, double p_106706_, double p_106707_, double p_106708_, ParticleEngine p_106709_, SpriteSet p_106710_ ) { super(p_106702_, p_106703_, p_106704_, p_106705_, p_106710_, 0.1F); this.xd = p_106706_; this.yd = p_106707_; this.zd = p_106708_; this.engine = p_106709_; this.quadSize *= 0.75F; this.lifetime = 48 + this.random.nextInt(12); this.setSpriteFromAge(p_106710_); } public void setTrail(boolean p_106728_) { this.trail = p_106728_; } public void setTwinkle(boolean p_331075_) { this.twinkle = p_331075_; } @Override public void render(VertexConsumer p_106724_, Camera p_106725_, float p_106726_) { if (!this.twinkle || this.age < this.lifetime / 3 || (this.age + this.lifetime) / 3 % 2 == 0) { super.render(p_106724_, p_106725_, p_106726_); } } @Override public void tick() { super.tick(); if (this.trail && this.age < this.lifetime / 2 && (this.age + this.lifetime) % 2 == 0) { FireworkParticles.SparkParticle fireworkparticles$sparkparticle = new FireworkParticles.SparkParticle( this.level, this.x, this.y, this.z, 0.0, 0.0, 0.0, this.engine, this.sprites ); fireworkparticles$sparkparticle.setAlpha(0.99F); fireworkparticles$sparkparticle.setColor(this.rCol, this.gCol, this.bCol); fireworkparticles$sparkparticle.age = fireworkparticles$sparkparticle.lifetime / 2; if (this.hasFade) { fireworkparticles$sparkparticle.hasFade = true; fireworkparticles$sparkparticle.fadeR = this.fadeR; fireworkparticles$sparkparticle.fadeG = this.fadeG; fireworkparticles$sparkparticle.fadeB = this.fadeB; } fireworkparticles$sparkparticle.twinkle = this.twinkle; this.engine.add(fireworkparticles$sparkparticle); } } } @OnlyIn(Dist.CLIENT) public static class SparkProvider implements ParticleProvider { private final SpriteSet sprites; public SparkProvider(SpriteSet p_106733_) { this.sprites = p_106733_; } public Particle createParticle( SimpleParticleType p_106744_, ClientLevel p_106745_, double p_106746_, double p_106747_, double p_106748_, double p_106749_, double p_106750_, double p_106751_ ) { FireworkParticles.SparkParticle fireworkparticles$sparkparticle = new FireworkParticles.SparkParticle( p_106745_, p_106746_, p_106747_, p_106748_, p_106749_, p_106750_, p_106751_, Minecraft.getInstance().particleEngine, this.sprites ); fireworkparticles$sparkparticle.setAlpha(0.99F); return fireworkparticles$sparkparticle; } } @OnlyIn(Dist.CLIENT) public static class Starter extends NoRenderParticle { private static final double[][] CREEPER_PARTICLE_COORDS = new double[][]{ {0.0, 0.2}, {0.2, 0.2}, {0.2, 0.6}, {0.6, 0.6}, {0.6, 0.2}, {0.2, 0.2}, {0.2, 0.0}, {0.4, 0.0}, {0.4, -0.6}, {0.2, -0.6}, {0.2, -0.4}, {0.0, -0.4} }; private static final double[][] STAR_PARTICLE_COORDS = new double[][]{ {0.0, 1.0}, {0.3455, 0.309}, {0.9511, 0.309}, {0.3795918367346939, -0.12653061224489795}, {0.6122448979591837, -0.8040816326530612}, {0.0, -0.35918367346938773} }; private int life; private final ParticleEngine engine; private final List explosions; private boolean twinkleDelay; public Starter( ClientLevel p_106757_, double p_106758_, double p_106759_, double p_106760_, double p_106761_, double p_106762_, double p_106763_, ParticleEngine p_106764_, List p_332725_ ) { super(p_106757_, p_106758_, p_106759_, p_106760_); this.xd = p_106761_; this.yd = p_106762_; this.zd = p_106763_; this.engine = p_106764_; if (p_332725_.isEmpty()) { throw new IllegalArgumentException("Cannot create firework starter with no explosions"); } else { this.explosions = p_332725_; this.lifetime = p_332725_.size() * 2 - 1; for (FireworkExplosion fireworkexplosion : p_332725_) { if (fireworkexplosion.hasTwinkle()) { this.twinkleDelay = true; this.lifetime += 15; break; } } } } @Override public void tick() { if (this.life == 0) { boolean flag = this.isFarAwayFromCamera(); boolean flag1 = false; if (this.explosions.size() >= 3) { flag1 = true; } else { for (FireworkExplosion fireworkexplosion : this.explosions) { if (fireworkexplosion.shape() == FireworkExplosion.Shape.LARGE_BALL) { flag1 = true; break; } } } SoundEvent soundevent1; if (flag1) { soundevent1 = flag ? SoundEvents.FIREWORK_ROCKET_LARGE_BLAST_FAR : SoundEvents.FIREWORK_ROCKET_LARGE_BLAST; } else { soundevent1 = flag ? SoundEvents.FIREWORK_ROCKET_BLAST_FAR : SoundEvents.FIREWORK_ROCKET_BLAST; } this.level .playLocalSound( this.x, this.y, this.z, soundevent1, SoundSource.AMBIENT, 20.0F, 0.95F + this.random.nextFloat() * 0.1F, true ); } if (this.life % 2 == 0 && this.life / 2 < this.explosions.size()) { int j = this.life / 2; FireworkExplosion fireworkexplosion1 = this.explosions.get(j); boolean flag3 = fireworkexplosion1.hasTrail(); boolean flag4 = fireworkexplosion1.hasTwinkle(); IntList intlist = fireworkexplosion1.colors(); IntList intlist1 = fireworkexplosion1.fadeColors(); if (intlist.isEmpty()) { intlist = IntList.of(DyeColor.BLACK.getFireworkColor()); } switch (fireworkexplosion1.shape()) { case SMALL_BALL: this.createParticleBall(0.25, 2, intlist, intlist1, flag3, flag4); break; case LARGE_BALL: this.createParticleBall(0.5, 4, intlist, intlist1, flag3, flag4); break; case STAR: this.createParticleShape(0.5, STAR_PARTICLE_COORDS, intlist, intlist1, flag3, flag4, false); break; case CREEPER: this.createParticleShape(0.5, CREEPER_PARTICLE_COORDS, intlist, intlist1, flag3, flag4, true); break; case BURST: this.createParticleBurst(intlist, intlist1, flag3, flag4); } int i = intlist.getInt(0); Particle particle = this.engine.createParticle(ParticleTypes.FLASH, this.x, this.y, this.z, 0.0, 0.0, 0.0); particle.setColor((float)ARGB.red(i) / 255.0F, (float)ARGB.green(i) / 255.0F, (float)ARGB.blue(i) / 255.0F); } this.life++; if (this.life > this.lifetime) { if (this.twinkleDelay) { boolean flag2 = this.isFarAwayFromCamera(); SoundEvent soundevent = flag2 ? SoundEvents.FIREWORK_ROCKET_TWINKLE_FAR : SoundEvents.FIREWORK_ROCKET_TWINKLE; this.level .playLocalSound( this.x, this.y, this.z, soundevent, SoundSource.AMBIENT, 20.0F, 0.9F + this.random.nextFloat() * 0.15F, true ); } this.remove(); } } private boolean isFarAwayFromCamera() { Minecraft minecraft = Minecraft.getInstance(); return minecraft.gameRenderer.getMainCamera().getPosition().distanceToSqr(this.x, this.y, this.z) >= 256.0; } private void createParticle( double p_106768_, double p_106769_, double p_106770_, double p_106771_, double p_106772_, double p_106773_, IntList p_329729_, IntList p_330193_, boolean p_106776_, boolean p_106777_ ) { FireworkParticles.SparkParticle fireworkparticles$sparkparticle = (FireworkParticles.SparkParticle)this.engine .createParticle(ParticleTypes.FIREWORK, p_106768_, p_106769_, p_106770_, p_106771_, p_106772_, p_106773_); fireworkparticles$sparkparticle.setTrail(p_106776_); fireworkparticles$sparkparticle.setTwinkle(p_106777_); fireworkparticles$sparkparticle.setAlpha(0.99F); fireworkparticles$sparkparticle.setColor(Util.getRandom(p_329729_, this.random)); if (!p_330193_.isEmpty()) { fireworkparticles$sparkparticle.setFadeColor(Util.getRandom(p_330193_, this.random)); } } private void createParticleBall(double p_106779_, int p_106780_, IntList p_331387_, IntList p_331480_, boolean p_106783_, boolean p_106784_) { double d0 = this.x; double d1 = this.y; double d2 = this.z; for (int i = -p_106780_; i <= p_106780_; i++) { for (int j = -p_106780_; j <= p_106780_; j++) { for (int k = -p_106780_; k <= p_106780_; k++) { double d3 = (double)j + (this.random.nextDouble() - this.random.nextDouble()) * 0.5; double d4 = (double)i + (this.random.nextDouble() - this.random.nextDouble()) * 0.5; double d5 = (double)k + (this.random.nextDouble() - this.random.nextDouble()) * 0.5; double d6 = Math.sqrt(d3 * d3 + d4 * d4 + d5 * d5) / p_106779_ + this.random.nextGaussian() * 0.05; this.createParticle(d0, d1, d2, d3 / d6, d4 / d6, d5 / d6, p_331387_, p_331480_, p_106783_, p_106784_); if (i != -p_106780_ && i != p_106780_ && j != -p_106780_ && j != p_106780_) { k += p_106780_ * 2 - 1; } } } } } private void createParticleShape( double p_106786_, double[][] p_106787_, IntList p_330103_, IntList p_332201_, boolean p_106790_, boolean p_106791_, boolean p_106792_ ) { double d0 = p_106787_[0][0]; double d1 = p_106787_[0][1]; this.createParticle(this.x, this.y, this.z, d0 * p_106786_, d1 * p_106786_, 0.0, p_330103_, p_332201_, p_106790_, p_106791_); float f = this.random.nextFloat() * (float) Math.PI; double d2 = p_106792_ ? 0.034 : 0.34; for (int i = 0; i < 3; i++) { double d3 = (double)f + (double)((float)i * (float) Math.PI) * d2; double d4 = d0; double d5 = d1; for (int j = 1; j < p_106787_.length; j++) { double d6 = p_106787_[j][0]; double d7 = p_106787_[j][1]; for (double d8 = 0.25; d8 <= 1.0; d8 += 0.25) { double d9 = Mth.lerp(d8, d4, d6) * p_106786_; double d10 = Mth.lerp(d8, d5, d7) * p_106786_; double d11 = d9 * Math.sin(d3); d9 *= Math.cos(d3); for (double d12 = -1.0; d12 <= 1.0; d12 += 2.0) { this.createParticle(this.x, this.y, this.z, d9 * d12, d10, d11 * d12, p_330103_, p_332201_, p_106790_, p_106791_); } } d4 = d6; d5 = d7; } } } private void createParticleBurst(IntList p_336354_, IntList p_328829_, boolean p_106796_, boolean p_106797_) { double d0 = this.random.nextGaussian() * 0.05; double d1 = this.random.nextGaussian() * 0.05; for (int i = 0; i < 70; i++) { double d2 = this.xd * 0.5 + this.random.nextGaussian() * 0.15 + d0; double d3 = this.zd * 0.5 + this.random.nextGaussian() * 0.15 + d1; double d4 = this.yd * 0.5 + this.random.nextDouble() * 0.5; this.createParticle(this.x, this.y, this.z, d2, d4, d3, p_336354_, p_328829_, p_106796_, p_106797_); } } } }