package sc.tools;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import sc.math.SimplexNoise;
import sc.server.Server;
import sc.server.world.Tile;
public final class MapTool extends JFrame implements ActionListener
{
/** The version of the serialised form of this class. */
private static final long serialVersionUID = 1L;
/** Constants */
private static final int WIDTH = 1000;
private static final int DEPTH = 1000;
private static final int WATERLEVEL = 10;
private static final int WATERBORDER = 100;
private static final int LANDRISE = 5;
/** Display elements */
private static JLabel canvasLabel;
private static BufferedImage map;
private static int[] pixels;
private static JLabel treeCountValue;
/** Map statistics */
private static int tree_count;
/**
* Creates an instance of MapMaker.
*
* @param width
* @param height
* @throws SQLException
* @throws ClassNotFoundException
*/
public MapTool(int width, int height) throws ClassNotFoundException,
SQLException
{
setTitle("Map Maker");
setSize(width, height);
layoutWindow();
setVisible(true);
}
/**
* Lays out the window controls.
*/
private void layoutWindow()
{
JButton btn;
// Add the controls panel to the left hand side
JPanel panel = new JPanel(new BorderLayout());
getContentPane().add(panel, BorderLayout.WEST);
// Add a reload map button to the top of the panel
btn = new JButton("Reload");
btn.addActionListener(this);
panel.add(btn, BorderLayout.NORTH);
// Add a save button to the top of the panel
btn = new JButton("Save");
btn.addActionListener(this);
panel.add(btn, BorderLayout.SOUTH);
// Add a info panel
JPanel infopanel = new JPanel(new GridLayout(7, 1));
panel.add(infopanel, BorderLayout.CENTER);
// Add number of trees labels
JLabel treeCountLabel = new JLabel("Trees:");
treeCountValue = new JLabel("0");
JPanel treeCountPanel = new JPanel();
treeCountPanel.add(treeCountLabel);
treeCountPanel.add(treeCountValue);
infopanel.add(treeCountPanel);
map = new BufferedImage(WIDTH, DEPTH, BufferedImage.TYPE_INT_RGB);
canvasLabel = new JLabel(new ImageIcon(map));
canvasLabel.setHorizontalAlignment(JLabel.CENTER);
JScrollPane sp = new JScrollPane(canvasLabel);
getContentPane().add(sp, BorderLayout.CENTER);
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
// Get a direct direct DataBuffer onto the bufferedimage
WritableRaster raster = map.getRaster();
pixels = ((DataBufferInt) raster.getDataBuffer()).getData();
}
/**
* (Re)generates the world map.
*/
public void regenerate()
{
double combined;
int height;
// Set random seeds
int octave1_seed = (int) (Math.random() * 5000);
int octave2_seed = (int) (Math.random() * 5000);
int trees_seed = (int) (Math.random() * 5000);
// Zero statistics
tree_count = 0;
for (int y = 0; y < DEPTH; y++)
{
for (int x = 0; x < WIDTH; x++)
{
/** There are multiple layers of noise that make up the terrain */
// Layer 0: Defines a tapered edge so water frames the map
double taper = 0;
if (y < WATERBORDER)
{
taper = ((double) (WATERBORDER - y) / WATERBORDER);
} else if (y > DEPTH - WATERBORDER)
{
taper = ((double) (y - DEPTH + WATERBORDER) / WATERBORDER);
}
if (x < WATERBORDER)
{
taper += ((double) (WATERBORDER - x) / WATERBORDER);
} else if (x > WIDTH - WATERBORDER)
{
taper += ((double) (x - WIDTH + WATERBORDER) / WATERBORDER);
}
double octave0 = ((LANDRISE + WATERLEVEL - 128) / 128f)
- (taper * taper);
// Layer 1: Defines the large scale water and land forms
double octave1 = SimplexNoise.noise(x * 0.005, y * 0.005,
octave1_seed);
// Layer 2: Defines smaller scale deformations
double octave2 = SimplexNoise.noise(x * 0.02, y * 0.02,
octave2_seed) * 0.2;
// Combine the layers together
combined = octave0 + octave1 + octave2;
// Bound noise to the range [-1, 1]
if (combined > 1)
combined -= (combined - 1);
else if (combined < -1)
combined = -1;
// Convert combined noise to height
// [-1, 1] => [0, 256]
height = (int) Math.round(combined * 128) + 128;
// Set the green colour component of the pixel to represent
// height
int rgb = height << 8;
if (height < WATERLEVEL)
{
// DEBUG: For testing set pixel to blue if below WATERLEVEL
rgb += 75;
} else
{
// We are above WATERLEVEL so apply trees
if (Math.random() > SimplexNoise.noise(x * 0.005,
y * 0.005, trees_seed) + 0.8)
{
rgb += 128;
tree_count++;
}
}
pixels[y * WIDTH + x] = rgb;
}
}
// Update map info
canvasLabel.repaint();
treeCountValue.setText(String.valueOf(tree_count));
}
/**
* Saves the current world map to disk.
*/
public void save()
{
int x = 0;
int z = 0;
// Make a Tile out of each pixel in the image
for (int i = 0; i < pixels.length; i++)
{
int height = (pixels[i] & 0x0000FF00) >> 8;
new Tile((x - Tile.HALFMAPSIZE) * Tile.WIDTH, height,
(z - Tile.HALFMAPSIZE) * Tile.WIDTH);
x++;
if (x == WIDTH)
{
x = 0;
z++;
}
}
}
@Override
public void actionPerformed(ActionEvent e)
{
if (e.getActionCommand() == "Reload")
{
regenerate();
} else if (e.getActionCommand() == "Save")
{
save();
}
}
/**
* Program entry point.
*
* @param args
*/
public static void main(String[] args)
{
try
{
MapTool mm = new MapTool(1024, 738);
mm.regenerate();
}
catch (Exception e)
{
Server.logger.severe(e.getMessage());
}
}
}