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

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

There is a newer version: 2.23.2
Show newest version
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.pepsoft.worldpainter.operations;

import org.pepsoft.worldpainter.Dimension;
import org.pepsoft.worldpainter.WorldPainter;
import org.pepsoft.worldpainter.layers.FloodWithLava;
import org.pepsoft.worldpainter.painting.GeneralQueueLinearFloodFiller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import java.awt.*;

import static org.pepsoft.util.swing.MessageUtils.showWarning;
import static org.pepsoft.worldpainter.Constants.TILE_SIZE;

/**
 *
 * @author pepijn
 */
public class Flood extends MouseOrTabletOperation {
    public Flood(WorldPainter view, boolean floodWithLava) {
        super(floodWithLava ? "Lava" : "Flood", "Flood an area with " + (floodWithLava ? "lava" : "water"),
                view,
                "operation.flood." + (floodWithLava ? "lava" : "water"),
                floodWithLava ? "flood_with_lava" : "flood");
        this.floodWithLava = floodWithLava;
        optionsPanel = floodWithLava
                ? new StandardOptionsPanel("Flood with Lava", "
  • Left-click on dry land to flood with lava\n" + "
  • Left-click on lava to raise it by one\n" + "
  • Right-click on lava to lower it by one\n" + "
  • Click on water to turn it to lava\n" + "
") : new StandardOptionsPanel("Flood with Water", "
  • Left-click on dry land to flood with water\n" + "
  • Left-click on water to raise it by one\n" + "
  • Right-click on water to lower it by one\n" + "
  • Click on lava to turn it to water\n" + "
"); } @Override protected void tick(int centreX, int centreY, boolean inverse, boolean first, float dynamicLevel) { final Dimension dimension = getDimension(); if (dimension == null) { // Probably some kind of race condition return; } // We have seen in the wild that this sometimes gets called recursively (perhaps someone clicks to flood more // than once and then it takes more than two seconds so it is continued in the background and event queue // processing is resumed?), which causes errors, so just ignore it if we are already flooding. if (alreadyFlooding) { logger.debug("Flood operation already in progress; ignoring repeated invocation"); return; } alreadyFlooding = true; try { final Rectangle dimensionBounds = new Rectangle(dimension.getLowestX() * TILE_SIZE, dimension.getLowestY() * TILE_SIZE, dimension.getWidth() * TILE_SIZE, dimension.getHeight() * TILE_SIZE); final int terrainHeight = dimension.getIntHeightAt(centreX, centreY); if (terrainHeight == Integer.MIN_VALUE) { // Not on a tile return; } final int waterLevel = dimension.getWaterLevelAt(centreX, centreY); final boolean fluidPresent = waterLevel > terrainHeight; if (inverse && (! fluidPresent)) { // No point lowering the water level if there is no water... return; } final GeneralQueueLinearFloodFiller.FillMethod fillMethod; if (fluidPresent && (floodWithLava != dimension.getBitLayerValueAt(FloodWithLava.INSTANCE, centreX, centreY))) { // There is fluid present of a different type; don't change the // height, just change the type if (floodWithLava) { fillMethod = new FloodFillMethod("Changing water to lava", dimensionBounds) { @Override public boolean isBoundary(int x, int y) { final int height = dimension.getIntHeightAt(x, y); return (height == Integer.MIN_VALUE) // Not on a tile || (dimension.getWaterLevelAt(x, y) <= height) // Not flooded || dimension.getBitLayerValueAt(FloodWithLava.INSTANCE, x, y); // Not water } @Override public void fill(int x, int y) { dimension.setBitLayerValueAt(FloodWithLava.INSTANCE, x, y, true); } }; } else { fillMethod = new FloodFillMethod("Changing lava to water", dimensionBounds) { @Override public boolean isBoundary(int x, int y) { final int height = dimension.getIntHeightAt(x, y); return (height == Integer.MIN_VALUE) // Not on a tile || (dimension.getWaterLevelAt(x, y) <= height) // Not flooded || (! dimension.getBitLayerValueAt(FloodWithLava.INSTANCE, x, y)); // Not lava } @Override public void fill(int x, int y) { dimension.setBitLayerValueAt(FloodWithLava.INSTANCE, x, y, false); } }; } } else { final int height = Math.max(terrainHeight, waterLevel); if (inverse ? (height <= dimension.getMinHeight()) : (height >= (dimension.getMaxHeight() - 1))) { // Already at the lowest or highest possible point return; } final int floodToHeight = inverse ? (height - 1): (height + 1); if (inverse) { if (floodWithLava) { fillMethod = new FloodFillMethod("Lowering lava level", dimensionBounds) { @Override public boolean isBoundary(int x, int y) { final int height = dimension.getIntHeightAt(x, y); return (height == Integer.MIN_VALUE) // Not on a tile || (dimension.getWaterLevelAt(x, y) <= height) // Not flooded || (dimension.getWaterLevelAt(x, y) <= floodToHeight); // Already at the required level or lower } @Override public void fill(int x, int y) { dimension.setWaterLevelAt(x, y, floodToHeight); dimension.setBitLayerValueAt(FloodWithLava.INSTANCE, x, y, true); } }; } else { fillMethod = new FloodFillMethod("Lowering water level", dimensionBounds) { @Override public boolean isBoundary(int x, int y) { final int height = dimension.getIntHeightAt(x, y); return (height == Integer.MIN_VALUE) // Not on a tile || (dimension.getWaterLevelAt(x, y) <= height) // Not flooded || (dimension.getWaterLevelAt(x, y) <= floodToHeight); // Already at the required level or lower } @Override public void fill(int x, int y) { dimension.setWaterLevelAt(x, y, floodToHeight); dimension.setBitLayerValueAt(FloodWithLava.INSTANCE, x, y, false); } }; } } else { if (floodWithLava) { fillMethod = new FloodFillMethod(fluidPresent ? "Raising lava level" : "Flooding with lava", dimensionBounds) { @Override public boolean isBoundary(int x, int y) { final int height = dimension.getIntHeightAt(x, y), waterLevel = dimension.getWaterLevelAt(x, y); return (height == Integer.MIN_VALUE) // Not on a tile || (height >= floodToHeight) // Higher land encountered || (waterLevel >= floodToHeight); // Already at the required level or lower } @Override public void fill(int x, int y) { dimension.setWaterLevelAt(x, y, floodToHeight); dimension.setBitLayerValueAt(FloodWithLava.INSTANCE, x, y, true); } }; } else { fillMethod = new FloodFillMethod(fluidPresent ? "Raising water level" : "Flooding with water", dimensionBounds) { @Override public boolean isBoundary(int x, int y) { final int height = dimension.getIntHeightAt(x, y), waterLevel = dimension.getWaterLevelAt(x, y); return (height == Integer.MIN_VALUE) // Not on a tile || (height >= floodToHeight) // Higher land encountered || (waterLevel >= floodToHeight); // Already at the required level or higher } @Override public void fill(int x, int y) { dimension.setWaterLevelAt(x, y, floodToHeight); dimension.setBitLayerValueAt(FloodWithLava.INSTANCE, x, y, false); } }; } } } synchronized (dimension) { dimension.setEventsInhibited(true); } try { synchronized (dimension) { dimension.rememberChanges(); } final GeneralQueueLinearFloodFiller flooder = new GeneralQueueLinearFloodFiller(fillMethod); try { if (! flooder.floodFill(centreX, centreY, SwingUtilities.getWindowAncestor(getView()))) { // Cancelled by user synchronized (dimension) { if (dimension.undoChanges()) { dimension.clearRedo(); dimension.armSavePoint(); } } return; } if (flooder.isBoundsHit()) { showWarning(getView(), "The area to be flooded was too large and may not have been completely flooded.", "Area Too Large"); } } catch (IndexOutOfBoundsException e) { // This most likely indicates that the area being flooded was too large synchronized (dimension) { if (dimension.undoChanges()) { dimension.clearRedo(); dimension.armSavePoint(); } } JOptionPane.showMessageDialog(getView(), "The area to be flooded is too large or complex; please retry with a smaller area", "Area Too Large", JOptionPane.ERROR_MESSAGE); } } finally { synchronized (dimension) { dimension.setEventsInhibited(false); } } } finally { alreadyFlooding = false; } } @Override public JPanel getOptionsPanel() { return optionsPanel; } private boolean alreadyFlooding; private final boolean floodWithLava; private final StandardOptionsPanel optionsPanel; private static final Logger logger = LoggerFactory.getLogger(Flood.class); static abstract class FloodFillMethod implements GeneralQueueLinearFloodFiller.FillMethod { protected FloodFillMethod(String description, Rectangle bounds) { this.description = description; this.bounds = bounds; } @Override public String getDescription() { return description; } @Override public Rectangle getBounds() { return bounds; } private final String description; private final Rectangle bounds; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy