2025-07-09 19:41:37 +02:00
|
|
|
package client.renderer;
|
|
|
|
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
|
2025-08-28 15:04:19 +02:00
|
|
|
import org.lwjgl.opengl.GL46;
|
2025-07-09 19:41:37 +02:00
|
|
|
|
|
|
|
import client.Client;
|
2025-07-20 14:56:03 +02:00
|
|
|
import client.renderer.texture.Sprite;
|
2025-07-09 19:41:37 +02:00
|
|
|
import client.renderer.texture.TextureManager;
|
|
|
|
import client.renderer.texture.TextureMap;
|
|
|
|
import common.block.Block;
|
|
|
|
import common.block.Material;
|
|
|
|
import common.block.liquid.BlockLiquid;
|
|
|
|
import common.collect.Lists;
|
|
|
|
import common.collect.Maps;
|
|
|
|
import common.entity.Entity;
|
|
|
|
import common.init.BlockRegistry;
|
|
|
|
import common.init.Blocks;
|
|
|
|
import common.init.ItemRegistry;
|
|
|
|
import common.item.Item;
|
2025-07-19 23:55:20 +02:00
|
|
|
import common.item.consumable.ItemPotion;
|
2025-07-09 19:41:37 +02:00
|
|
|
import common.rng.Random;
|
|
|
|
import common.util.BlockPos;
|
|
|
|
import common.util.ExtMath;
|
|
|
|
import common.util.Facing;
|
2025-07-10 00:22:29 +02:00
|
|
|
import common.util.ParticleType;
|
2025-07-09 19:41:37 +02:00
|
|
|
import common.world.State;
|
|
|
|
import common.world.World;
|
|
|
|
|
|
|
|
public class EffectRenderer {
|
2025-07-10 18:24:08 +02:00
|
|
|
private interface Effect {
|
|
|
|
boolean onUpdate();
|
|
|
|
void render(RenderBuffer rb, float partial, float rotX, float rotZ, float rotYZ, float rotXY, float rotXZ);
|
|
|
|
int getLayer();
|
2025-07-09 19:41:37 +02:00
|
|
|
}
|
|
|
|
|
2025-07-10 18:24:08 +02:00
|
|
|
private abstract class Moveable implements Effect {
|
2025-07-09 19:41:37 +02:00
|
|
|
protected double prevX;
|
|
|
|
protected double prevY;
|
|
|
|
protected double prevZ;
|
|
|
|
protected double posX;
|
|
|
|
protected double posY;
|
|
|
|
protected double posZ;
|
|
|
|
protected double motionX;
|
|
|
|
protected double motionY;
|
|
|
|
protected double motionZ;
|
|
|
|
|
2025-07-10 18:24:08 +02:00
|
|
|
protected int age;
|
|
|
|
protected int lifetime;
|
2025-07-09 19:41:37 +02:00
|
|
|
protected float gravity;
|
|
|
|
|
|
|
|
protected Moveable(double posXIn, double posYIn, double posZIn) {
|
|
|
|
this.posX = this.prevX = posXIn;
|
|
|
|
this.posY = this.prevY = posYIn;
|
|
|
|
this.posZ = this.prevZ = posZIn;
|
|
|
|
this.lifetime = (int)(4.0F / (rng.floatv() * 0.9F + 0.1F));
|
|
|
|
this.age = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Moveable(double xCoordIn, double yCoordIn, double zCoordIn, double xSpeedIn, double ySpeedIn, double zSpeedIn) {
|
|
|
|
this(xCoordIn, yCoordIn, zCoordIn);
|
|
|
|
this.motionX = xSpeedIn + (Math.random() * 2.0D - 1.0D) * 0.4000000059604645D;
|
|
|
|
this.motionY = ySpeedIn + (Math.random() * 2.0D - 1.0D) * 0.4000000059604645D;
|
|
|
|
this.motionZ = zSpeedIn + (Math.random() * 2.0D - 1.0D) * 0.4000000059604645D;
|
|
|
|
float f = (float)(Math.random() + Math.random() + 1.0D) * 0.15F;
|
|
|
|
float f1 = ExtMath.sqrtd(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ);
|
|
|
|
this.motionX = this.motionX / (double)f1 * (double)f * 0.4000000059604645D;
|
|
|
|
this.motionY = this.motionY / (double)f1 * (double)f * 0.4000000059604645D + 0.10000000149011612D;
|
|
|
|
this.motionZ = this.motionZ / (double)f1 * (double)f * 0.4000000059604645D;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final void move(double x, double y, double z) {
|
|
|
|
this.posX += x;
|
|
|
|
this.posY += y;
|
|
|
|
this.posZ += z;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getBrightness(float partial) {
|
|
|
|
BlockPos pos = new BlockPos(this.posX, this.posY, this.posZ);
|
|
|
|
return world.isBlockLoaded(pos) ? world.getCombinedLight(pos, 0) : 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private abstract class Particle extends Moveable {
|
|
|
|
private int textureU;
|
|
|
|
private int textureV;
|
|
|
|
protected float scale;
|
|
|
|
protected float red;
|
|
|
|
protected float green;
|
|
|
|
protected float blue;
|
|
|
|
|
|
|
|
public Particle(double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) {
|
|
|
|
super(x, y, z, xSpeed, ySpeed, zSpeed);
|
|
|
|
this.red = this.green = this.blue = 1.0F;
|
|
|
|
this.scale = (rng.floatv() * 0.5F + 0.5F) * 2.0F;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setScale(float partial) {
|
|
|
|
}
|
|
|
|
|
|
|
|
public final void render(RenderBuffer rb, float partial, float rotX, float rotZ, float rotYZ, float rotXY, float rotXZ) {
|
|
|
|
this.setScale(partial);
|
2025-07-10 18:24:08 +02:00
|
|
|
float u1 = (float)this.textureU / 8.0F;
|
|
|
|
float u2 = u1 + 1.0f / 8.0f;
|
|
|
|
float v1 = (float)this.textureV / 4.0F;
|
|
|
|
float v2 = v1 + 1.0f / 4.0f;
|
|
|
|
float scale = 0.1F * this.scale;
|
|
|
|
float x = (float)(this.prevX + (this.posX - this.prevX) * (double)partial - interpPosX);
|
|
|
|
float y = (float)(this.prevY + (this.posY - this.prevY) * (double)partial - interpPosY);
|
|
|
|
float z = (float)(this.prevZ + (this.posZ - this.prevZ) * (double)partial - interpPosZ);
|
|
|
|
int light = this.getBrightness(partial);
|
|
|
|
int sky = light >> 16 & 65535;
|
|
|
|
int block = light & 65535;
|
|
|
|
rb.pos((double)(x - rotX * scale - rotXY * scale), (double)(y - rotZ * scale), (double)(z - rotYZ * scale - rotXZ * scale))
|
|
|
|
.tex((double)u2, (double)v2).color(this.red, this.green, this.blue, 1.0f).lightmap(sky, block).endVertex();
|
|
|
|
rb.pos((double)(x - rotX * scale + rotXY * scale), (double)(y + rotZ * scale), (double)(z - rotYZ * scale + rotXZ * scale))
|
|
|
|
.tex((double)u2, (double)v1).color(this.red, this.green, this.blue, 1.0f).lightmap(sky, block).endVertex();
|
|
|
|
rb.pos((double)(x + rotX * scale + rotXY * scale), (double)(y + rotZ * scale), (double)(z + rotYZ * scale + rotXZ * scale)).tex((double)u1, (double)v1)
|
|
|
|
.color(this.red, this.green, this.blue, 1.0f).lightmap(sky, block).endVertex();
|
|
|
|
rb.pos((double)(x + rotX * scale - rotXY * scale), (double)(y - rotZ * scale), (double)(z + rotYZ * scale - rotXZ * scale)).tex((double)u1, (double)v2)
|
|
|
|
.color(this.red, this.green, this.blue, 1.0f).lightmap(sky, block).endVertex();
|
2025-07-09 19:41:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public final int getLayer() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected final void setUV(int x, int y) {
|
|
|
|
this.textureU = x;
|
|
|
|
this.textureV = y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class Aura extends Particle {
|
|
|
|
private final boolean fullBright;
|
|
|
|
|
2025-07-10 18:24:08 +02:00
|
|
|
protected Aura(double xCoordIn, double yCoordIn, double zCoordIn, boolean fullBright,
|
2025-07-09 19:41:37 +02:00
|
|
|
int texture) {
|
2025-07-10 18:24:08 +02:00
|
|
|
super(xCoordIn, yCoordIn, zCoordIn, 0.0D, 0.0D, 0.0D);
|
2025-07-09 19:41:37 +02:00
|
|
|
float f = texture != 0 ? 1.0f : rng.floatv() * 0.1F + 0.2F;
|
|
|
|
this.red = f;
|
|
|
|
this.green = f;
|
|
|
|
this.blue = f;
|
|
|
|
this.setUV(texture != 0 ? 5 + texture : 0, texture != 0 ? 2 : 0);
|
|
|
|
this.scale *= rng.floatv() * 0.6F + 0.5F;
|
|
|
|
this.motionX *= 0.019999999552965164D;
|
|
|
|
this.motionY *= 0.019999999552965164D;
|
|
|
|
this.motionZ *= 0.019999999552965164D;
|
|
|
|
this.lifetime = (int)(20.0D / (Math.random() * 0.8D + 0.2D));
|
|
|
|
this.fullBright = fullBright;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean onUpdate() {
|
|
|
|
this.prevX = this.posX;
|
|
|
|
this.prevY = this.posY;
|
|
|
|
this.prevZ = this.posZ;
|
|
|
|
this.move(this.motionX, this.motionY, this.motionZ);
|
|
|
|
this.motionX *= 0.99D;
|
|
|
|
this.motionY *= 0.99D;
|
|
|
|
this.motionZ *= 0.99D;
|
|
|
|
|
|
|
|
if(this.lifetime-- <= 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getBrightness(float partialTicks) {
|
|
|
|
return this.fullBright ? 15728880 : super.getBrightness(partialTicks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class Crit extends Particle {
|
|
|
|
private final float baseScale;
|
|
|
|
|
2025-07-10 18:24:08 +02:00
|
|
|
protected Crit(double xCoordIn, double yCoordIn, double zCoordIn) {
|
2025-07-09 19:41:37 +02:00
|
|
|
super(xCoordIn, yCoordIn, zCoordIn, 0.0D, 0.0D, 0.0D);
|
|
|
|
this.motionX *= 0.10000000149011612D;
|
|
|
|
this.motionY *= 0.10000000149011612D;
|
|
|
|
this.motionZ *= 0.10000000149011612D;
|
2025-07-10 18:24:08 +02:00
|
|
|
this.motionX += (double)(rng.floatv() * 2.0F - 1.0F) * 0.4D;
|
|
|
|
this.motionY += ((double)(rng.floatv() * 2.0F - 1.0F) + 0.2) * 0.4D;
|
|
|
|
this.motionZ += (double)(rng.floatv() * 2.0F - 1.0F) * 0.4D;
|
2025-07-09 19:41:37 +02:00
|
|
|
this.blue = (float)(Math.random() * 0.30000001192092896D + 0.6000000238418579D);
|
|
|
|
this.red = this.blue * 0.3f;
|
|
|
|
this.green = this.blue * 0.8f;
|
|
|
|
this.scale *= 0.75F;
|
|
|
|
this.baseScale = this.scale;
|
|
|
|
this.lifetime = (int)(6.0D / (Math.random() * 0.8D + 0.6D));
|
|
|
|
this.setUV(4, 2);
|
|
|
|
this.onUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setScale(float partialTicks) {
|
|
|
|
float f = ((float)this.age + partialTicks) / (float)this.lifetime * 32.0F;
|
|
|
|
f = ExtMath.clampf(f, 0.0F, 1.0F);
|
|
|
|
this.scale = this.baseScale * f;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean onUpdate() {
|
|
|
|
this.prevX = this.posX;
|
|
|
|
this.prevY = this.posY;
|
|
|
|
this.prevZ = this.posZ;
|
|
|
|
|
|
|
|
if(this.age++ >= this.lifetime) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.move(this.motionX, this.motionY, this.motionZ);
|
|
|
|
this.green = (float)((double)this.green * 0.96D);
|
|
|
|
this.blue = (float)((double)this.blue * 0.9D);
|
|
|
|
this.motionX *= 0.699999988079071D;
|
|
|
|
this.motionY *= 0.699999988079071D;
|
|
|
|
this.motionZ *= 0.699999988079071D;
|
|
|
|
this.motionY -= 0.019999999552965164D;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class Downfall extends Particle {
|
|
|
|
protected Downfall(double xCoordIn, double yCoordIn, double zCoordIn, int texture) {
|
|
|
|
super(xCoordIn, yCoordIn, zCoordIn, 0.0D, 0.0D, 0.0D);
|
|
|
|
this.motionX *= 0.30000001192092896D;
|
|
|
|
this.motionY = Math.random() * 0.20000000298023224D + 0.10000000149011612D;
|
|
|
|
this.motionZ *= 0.30000001192092896D;
|
|
|
|
this.red = 1.0F;
|
|
|
|
this.green = 1.0F;
|
|
|
|
this.blue = 1.0F;
|
|
|
|
this.setUV(rng.zrange(4), 1 + texture);
|
|
|
|
this.gravity = 0.06F;
|
|
|
|
this.lifetime = (int)(8.0D / (Math.random() * 0.8D + 0.2D));
|
|
|
|
}
|
|
|
|
|
2025-07-10 18:24:08 +02:00
|
|
|
protected Downfall(double xCoordIn, double yCoordIn, double zCoordIn) {
|
2025-07-09 19:41:37 +02:00
|
|
|
this(xCoordIn, yCoordIn, zCoordIn, 0);
|
|
|
|
this.gravity = 0.04F;
|
|
|
|
this.setUV(1 + rng.zrange(3), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean onUpdate() {
|
|
|
|
this.prevX = this.posX;
|
|
|
|
this.prevY = this.posY;
|
|
|
|
this.prevZ = this.posZ;
|
|
|
|
this.motionY -= (double)this.gravity;
|
|
|
|
this.move(this.motionX, this.motionY, this.motionZ);
|
|
|
|
this.motionX *= 0.9800000190734863D;
|
|
|
|
this.motionY *= 0.9800000190734863D;
|
|
|
|
this.motionZ *= 0.9800000190734863D;
|
|
|
|
|
|
|
|
if(this.lifetime-- <= 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
BlockPos blockpos = new BlockPos(this.posX, this.posY, this.posZ);
|
|
|
|
State iblockstate = world.getState(blockpos);
|
|
|
|
Block block = iblockstate.getBlock();
|
2025-07-20 14:20:55 +02:00
|
|
|
block.setBlockBounds(world, blockpos);
|
2025-07-09 19:41:37 +02:00
|
|
|
Material material = iblockstate.getBlock().getMaterial();
|
|
|
|
|
|
|
|
if(material.isLiquid() || material.isSolid()) {
|
|
|
|
double d0 = 0.0D;
|
|
|
|
|
|
|
|
if(iblockstate.getBlock() instanceof BlockLiquid) {
|
|
|
|
d0 = (double)(1.0F - BlockLiquid.getLiquidHeightPercent(((Integer)iblockstate.getValue(BlockLiquid.LEVEL)).intValue()));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
d0 = block.getBlockBoundsMaxY();
|
|
|
|
}
|
|
|
|
|
|
|
|
double d1 = (double)ExtMath.floord(this.posY) + d0;
|
|
|
|
|
|
|
|
if(this.posY < d1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class Dust extends Particle {
|
|
|
|
private final float baseScale;
|
|
|
|
|
2025-07-10 18:24:08 +02:00
|
|
|
protected Dust(double xCoordIn, double yCoordIn, double zCoordIn, int color) {
|
2025-07-09 19:41:37 +02:00
|
|
|
super(xCoordIn, yCoordIn, zCoordIn, 0.0D, 0.0D, 0.0D);
|
|
|
|
this.motionX *= 0.10000000149011612D;
|
|
|
|
this.motionY *= 0.10000000149011612D;
|
|
|
|
this.motionZ *= 0.10000000149011612D;
|
|
|
|
float f = (float)Math.random() * 0.4F + 0.6F;
|
2025-07-10 18:24:08 +02:00
|
|
|
this.red = ((float)(Math.random() * 0.20000000298023224D) + 0.8F) * ((float)((color >> 16) & 0xff) / 255.0f) * f;
|
|
|
|
this.green = ((float)(Math.random() * 0.20000000298023224D) + 0.8F) * ((float)((color >> 8) & 0xff) / 255.0f) * f;
|
|
|
|
this.blue = ((float)(Math.random() * 0.20000000298023224D) + 0.8F) * ((float)(color & 0xff) / 255.0f) * f;
|
2025-07-09 19:41:37 +02:00
|
|
|
this.scale *= 0.75F;
|
|
|
|
this.baseScale = this.scale;
|
|
|
|
this.lifetime = (int)(8.0D / (Math.random() * 0.8D + 0.2D));
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setScale(float partialTicks) {
|
|
|
|
float f = ((float)this.age + partialTicks) / (float)this.lifetime * 32.0F;
|
|
|
|
f = ExtMath.clampf(f, 0.0F, 1.0F);
|
|
|
|
this.scale = this.baseScale * f;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean onUpdate() {
|
|
|
|
this.prevX = this.posX;
|
|
|
|
this.prevY = this.posY;
|
|
|
|
this.prevZ = this.posZ;
|
|
|
|
|
|
|
|
if(this.age++ >= this.lifetime) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setUV(Math.max(0, 7 - this.age * 8 / this.lifetime), 0);
|
|
|
|
this.move(this.motionX, this.motionY, this.motionZ);
|
|
|
|
|
|
|
|
if(this.posY == this.prevY) {
|
|
|
|
this.motionX *= 1.1D;
|
|
|
|
this.motionZ *= 1.1D;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.motionX *= 0.9599999785423279D;
|
|
|
|
this.motionY *= 0.9599999785423279D;
|
|
|
|
this.motionZ *= 0.9599999785423279D;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class Explosion extends Particle {
|
2025-07-10 18:24:08 +02:00
|
|
|
protected Explosion(double xCoordIn, double yCoordIn, double zCoordIn) {
|
|
|
|
super(xCoordIn, yCoordIn, zCoordIn, 0.0D, 0.0D, 0.0D);
|
|
|
|
this.motionX = (Math.random() * 2.0D - 1.0D) * 0.05000000074505806D;
|
|
|
|
this.motionY = (Math.random() * 2.0D - 1.0D) * 0.05000000074505806D;
|
|
|
|
this.motionZ = (Math.random() * 2.0D - 1.0D) * 0.05000000074505806D;
|
2025-07-09 19:41:37 +02:00
|
|
|
this.red = this.green = this.blue = rng.floatv() * 0.3F + 0.7F;
|
|
|
|
this.scale = rng.floatv() * rng.floatv() * 6.0F + 1.0F;
|
|
|
|
this.lifetime = (int)(16.0D / ((double)rng.floatv() * 0.8D + 0.2D)) + 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean onUpdate() {
|
|
|
|
this.prevX = this.posX;
|
|
|
|
this.prevY = this.posY;
|
|
|
|
this.prevZ = this.posZ;
|
|
|
|
|
|
|
|
if(this.age++ >= this.lifetime) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setUV(Math.max(0, 7 - this.age * 8 / this.lifetime), 0);
|
|
|
|
this.motionY += 0.004D;
|
|
|
|
this.move(this.motionX, this.motionY, this.motionZ);
|
|
|
|
this.motionX *= 0.8999999761581421D;
|
|
|
|
this.motionY *= 0.8999999761581421D;
|
|
|
|
this.motionZ *= 0.8999999761581421D;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class Flame extends Particle {
|
|
|
|
private final float baseScale;
|
|
|
|
|
2025-07-10 18:24:08 +02:00
|
|
|
protected Flame(double xCoordIn, double yCoordIn, double zCoordIn) {
|
|
|
|
super(xCoordIn, yCoordIn, zCoordIn, 0.0D, 0.0D, 0.0D);
|
|
|
|
this.motionX = this.motionX * 0.009999999776482582D;
|
|
|
|
this.motionY = this.motionY * 0.009999999776482582D;
|
|
|
|
this.motionZ = this.motionZ * 0.009999999776482582D;
|
2025-07-09 19:41:37 +02:00
|
|
|
this.posX += (double)((rng.floatv() - rng.floatv()) * 0.05F);
|
|
|
|
this.posY += (double)((rng.floatv() - rng.floatv()) * 0.05F);
|
|
|
|
this.posZ += (double)((rng.floatv() - rng.floatv()) * 0.05F);
|
|
|
|
this.baseScale = this.scale;
|
|
|
|
this.red = this.green = this.blue = 1.0F;
|
|
|
|
this.lifetime = (int)(8.0D / (Math.random() * 0.8D + 0.2D)) + 4;
|
|
|
|
this.setUV(6, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setScale(float partialTicks) {
|
|
|
|
float f = ((float)this.age + partialTicks) / (float)this.lifetime;
|
|
|
|
this.scale = this.baseScale * (1.0F - f * f * 0.5F);
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getBrightness(float partialTicks) {
|
|
|
|
float f = ((float)this.age + partialTicks) / (float)this.lifetime;
|
|
|
|
f = ExtMath.clampf(f, 0.0F, 1.0F);
|
|
|
|
int i = super.getBrightness(partialTicks);
|
|
|
|
int j = i & 255;
|
|
|
|
int k = i >> 16 & 255;
|
|
|
|
j = j + (int)(f * 15.0F * 16.0F);
|
|
|
|
|
|
|
|
if(j > 240) {
|
|
|
|
j = 240;
|
|
|
|
}
|
|
|
|
|
|
|
|
return j | k << 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean onUpdate() {
|
|
|
|
this.prevX = this.posX;
|
|
|
|
this.prevY = this.posY;
|
|
|
|
this.prevZ = this.posZ;
|
|
|
|
|
|
|
|
if(this.age++ >= this.lifetime) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.move(this.motionX, this.motionY, this.motionZ);
|
|
|
|
this.motionX *= 0.9599999785423279D;
|
|
|
|
this.motionY *= 0.9599999785423279D;
|
|
|
|
this.motionZ *= 0.9599999785423279D;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class Heart extends Particle {
|
|
|
|
private final float baseScale;
|
|
|
|
|
|
|
|
protected Heart(double xCoordIn, double yCoordIn, double zCoordIn) {
|
|
|
|
super(xCoordIn, yCoordIn, zCoordIn, 0.0D, 0.0D, 0.0D);
|
|
|
|
this.motionX *= 0.009999999776482582D;
|
|
|
|
this.motionY *= 0.009999999776482582D;
|
|
|
|
this.motionZ *= 0.009999999776482582D;
|
|
|
|
this.motionY += 0.1D;
|
|
|
|
this.scale *= 0.75F;
|
|
|
|
this.baseScale = this.scale;
|
|
|
|
this.lifetime = 16;
|
|
|
|
this.setUV(5, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setScale(float partialTicks) {
|
|
|
|
float f = ((float)this.age + partialTicks) / (float)this.lifetime * 32.0F;
|
|
|
|
f = ExtMath.clampf(f, 0.0F, 1.0F);
|
|
|
|
this.scale = this.baseScale * f;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean onUpdate() {
|
|
|
|
this.prevX = this.posX;
|
|
|
|
this.prevY = this.posY;
|
|
|
|
this.prevZ = this.posZ;
|
|
|
|
|
|
|
|
if(this.age++ >= this.lifetime) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.move(this.motionX, this.motionY, this.motionZ);
|
|
|
|
|
|
|
|
if(this.posY == this.prevY) {
|
|
|
|
this.motionX *= 1.1D;
|
|
|
|
this.motionZ *= 1.1D;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.motionX *= 0.8600000143051147D;
|
|
|
|
this.motionY *= 0.8600000143051147D;
|
|
|
|
this.motionZ *= 0.8600000143051147D;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class LavaPop extends Particle {
|
|
|
|
private final float baseScale;
|
|
|
|
|
|
|
|
protected LavaPop(double xCoordIn, double yCoordIn, double zCoordIn) {
|
|
|
|
super(xCoordIn, yCoordIn, zCoordIn, 0.0D, 0.0D, 0.0D);
|
|
|
|
this.motionX *= 0.800000011920929D;
|
|
|
|
this.motionY *= 0.800000011920929D;
|
|
|
|
this.motionZ *= 0.800000011920929D;
|
|
|
|
this.motionY = (double)(rng.floatv() * 0.4F + 0.05F);
|
|
|
|
this.red = this.green = this.blue = 1.0F;
|
|
|
|
this.scale *= rng.floatv() * 2.0F + 0.2F;
|
|
|
|
this.baseScale = this.scale;
|
|
|
|
this.lifetime = (int)(16.0D / (Math.random() * 0.8D + 0.2D));
|
|
|
|
this.setUV(7, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getBrightness(float partialTicks) {
|
|
|
|
float f = ((float)this.age + partialTicks) / (float)this.lifetime;
|
|
|
|
f = ExtMath.clampf(f, 0.0F, 1.0F);
|
|
|
|
int i = super.getBrightness(partialTicks);
|
|
|
|
int j = 240;
|
|
|
|
int k = i >> 16 & 255;
|
|
|
|
return j | k << 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setScale(float partialTicks) {
|
|
|
|
float f = ((float)this.age + partialTicks) / (float)this.lifetime;
|
|
|
|
this.scale = this.baseScale * (1.0F - f * f);
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean onUpdate() {
|
|
|
|
this.prevX = this.posX;
|
|
|
|
this.prevY = this.posY;
|
|
|
|
this.prevZ = this.posZ;
|
|
|
|
|
|
|
|
if(this.age++ >= this.lifetime) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
float f = (float)this.age / (float)this.lifetime;
|
|
|
|
|
|
|
|
if(rng.floatv() > f) {
|
2025-07-23 14:23:06 +02:00
|
|
|
world.clientParticle(ParticleType.SMOKE, this.posX, this.posY, this.posZ);
|
2025-07-09 19:41:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
this.motionY -= 0.03D;
|
|
|
|
this.move(this.motionX, this.motionY, this.motionZ);
|
|
|
|
this.motionX *= 0.9990000128746033D;
|
|
|
|
this.motionY *= 0.9990000128746033D;
|
|
|
|
this.motionZ *= 0.9990000128746033D;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class Teleport extends Particle {
|
|
|
|
private final float baseScale;
|
|
|
|
private final double baseX;
|
|
|
|
private final double baseY;
|
|
|
|
private final double baseZ;
|
|
|
|
|
2025-07-10 18:24:08 +02:00
|
|
|
protected Teleport(double xCoordIn, double yCoordIn, double zCoordIn) {
|
|
|
|
super(xCoordIn, yCoordIn, zCoordIn, 0.0, 0.0, 0.0);
|
|
|
|
this.motionX = (rng.floatv() - 0.5F) * 0.2F;
|
|
|
|
this.motionY = (rng.floatv() - 0.5F) * 0.2F;
|
|
|
|
this.motionZ = (rng.floatv() - 0.5F) * 0.2F;
|
2025-07-09 19:41:37 +02:00
|
|
|
this.baseX = this.posX = xCoordIn;
|
|
|
|
this.baseY = this.posY = yCoordIn;
|
|
|
|
this.baseZ = this.posZ = zCoordIn;
|
|
|
|
float f = rng.floatv() * 0.6F + 0.4F;
|
|
|
|
this.baseScale = this.scale = rng.floatv() * 0.2F + 0.5F;
|
|
|
|
this.red = this.green = this.blue = 1.0F * f;
|
|
|
|
this.green *= 0.1F;
|
|
|
|
this.red *= 0.2F;
|
|
|
|
this.blue *= 0.25F;
|
|
|
|
this.lifetime = (int)(Math.random() * 10.0D) + 40;
|
|
|
|
this.setUV((int)(Math.random() * 8.0D), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setScale(float partialTicks) {
|
|
|
|
float f = ((float)this.age + partialTicks) / (float)this.lifetime;
|
|
|
|
f = 1.0F - f;
|
|
|
|
f = f * f;
|
|
|
|
f = 1.0F - f;
|
|
|
|
this.scale = this.baseScale * f;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getBrightness(float partialTicks) {
|
|
|
|
int i = super.getBrightness(partialTicks);
|
|
|
|
float f = (float)this.age / (float)this.lifetime;
|
|
|
|
f = f * f;
|
|
|
|
f = f * f;
|
|
|
|
int j = i & 255;
|
|
|
|
int k = i >> 16 & 255;
|
|
|
|
k = k + (int)(f * 15.0F * 16.0F);
|
|
|
|
|
|
|
|
if(k > 240) {
|
|
|
|
k = 240;
|
|
|
|
}
|
|
|
|
|
|
|
|
return j | k << 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean onUpdate() {
|
|
|
|
this.prevX = this.posX;
|
|
|
|
this.prevY = this.posY;
|
|
|
|
this.prevZ = this.posZ;
|
|
|
|
float f = (float)this.age / (float)this.lifetime;
|
|
|
|
f = -f + f * f * 2.0F;
|
|
|
|
f = 1.0F - f;
|
|
|
|
this.posX = this.baseX + this.motionX * (double)f;
|
|
|
|
this.posY = this.baseY + this.motionY * (double)f + (double)(1.0F - f);
|
|
|
|
this.posZ = this.baseZ + this.motionZ * (double)f;
|
|
|
|
|
|
|
|
if(this.age++ >= this.lifetime) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class Smoke extends Particle {
|
|
|
|
private final float baseScale;
|
|
|
|
|
2025-07-10 18:24:08 +02:00
|
|
|
protected Smoke(double xCoordIn, double yCoordIn, double zCoordIn) {
|
2025-07-09 19:41:37 +02:00
|
|
|
super(xCoordIn, yCoordIn, zCoordIn, 0.0D, 0.0D, 0.0D);
|
|
|
|
this.motionX *= 0.10000000149011612D;
|
|
|
|
this.motionY *= 0.10000000149011612D;
|
|
|
|
this.motionZ *= 0.10000000149011612D;
|
|
|
|
this.red = this.green = this.blue = (float)(Math.random() * 0.30000001192092896D);
|
|
|
|
this.scale *= 0.75F;
|
|
|
|
this.baseScale = this.scale;
|
|
|
|
this.lifetime = (int)(8.0D / (Math.random() * 0.8D + 0.2D));
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setScale(float partialTicks) {
|
|
|
|
float f = ((float)this.age + partialTicks) / (float)this.lifetime * 32.0F;
|
|
|
|
f = ExtMath.clampf(f, 0.0F, 1.0F);
|
|
|
|
this.scale = this.baseScale * f;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean onUpdate() {
|
|
|
|
this.prevX = this.posX;
|
|
|
|
this.prevY = this.posY;
|
|
|
|
this.prevZ = this.posZ;
|
|
|
|
|
|
|
|
if(this.age++ >= this.lifetime) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setUV(Math.max(0, 7 - this.age * 8 / this.lifetime), 0);
|
|
|
|
this.motionY += 0.004D;
|
|
|
|
this.move(this.motionX, this.motionY, this.motionZ);
|
|
|
|
|
|
|
|
if(this.posY == this.prevY) {
|
|
|
|
this.motionX *= 1.1D;
|
|
|
|
this.motionZ *= 1.1D;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.motionX *= 0.9599999785423279D;
|
|
|
|
this.motionY *= 0.9599999785423279D;
|
|
|
|
this.motionZ *= 0.9599999785423279D;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class Spell extends Particle {
|
2025-07-10 00:22:29 +02:00
|
|
|
private final boolean effect;
|
|
|
|
|
2025-07-10 18:24:08 +02:00
|
|
|
protected Spell(double xCoordIn, double yCoordIn, double zCoordIn, ItemPotion potion) {
|
|
|
|
this(xCoordIn, yCoordIn, zCoordIn, potion, rng.doublev() * 4.0D, rng.doublev() * Math.PI * 2.0D);
|
2025-07-09 19:41:37 +02:00
|
|
|
}
|
|
|
|
|
2025-07-10 18:24:08 +02:00
|
|
|
private Spell(double xCoordIn, double yCoordIn, double zCoordIn, ItemPotion potion,
|
2025-07-09 19:41:37 +02:00
|
|
|
double d22, double d23) {
|
|
|
|
super(xCoordIn + Math.cos(d23) * d22 * 0.1, yCoordIn + 0.3, zCoordIn + Math.sin(d23) * d22 * 0.1, 0.5 - rng.doublev(),
|
|
|
|
0.01 + rng.doublev() * 0.5, 0.5 - rng.doublev());
|
|
|
|
this.motionY *= 0.20000000298023224D;
|
|
|
|
|
|
|
|
if(Math.cos(d23) * d22 == 0.0D && Math.sin(d23) * d22 == 0.0D) {
|
|
|
|
this.motionX *= 0.10000000149011612D;
|
|
|
|
this.motionZ *= 0.10000000149011612D;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.scale *= 0.75F;
|
|
|
|
this.lifetime = (int)(8.0D / (Math.random() * 0.8D + 0.2D));
|
|
|
|
|
|
|
|
float f = 1.0F;
|
|
|
|
float f1 = 1.0F;
|
|
|
|
float f2 = 1.0F;
|
2025-07-10 00:22:29 +02:00
|
|
|
if(this.effect = potion.getEffect() != null) {
|
|
|
|
int j1 = potion.getEffect().getPotion().getColor();
|
2025-07-09 19:41:37 +02:00
|
|
|
f = (float)(j1 >> 16 & 255) / 255.0F;
|
|
|
|
f1 = (float)(j1 >> 8 & 255) / 255.0F;
|
|
|
|
f2 = (float)(j1 >> 0 & 255) / 255.0F;
|
|
|
|
}
|
2025-07-10 00:22:29 +02:00
|
|
|
else {
|
|
|
|
this.setUV(1 + rng.zrange(3), 1);
|
|
|
|
}
|
2025-07-09 19:41:37 +02:00
|
|
|
this.motionX *= d22;
|
|
|
|
this.motionY = (this.motionY - 0.10000000149011612D) * d22 + 0.10000000149011612D;
|
|
|
|
this.motionZ *= d22;
|
|
|
|
float f3 = 0.75F + rng.floatv() * 0.25F;
|
|
|
|
this.red = f * f3;
|
|
|
|
this.green = f1 * f3;
|
|
|
|
this.blue = f2 * f3;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean onUpdate() {
|
|
|
|
this.prevX = this.posX;
|
|
|
|
this.prevY = this.posY;
|
|
|
|
this.prevZ = this.posZ;
|
|
|
|
|
|
|
|
if(this.age++ >= this.lifetime) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2025-07-10 00:22:29 +02:00
|
|
|
if(this.effect) {
|
|
|
|
this.setUV(Math.max(0, 7 - this.age * 8 / this.lifetime), 3);
|
|
|
|
this.motionY += 0.004;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
this.motionY -= 0.004;
|
|
|
|
}
|
2025-07-09 19:41:37 +02:00
|
|
|
this.move(this.motionX, this.motionY, this.motionZ);
|
|
|
|
|
|
|
|
if(this.posY == this.prevY) {
|
|
|
|
this.motionX *= 1.1D;
|
|
|
|
this.motionZ *= 1.1D;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.motionX *= 0.9599999785423279D;
|
|
|
|
this.motionY *= 0.9599999785423279D;
|
|
|
|
this.motionZ *= 0.9599999785423279D;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class Icon extends Moveable {
|
2025-07-20 14:56:03 +02:00
|
|
|
private final Sprite icon;
|
2025-07-09 19:41:37 +02:00
|
|
|
private final float offsetX;
|
|
|
|
private final float offsetY;
|
|
|
|
private final float size;
|
|
|
|
|
|
|
|
protected float red;
|
|
|
|
protected float green;
|
|
|
|
protected float blue;
|
|
|
|
|
2025-07-20 14:56:03 +02:00
|
|
|
private Icon(double x, double y, double z, float size, Sprite icon) {
|
2025-07-10 18:24:08 +02:00
|
|
|
super(x, y, z, 0.0, 0.0, 0.0);
|
2025-07-09 19:41:37 +02:00
|
|
|
this.size = (rng.floatv() * 0.5F + 0.5F) * size * 0.1f;
|
|
|
|
this.icon = icon;
|
|
|
|
this.offsetX = rng.floatv() * 3.0F;
|
|
|
|
this.offsetY = rng.floatv() * 3.0F;
|
|
|
|
}
|
|
|
|
|
2025-07-21 19:54:21 +02:00
|
|
|
protected Icon(double x, double y, double z, State state, boolean hit) {
|
2025-08-06 13:53:25 +02:00
|
|
|
this(x, y, z, hit ? 0.6f : 1.0f, Client.CLIENT.renderer.getModelManager().getTexture(state));
|
2025-07-09 19:41:37 +02:00
|
|
|
this.gravity = 1.0F;
|
|
|
|
this.red = this.green = this.blue = 0.6F;
|
|
|
|
if(hit) {
|
|
|
|
this.motionX *= 0.2;
|
|
|
|
this.motionY = (this.motionY - 0.1) * 0.2 + 0.1;
|
|
|
|
this.motionZ *= 0.2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-07-10 18:24:08 +02:00
|
|
|
protected Icon(double x, double y, double z, Item item) {
|
|
|
|
this(x, y, z, 1.0f, Client.CLIENT.getRenderItem().getItemModelMesher().getParticleIcon(item));
|
2025-07-09 19:41:37 +02:00
|
|
|
this.red = this.green = this.blue = 1.0F;
|
|
|
|
this.gravity = 1.0F;
|
|
|
|
this.motionX *= 0.10000000149011612D;
|
|
|
|
this.motionY *= 0.10000000149011612D;
|
|
|
|
this.motionZ *= 0.10000000149011612D;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final int getLayer() {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final void render(RenderBuffer rb, float partial, float rotX, float rotZ, float rotYZ, float rotXY, float rotXZ) {
|
|
|
|
float u1 = this.icon.getInterpolatedU((double)(this.offsetX * 4.0F));
|
|
|
|
float u2 = this.icon.getInterpolatedU((double)((this.offsetX + 1.0F) * 4.0F));
|
|
|
|
float v1 = this.icon.getInterpolatedV((double)(this.offsetY * 4.0F));
|
|
|
|
float v2 = this.icon.getInterpolatedV((double)((this.offsetY + 1.0F) * 4.0F));
|
|
|
|
float scale = this.size;
|
|
|
|
float x = (float)(this.prevX + (this.posX - this.prevX) * (double)partial - interpPosX);
|
|
|
|
float y = (float)(this.prevY + (this.posY - this.prevY) * (double)partial - interpPosY);
|
|
|
|
float z = (float)(this.prevZ + (this.posZ - this.prevZ) * (double)partial - interpPosZ);
|
|
|
|
int light = this.getBrightness(partial);
|
|
|
|
int sky = light >> 16 & 65535;
|
|
|
|
int block = light & 65535;
|
|
|
|
rb.pos((double)(x - rotX * scale - rotXY * scale), (double)(y - rotZ * scale), (double)(z - rotYZ * scale - rotXZ * scale))
|
|
|
|
.tex((double)u1, (double)v2).color(this.red, this.green, this.blue, 1.0F).lightmap(sky, block).endVertex();
|
|
|
|
rb.pos((double)(x - rotX * scale + rotXY * scale), (double)(y + rotZ * scale), (double)(z - rotYZ * scale + rotXZ * scale))
|
|
|
|
.tex((double)u1, (double)v1).color(this.red, this.green, this.blue, 1.0F).lightmap(sky, block).endVertex();
|
|
|
|
rb.pos((double)(x + rotX * scale + rotXY * scale), (double)(y + rotZ * scale), (double)(z + rotYZ * scale + rotXZ * scale))
|
|
|
|
.tex((double)u2, (double)v1).color(this.red, this.green, this.blue, 1.0F).lightmap(sky, block).endVertex();
|
|
|
|
rb.pos((double)(x + rotX * scale - rotXY * scale), (double)(y - rotZ * scale), (double)(z + rotYZ * scale - rotXZ * scale))
|
|
|
|
.tex((double)u2, (double)v2).color(this.red, this.green, this.blue, 1.0F).lightmap(sky, block).endVertex();
|
|
|
|
}
|
|
|
|
|
|
|
|
public final boolean onUpdate() {
|
|
|
|
this.prevX = this.posX;
|
|
|
|
this.prevY = this.posY;
|
|
|
|
this.prevZ = this.posZ;
|
|
|
|
if(this.age++ >= this.lifetime)
|
|
|
|
return true;
|
|
|
|
this.motionY -= 0.04D * (double)this.gravity;
|
|
|
|
this.move(this.motionX, this.motionY, this.motionZ);
|
|
|
|
this.motionX *= 0.9800000190734863D;
|
|
|
|
this.motionY *= 0.9800000190734863D;
|
|
|
|
this.motionZ *= 0.9800000190734863D;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-07-10 18:24:08 +02:00
|
|
|
private class Textured implements Effect {
|
2025-07-09 19:41:37 +02:00
|
|
|
private static final VertexFormat VERTEX_FORMAT = (new VertexFormat()).addElement(DefaultVertexFormats.POSITION_3F)
|
|
|
|
.addElement(DefaultVertexFormats.TEX_2F).addElement(DefaultVertexFormats.COLOR_4UB).addElement(DefaultVertexFormats.TEX_2S)
|
|
|
|
.addElement(DefaultVertexFormats.NORMAL_3B).addElement(DefaultVertexFormats.PADDING_1B);
|
|
|
|
|
|
|
|
private final String texture;
|
2025-07-10 18:24:08 +02:00
|
|
|
private final int texWidth;
|
|
|
|
private final int texHeight;
|
2025-07-09 19:41:37 +02:00
|
|
|
private final float scale;
|
|
|
|
private final float brightness;
|
|
|
|
private final double posX;
|
|
|
|
private final double posY;
|
|
|
|
private final double posZ;
|
|
|
|
|
|
|
|
private int age;
|
|
|
|
private int lifetime;
|
|
|
|
private TextureManager manager;
|
|
|
|
|
2025-07-10 18:24:08 +02:00
|
|
|
protected Textured(TextureManager manager, double x, double y, double z, float scale, String texture, int width, int height) {
|
2025-07-09 19:41:37 +02:00
|
|
|
this.manager = manager;
|
|
|
|
this.lifetime = 6 + rng.zrange(4);
|
|
|
|
this.brightness = rng.floatv() * 0.6F + 0.4F;
|
2025-07-10 18:24:08 +02:00
|
|
|
this.scale = scale;
|
|
|
|
this.texture = "textures/world/" + texture + ".png";
|
|
|
|
this.texWidth = width;
|
|
|
|
this.texHeight = height;
|
2025-07-09 19:41:37 +02:00
|
|
|
this.posX = x;
|
|
|
|
this.posY = y;
|
|
|
|
this.posZ = z;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final void render(RenderBuffer rb, float partial, float rotX, float rotZ, float rotYZ, float rotXY, float rotXZ) {
|
2025-07-10 18:24:08 +02:00
|
|
|
int idx = (int)(((float)this.age + partial) * (float)(this.texWidth * this.texHeight - 1) / (float)this.lifetime);
|
|
|
|
if(idx < this.texWidth * this.texHeight) {
|
2025-07-09 19:41:37 +02:00
|
|
|
this.manager.bindTexture(this.texture);
|
2025-07-10 18:24:08 +02:00
|
|
|
float u1 = (float)(idx % this.texWidth) / (float)this.texWidth;
|
|
|
|
float u2 = u1 + 1.0f / (float)this.texWidth;
|
|
|
|
float v1 = (float)(idx / this.texWidth) / (float)this.texHeight;
|
|
|
|
float v2 = v1 + 1.0f / (float)this.texHeight;
|
|
|
|
float scale = 2.0F * (1.0F - this.scale * 0.5F);
|
|
|
|
float x = (float)(this.posX - interpPosX);
|
|
|
|
float y = (float)(this.posY - interpPosY);
|
|
|
|
float z = (float)(this.posZ - interpPosZ);
|
2025-07-09 19:41:37 +02:00
|
|
|
GlState.color(1.0F, 1.0F, 1.0F, 1.0F);
|
|
|
|
GlState.disableLighting();
|
|
|
|
ItemRenderer.disableStandardItemLighting();
|
2025-08-28 15:04:19 +02:00
|
|
|
rb.begin(GL46.GL_QUADS, VERTEX_FORMAT);
|
2025-07-10 18:24:08 +02:00
|
|
|
rb.pos((double)(x - rotX * scale - rotXY * scale), (double)(y - rotZ * scale), (double)(z - rotYZ * scale - rotXZ * scale))
|
|
|
|
.tex((double)u2, (double)v2).color(this.brightness, this.brightness, this.brightness, 1.0F).lightmap(0, 240)
|
2025-07-09 19:41:37 +02:00
|
|
|
.normal(0.0F, 1.0F, 0.0F).endVertex();
|
2025-07-10 18:24:08 +02:00
|
|
|
rb.pos((double)(x - rotX * scale + rotXY * scale), (double)(y + rotZ * scale), (double)(z - rotYZ * scale + rotXZ * scale))
|
|
|
|
.tex((double)u2, (double)v1).color(this.brightness, this.brightness, this.brightness, 1.0F).lightmap(0, 240)
|
2025-07-09 19:41:37 +02:00
|
|
|
.normal(0.0F, 1.0F, 0.0F).endVertex();
|
2025-07-10 18:24:08 +02:00
|
|
|
rb.pos((double)(x + rotX * scale + rotXY * scale), (double)(y + rotZ * scale), (double)(z + rotYZ * scale + rotXZ * scale))
|
|
|
|
.tex((double)u1, (double)v1).color(this.brightness, this.brightness, this.brightness, 1.0F).lightmap(0, 240)
|
2025-07-09 19:41:37 +02:00
|
|
|
.normal(0.0F, 1.0F, 0.0F).endVertex();
|
2025-07-10 18:24:08 +02:00
|
|
|
rb.pos((double)(x + rotX * scale - rotXY * scale), (double)(y - rotZ * scale), (double)(z + rotYZ * scale - rotXZ * scale))
|
|
|
|
.tex((double)u1, (double)v2).color(this.brightness, this.brightness, this.brightness, 1.0F).lightmap(0, 240)
|
2025-07-09 19:41:37 +02:00
|
|
|
.normal(0.0F, 1.0F, 0.0F).endVertex();
|
|
|
|
Tessellator.draw();
|
|
|
|
GlState.enableLighting();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public final boolean onUpdate() {
|
|
|
|
if(++this.age >= this.lifetime)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final int getLayer() {
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-07-10 18:24:08 +02:00
|
|
|
private abstract class Spawner implements Effect {
|
|
|
|
protected final int amount;
|
|
|
|
protected final int lifetime;
|
|
|
|
|
|
|
|
protected int age;
|
2025-07-09 19:41:37 +02:00
|
|
|
|
|
|
|
protected Spawner(int amount, int lifetime) {
|
|
|
|
this.amount = amount;
|
|
|
|
this.lifetime = lifetime;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final boolean onUpdate() {
|
|
|
|
for(int z = 0; z < this.amount; z++) {
|
|
|
|
this.spawnParticle();
|
|
|
|
}
|
|
|
|
if(++this.age >= this.lifetime)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final int getLayer() {
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
public final void render(RenderBuffer rb, float partial, float rotX, float rotZ, float rotYZ, float rotXY, float rotXZ) {
|
|
|
|
}
|
|
|
|
|
|
|
|
protected abstract void spawnParticle();
|
|
|
|
}
|
|
|
|
|
|
|
|
private class HitSpawner extends Spawner {
|
|
|
|
private final Entity entity;
|
|
|
|
|
|
|
|
public HitSpawner(Entity entity) {
|
|
|
|
super(16, 3);
|
|
|
|
this.entity = entity;
|
|
|
|
this.onUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void spawnParticle() {
|
|
|
|
double x = (double)(rng.floatv() * 2.0F - 1.0F);
|
|
|
|
double y = (double)(rng.floatv() * 2.0F - 1.0F);
|
|
|
|
double z = (double)(rng.floatv() * 2.0F - 1.0F);
|
|
|
|
if(x * x + y * y + z * z <= 1.0D)
|
2025-07-23 14:23:06 +02:00
|
|
|
world.clientParticle(ParticleType.CRIT, this.entity.posX + x * (double)this.entity.width / 4.0D,
|
2025-07-09 19:41:37 +02:00
|
|
|
this.entity.getEntityBoundingBox().minY + (double)(this.entity.height / 2.0F) + y * (double)this.entity.height / 4.0D,
|
2025-07-10 18:24:08 +02:00
|
|
|
this.entity.posZ + z * (double)this.entity.width / 4.0D);
|
2025-07-09 19:41:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class ExplosionSpawner extends Spawner {
|
|
|
|
private final double posX;
|
|
|
|
private final double posY;
|
|
|
|
private final double posZ;
|
|
|
|
|
|
|
|
protected ExplosionSpawner(double x, double y, double z) {
|
|
|
|
super(6, 8);
|
|
|
|
this.posX = x;
|
|
|
|
this.posY = y;
|
|
|
|
this.posZ = z;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void spawnParticle() {
|
|
|
|
double x = this.posX + (rng.doublev() - rng.doublev()) * 4.0D;
|
|
|
|
double y = this.posY + (rng.doublev() - rng.doublev()) * 4.0D;
|
|
|
|
double z = this.posZ + (rng.doublev() - rng.doublev()) * 4.0D;
|
2025-07-23 14:23:06 +02:00
|
|
|
world.clientParticle(ParticleType.EXPLOSION_LARGE, x, y, z, (int)(100.0f * (float)this.age / (float)this.lifetime));
|
2025-07-09 19:41:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private interface Creator {
|
2025-07-10 18:24:08 +02:00
|
|
|
Effect create(double x, double y, double z, int data);
|
2025-07-09 19:41:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static final String TEXTURE = "textures/world/particles.png";
|
|
|
|
|
|
|
|
private final Random rng = new Random();
|
|
|
|
private final List<Effect>[] layers = new List[4];
|
|
|
|
private final TextureManager manager;
|
|
|
|
private final Random rand = new Random();
|
|
|
|
private final Map<ParticleType, Creator> types = Maps.<ParticleType, Creator>newEnumMap(ParticleType.class);
|
|
|
|
|
|
|
|
private World world;
|
|
|
|
private double interpPosX;
|
|
|
|
private double interpPosY;
|
|
|
|
private double interpPosZ;
|
|
|
|
|
|
|
|
public EffectRenderer(TextureManager manager) {
|
|
|
|
this.manager = manager;
|
|
|
|
for(int i = 0; i < this.layers.length; ++i) {
|
|
|
|
this.layers[i] = Lists.newArrayList();
|
|
|
|
}
|
|
|
|
this.registerEffects();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void register(ParticleType type, Creator factory) {
|
|
|
|
this.types.put(type, factory);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void registerEffects() {
|
2025-07-10 18:24:08 +02:00
|
|
|
this.register(ParticleType.EXPLOSION_NORMAL, (x, y, z, u) -> new Explosion(x, y, z));
|
|
|
|
this.register(ParticleType.SPLASH, (x, y, z, u) -> new Downfall(x, y, z));
|
|
|
|
this.register(ParticleType.WATER_DROP, (x, y, z, u) -> new Downfall(x, y, z, 0));
|
|
|
|
this.register(ParticleType.DEPTH, (x, y, z, u) -> new Aura(x, y, z, true, 0));
|
|
|
|
this.register(ParticleType.CRIT, (x, y, z, u) -> new Crit(x, y, z));
|
|
|
|
this.register(ParticleType.SMOKE, (x, y, z, u) -> new Smoke(x, y, z));
|
|
|
|
this.register(ParticleType.POTION, (x, y, z, id) -> new Spell(x, y, z, ItemPotion.getPotionItem(id)));
|
|
|
|
this.register(ParticleType.GROW, (x, y, z, u) -> new Aura(x, y, z, false, 1));
|
|
|
|
this.register(ParticleType.SPORE, (x, y, z, u) -> new Aura(x, y, z, false, 0));
|
|
|
|
this.register(ParticleType.TELEPORT, (x, y, z, u) -> new Teleport(x, y, z));
|
|
|
|
this.register(ParticleType.FLAME, (x, y, z, u) -> new Flame(x, y, z));
|
|
|
|
this.register(ParticleType.LAVA, (x, y, z, u) -> new LavaPop(x, y, z));
|
|
|
|
this.register(ParticleType.DUST, (x, y, z, color) -> new Dust(x, y, z, color));
|
|
|
|
this.register(ParticleType.HEART, (x, y, z, u) -> new Heart(x, y, z));
|
|
|
|
this.register(ParticleType.ITEM_CRACK, (x, y, z, id) -> {
|
|
|
|
Item item = ItemRegistry.byId(id);
|
|
|
|
return item == null ? null : new Icon(x, y, z, item);
|
2025-07-09 19:41:37 +02:00
|
|
|
});
|
2025-07-10 18:24:08 +02:00
|
|
|
this.register(ParticleType.BLOCK_CRACK, (x, y, z, id) -> {
|
|
|
|
State state = BlockRegistry.byId(id);
|
2025-07-21 19:54:21 +02:00
|
|
|
return state == null ? null : new Icon(x, y, z, state, false);
|
2025-07-09 19:41:37 +02:00
|
|
|
});
|
2025-07-10 18:24:08 +02:00
|
|
|
this.register(ParticleType.EXPLOSION_HUGE, (x, y, z, u) -> new ExplosionSpawner(x, y, z));
|
2025-07-09 19:41:37 +02:00
|
|
|
this.register(ParticleType.EXPLOSION_LARGE,
|
2025-07-10 18:24:08 +02:00
|
|
|
(x, y, z, scale) -> new Textured(this.manager, x, y, z, (float)scale / 100.0f, "explosion", 4, 4));
|
|
|
|
this.register(ParticleType.HAIL_CORN, (x, y, z, u) -> new Downfall(x, y, z, 1));
|
2025-07-09 19:41:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void setWorld(World world) {
|
|
|
|
this.world = world;
|
|
|
|
for(int i = 0; i < this.layers.length; ++i) {
|
|
|
|
this.layers[i].clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void update() {
|
|
|
|
for(int i = 0; i < this.layers.length; ++i) {
|
|
|
|
List<Effect> layer = this.layers[i];
|
|
|
|
List<Effect> list = Lists.<Effect>newArrayList();
|
|
|
|
for(int n = 0; n < layer.size(); ++n) {
|
|
|
|
Effect effect = layer.get(n);
|
|
|
|
if(effect.onUpdate())
|
|
|
|
list.add(effect);
|
|
|
|
}
|
|
|
|
layer.removeAll(list);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void render(Entity entity, float partial) {
|
|
|
|
float f = MatrixState.getRotationX();
|
|
|
|
float f1 = MatrixState.getRotationZ();
|
|
|
|
float f2 = MatrixState.getRotationYZ();
|
|
|
|
float f3 = MatrixState.getRotationXY();
|
|
|
|
float f4 = MatrixState.getRotationXZ();
|
|
|
|
this.interpPosX = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * (double)partial;
|
|
|
|
this.interpPosY = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * (double)partial;
|
|
|
|
this.interpPosZ = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * (double)partial;
|
|
|
|
GlState.enableBlend();
|
2025-08-28 15:04:19 +02:00
|
|
|
GlState.blendFunc(GL46.GL_SRC_ALPHA, GL46.GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
GlState.alphaFunc(GL46.GL_GREATER, 0.003921569F);
|
2025-07-09 19:41:37 +02:00
|
|
|
|
|
|
|
for(int i = 0; i < 2; ++i) {
|
|
|
|
if(!this.layers[i].isEmpty()) {
|
|
|
|
GlState.depthMask(true);
|
|
|
|
|
|
|
|
switch(i) {
|
|
|
|
case 0:
|
|
|
|
default:
|
|
|
|
this.manager.bindTexture(TEXTURE);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
this.manager.bindTexture(TextureMap.BLOCKS);
|
|
|
|
}
|
|
|
|
|
|
|
|
GlState.color(1.0F, 1.0F, 1.0F, 1.0F);
|
|
|
|
RenderBuffer worldrenderer = Tessellator.getBuffer();
|
2025-08-28 15:04:19 +02:00
|
|
|
worldrenderer.begin(GL46.GL_QUADS, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP);
|
2025-07-09 19:41:37 +02:00
|
|
|
|
|
|
|
for(int k = 0; k < this.layers[i].size(); ++k) {
|
|
|
|
this.layers[i].get(k).render(worldrenderer, partial, f, f4, f1, f2, f3);
|
|
|
|
}
|
|
|
|
|
|
|
|
Tessellator.draw();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GlState.depthMask(true);
|
|
|
|
GlState.disableBlend();
|
2025-08-28 15:04:19 +02:00
|
|
|
GlState.alphaFunc(GL46.GL_GREATER, 0.1F);
|
2025-07-09 19:41:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void renderTextured(Entity entity, float partial) {
|
|
|
|
float f = 0.017453292F;
|
|
|
|
float f1 = ExtMath.cos(entity.rotYaw * 0.017453292F);
|
|
|
|
float f2 = ExtMath.sin(entity.rotYaw * 0.017453292F);
|
|
|
|
float f3 = -f2 * ExtMath.sin(entity.rotPitch * 0.017453292F);
|
|
|
|
float f4 = f1 * ExtMath.sin(entity.rotPitch * 0.017453292F);
|
|
|
|
float f5 = ExtMath.cos(entity.rotPitch * 0.017453292F);
|
|
|
|
|
|
|
|
List<Effect> list = this.layers[2];
|
|
|
|
|
|
|
|
if(!list.isEmpty()) {
|
|
|
|
RenderBuffer worldrenderer = Tessellator.getBuffer();
|
|
|
|
|
|
|
|
for(int j = 0; j < list.size(); ++j) {
|
|
|
|
list.get(j).render(worldrenderer, partial, f1, f5, f2, f3, f4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void add(Effect effect) {
|
|
|
|
int i = effect.getLayer();
|
|
|
|
if(this.layers[i].size() >= 4000)
|
|
|
|
this.layers[i].remove(0);
|
|
|
|
this.layers[i].add(effect);
|
|
|
|
}
|
|
|
|
|
2025-07-23 13:19:32 +02:00
|
|
|
public void spawnParticle(Entity entity, ParticleType type, double x, double y, double z, int data) {
|
|
|
|
if(entity != null) {
|
|
|
|
double dx = entity.posX - x;
|
|
|
|
double dy = entity.posY - y;
|
|
|
|
double dz = entity.posZ - z;
|
|
|
|
if(type.isUnlimited() || dx * dx + dy * dy + dz * dz <= 256.0D) {
|
|
|
|
Creator creator = this.types.get(type);
|
|
|
|
if(creator != null) {
|
|
|
|
Effect effect = creator.create(x, y, z, data);
|
|
|
|
if(effect != null)
|
|
|
|
this.add(effect);
|
|
|
|
}
|
|
|
|
}
|
2025-07-09 19:41:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void destroyBlock(BlockPos pos, State state) {
|
|
|
|
if(state.getBlock() != Blocks.air) {
|
2025-07-20 14:20:55 +02:00
|
|
|
state = state.getBlock().getState(state, this.world, pos);
|
2025-07-09 19:41:37 +02:00
|
|
|
int i = 4;
|
|
|
|
|
|
|
|
for(int j = 0; j < i; ++j) {
|
|
|
|
for(int k = 0; k < i; ++k) {
|
|
|
|
for(int l = 0; l < i; ++l) {
|
|
|
|
double d0 = (double)pos.getX() + ((double)j + 0.5D) / (double)i;
|
|
|
|
double d1 = (double)pos.getY() + ((double)k + 0.5D) / (double)i;
|
|
|
|
double d2 = (double)pos.getZ() + ((double)l + 0.5D) / (double)i;
|
2025-07-21 19:54:21 +02:00
|
|
|
this.add(new Icon(d0, d1, d2, state, false));
|
2025-07-09 19:41:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void damageBlock(BlockPos pos, Facing side) {
|
|
|
|
State iblockstate = this.world.getState(pos);
|
|
|
|
Block block = iblockstate.getBlock();
|
|
|
|
|
2025-07-25 10:32:13 +02:00
|
|
|
if(block != Blocks.air) {
|
2025-07-09 19:41:37 +02:00
|
|
|
int i = pos.getX();
|
|
|
|
int j = pos.getY();
|
|
|
|
int k = pos.getZ();
|
|
|
|
float f = 0.1F;
|
|
|
|
double d0 = (double)i + this.rng.doublev() * (block.getBlockBoundsMaxX() - block.getBlockBoundsMinX() - (double)(f * 2.0F)) + (double)f
|
|
|
|
+ block.getBlockBoundsMinX();
|
|
|
|
double d1 = (double)j + this.rng.doublev() * (block.getBlockBoundsMaxY() - block.getBlockBoundsMinY() - (double)(f * 2.0F)) + (double)f
|
|
|
|
+ block.getBlockBoundsMinY();
|
|
|
|
double d2 = (double)k + this.rng.doublev() * (block.getBlockBoundsMaxZ() - block.getBlockBoundsMinZ() - (double)(f * 2.0F)) + (double)f
|
|
|
|
+ block.getBlockBoundsMinZ();
|
|
|
|
|
|
|
|
if(side == Facing.DOWN) {
|
|
|
|
d1 = (double)j + block.getBlockBoundsMinY() - (double)f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(side == Facing.UP) {
|
|
|
|
d1 = (double)j + block.getBlockBoundsMaxY() + (double)f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(side == Facing.NORTH) {
|
|
|
|
d2 = (double)k + block.getBlockBoundsMinZ() - (double)f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(side == Facing.SOUTH) {
|
|
|
|
d2 = (double)k + block.getBlockBoundsMaxZ() + (double)f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(side == Facing.WEST) {
|
|
|
|
d0 = (double)i + block.getBlockBoundsMinX() - (double)f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(side == Facing.EAST) {
|
|
|
|
d0 = (double)i + block.getBlockBoundsMaxX() + (double)f;
|
|
|
|
}
|
|
|
|
|
2025-07-21 19:54:21 +02:00
|
|
|
this.add(new Icon(d0, d1, d2, iblockstate, true));
|
2025-07-09 19:41:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void spawnCritParticles(Entity entity) {
|
|
|
|
this.add(new HitSpawner(entity));
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getParticleCount() {
|
|
|
|
int i = 0;
|
|
|
|
for(int j = 0; j < 3; ++j) {
|
|
|
|
i += this.layers[j].size();
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|