Skip to content

Implement Lightning Arc #51

Description

@Virus5600

Lightning Arc

Implement Lightning Arc that is able to hit an entity and then jump towards another target near it.

Image

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);
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

additionNew feature or request

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions