All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pepsoft.worldpainter.operations.RiverPaint Maven / Gradle / Ivy

There is a newer version: 2.23.2
Show newest version
package org.pepsoft.worldpainter.operations;

import org.pepsoft.worldpainter.*;
import org.pepsoft.worldpainter.layers.FloodWithLava;

/**
 * A tool for creating rivers. It floods an area defined by where the brush
 * intensity is at least 25% to a depth defined by where you first clicked.
 * The level to which it is flooded can only decrease while the mouse is held
 * down. The terrain is only ever lowered, not raised (which means the level of
 * the water is the highest at which it will not spill over).
 *
 * Created by Pepijn Schmitz on 30-09-15.
 */
public class RiverPaint extends RadiusOperation {
    public RiverPaint(WorldPainterView view, RadiusControl radiusControl, MapDragControl mapDragControl) {
        super("RiverPaint", "Paint a river of water or lava", view, radiusControl, mapDragControl, 100, "operation.riverPaint", "river");
    }

    @Override
    protected void tick(int centreX, int centreY, boolean inverse, boolean first, float dynamicLevel) {
        final Dimension dim = getDimension();
        if (dim == null) {
            // Probably some kind of race condition
            return;
        }
        if (first) {
            // Measure depth and fluid type at first click
            previousWaterLevel = dim.getWaterLevelAt(centreX, centreY);
            depth = (previousWaterLevel - dim.getHeightAt(centreX, centreY)) / getFullStrength(centreX, centreY, centreX, centreY);
            lava = dim.getBitLayerValueAt(FloodWithLava.INSTANCE, centreX, centreY);
//            System.out.println("previousWaterLevel: " + previousWaterLevel + ", height: " + dim.getHeightAt(centreX, centreY) + ", depth: " + depth + ", brush strength at centre: " + getFullStrength(centreX, centreY, centreX, centreY) + ", lava: " + lava);
        }
        if (depth < 0) {
            return;
        }
        int r = getEffectiveRadius();

        // Step 1: determine the water level by finding the lowest block along the edge of the part which should be
        // flooded (the part where the brush is at 25% intensity or higher)
        int waterLevel = Integer.MAX_VALUE;
        for (int x = centreX - r; x <= centreX + r; x++) {
            for (int y = centreY - r; y <= centreY + r; y++) {
                int height;
                if ((! shouldFlood(centreX, centreY, x, y, r))
                        && (dim.getWaterLevelAt(x, y) < (height = dim.getIntHeightAt(x, y)))
                        && (height < waterLevel)
                        && (shouldFlood(centreX, centreY, x - 1, y, r) || shouldFlood(centreX, centreY, x, y - 1, r) || shouldFlood(centreX, centreY, x + 1, y, r) || shouldFlood(centreX, centreY, x, y + 1, r))) {
                    // Edge block; the water level must not be higher than the
                    // lowest edge block so it doesn't spill over
                    waterLevel = height;
                }
            }
        }
        // Only lower the water level during each drag, never raise
        if (waterLevel > previousWaterLevel) {
            waterLevel = previousWaterLevel;
        } else {
            previousWaterLevel = waterLevel;
        }

        // Step 2: lower the terrain and flood with water or lava
        dim.setEventsInhibited(true);
        try {
            for (int x = centreX - r; x <= centreX + r; x++) {
                for (int y = centreY - r; y <= centreY + r; y++) {
                    float strength = getFullStrength(centreX, centreY, x, y);
                    if (shouldFlood(centreX, centreY, x, y, r)) {
                        // Should be flooded; lower terrain and add water or lava
                        float requiredHeight = waterLevel - strength / 0.75f * depth;
                        if (dim.getHeightAt(x, y) > requiredHeight) {
                            dim.setHeightAt(x, y, requiredHeight);
                        }
                        dim.setWaterLevelAt(x, y, waterLevel);
                        dim.setBitLayerValueAt(FloodWithLava.INSTANCE, x, y, lava);
                        if (! lava) {
                            dim.setTerrainAt(x, y, Terrain.BEACHES);
                        }
                    } else if (strength > 0.0f) {
                        // Should not be flooded; lower terrain proportionally and
                        // leave existing fluids alone
                        float maximumHeight = waterLevel + (float) (Math.tan(-strength * DOUBLE_PI + HALF_PI) / DOUBLE_PI);
                        if (dim.getHeightAt(x, y) > maximumHeight) {
                            dim.setHeightAt(x, y, maximumHeight);
                        }
                        if ((! lava) && ((maximumHeight - waterLevel) < 2)) {
                            dim.setTerrainAt(x, y, Terrain.BEACHES);
                        }
                    }
                }
            }
        } finally {
            dim.setEventsInhibited(false);
        }
    }

    private boolean shouldFlood(int centreX, int centreY, int x, int y, int r) {
        int dx = Math.abs(x - centreX), dy = Math.abs(y - centreY);
        return (dx <= r) && (dy <= r) && (getFullStrength(centreX, centreY, x, y) > 0.25f);
    }

    private float depth;
    private int previousWaterLevel;
    private boolean lava;

    private static final double DOUBLE_PI = Math.PI * 2;
    private static final double HALF_PI = Math.PI / 2;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy