Lightning Arc
Implement Lightning Arc that is able to hit an entity and then jump towards another target near it.
By default, a Lightning Arc can jump up to 1 target, allowing the attacker to damage 2 targets at once. However, there will be occasions where the Lightning Arc will not jump.
Additional Information
For the implementation of the Lightning Arc's renderer, the lightning will be drawn using this method:
/**
* Draws a lightning arc from the source point to the target point using
* a segmented polyline with random deviation, simulating a realistic
* lightning bolt effect.
*
* @param matrix The transformation matrix
* @param buffer The vertex consumer
* @param ps The source point (turret barrel)
* @param pe The target point (enemy)
* @param random The random instance for generating deviation
* @param width The base width of the lightning bolt
* @param maxDeviation The maximum perpendicular deviation of each point
*/
private static void drawLightningArc(Matrix4f matrix, VertexConsumer buffer, Vec3d ps, Vec3d pe, Random random, float width, float maxDeviation) {
// Compute perpendicular axes to the direction vector
Vec3d direction = pe.subtract(ps).normalize();
Vec3d up = new Vec3d(0, 1, 0);
Vec3d perpA = direction.crossProduct(up).normalize();
Vec3d perpB = direction.crossProduct(perpA).normalize();
// Generate skeleton points
int n = random.nextInt(5) + 1; // 1 <= n <= 5
List<Vec3d> points = new ArrayList<>();
points.add(ps);
for (int i = 1; i <= n; i++) {
float t = i / (float)(n + 1);
// Linearly interpolate along main axis
Vec3d interpolated = ps.add(pe.subtract(ps).multiply(t));
// Random perpendicular offset
double offsetA = (random.nextDouble() - 0.5) * maxDeviation;
double offsetB = (random.nextDouble() - 0.5) * maxDeviation;
points.add(interpolated
.add(perpA.multiply(offsetA))
.add(perpB.multiply(offsetB))
);
}
points.add(pe);
// Draw segments between consecutive points
float red = 0.6F, green = 0.8F, blue = 1.0F, alpha = 0.8F;
for (int i = 0; i < points.size() - 1; i++) {
Vec3d start = points.get(i);
Vec3d end = points.get(i + 1);
// Taper width from base to tip
float startWidth = width * (1.0F - (i / (float) points.size()));
float endWidth = width * (1.0F - ((i + 1) / (float) points.size()));
// Compute quad vertices using perpendicular axes
Vec3d s1 = start.add(perpA.multiply(startWidth)).add(perpB.multiply(startWidth));
Vec3d s2 = start.subtract(perpA.multiply(startWidth)).add(perpB.multiply(startWidth));
Vec3d e1 = end.subtract(perpA.multiply(endWidth)).subtract(perpB.multiply(endWidth));
Vec3d e2 = end.add(perpA.multiply(endWidth)).subtract(perpB.multiply(endWidth));
buffer.vertex(matrix, (float) s1.x, (float) s1.y, (float) s1.z).color(red, green, blue, alpha);
buffer.vertex(matrix, (float) s2.x, (float) s2.y, (float) s2.z).color(red, green, blue, alpha);
buffer.vertex(matrix, (float) e1.x, (float) e1.y, (float) e1.z).color(red, green, blue, alpha);
buffer.vertex(matrix, (float) e2.x, (float) e2.y, (float) e2.z).color(red, green, blue, alpha);
}
}
As for the LIPC, which is an optional addition to the rendering of the arc:
/**
* Draws the LIPC (Laser-Induced Plasma Channel) as a straight, thin line
* from the source point to the target point.
*
* @param matrix The transformation matrix
* @param buffer The vertex consumer
* @param ps The source point (turret barrel)
* @param pe The target point (enemy)
* @param width The width of the LIPC beam
*/
private static void drawLIPC(Matrix4f matrix, VertexConsumer buffer, Vec3d ps, Vec3d pe, float width) {
Vec3d direction = pe.subtract(ps).normalize();
Vec3d up = new Vec3d(0, 1, 0);
Vec3d perpA = direction.crossProduct(up).normalize();
Vec3d perpB = direction.crossProduct(perpA).normalize();
Vec3d p1 = ps.add(perpA.multiply(width)).add(perpB.multiply(width));
Vec3d p2 = ps.subtract(perpA.multiply(width)).add(perpB.multiply(width));
Vec3d p3 = pe.subtract(perpA.multiply(width)).subtract(perpB.multiply(width));
Vec3d p4 = pe.add(perpA.multiply(width)).subtract(perpB.multiply(width));
// Red-ish laser color, semi-transparent
float red = 1.0F, green = 0.2F, blue = 0.2F, alpha = 0.6F;
buffer.vertex(matrix, (float) p1.x, (float) p1.y, (float) p1.z).color(red, green, blue, alpha);
buffer.vertex(matrix, (float) p2.x, (float) p2.y, (float) p2.z).color(red, green, blue, alpha);
buffer.vertex(matrix, (float) p3.x, (float) p3.y, (float) p3.z).color(red, green, blue, alpha);
buffer.vertex(matrix, (float) p4.x, (float) p4.y, (float) p4.z).color(red, green, blue, alpha);
}
Lightning Arc
Implement Lightning Arc that is able to hit an entity and then jump towards another target near it.
By default, a Lightning Arc can jump up to 1 target, allowing the attacker to damage 2 targets at once. However, there will be occasions where the Lightning Arc will not jump.
Additional Information
For the implementation of the Lightning Arc's renderer, the lightning will be drawn using this method:
As for the LIPC, which is an optional addition to the rendering of the arc: