|
|
@@ -0,0 +1,183 @@
|
|
|
+package util;
|
|
|
+
|
|
|
+import java.io.*;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+import java.io.*;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+public class WorldGenerator {
|
|
|
+ private static int worldGenNumber = 0;
|
|
|
+
|
|
|
+ public static String generateNewWorld(int worldWidth, int worldHeight, int seed) {
|
|
|
+ Random rand = new Random(seed);
|
|
|
+ int[][] world = new int[worldHeight][worldWidth];
|
|
|
+ initializeWorld(world);
|
|
|
+ createMainPath(world, worldWidth, worldHeight, rand);
|
|
|
+ addSecondaryPaths(world, worldWidth, worldHeight, rand);
|
|
|
+ addWater(world, worldWidth, worldHeight, rand);
|
|
|
+ addTrees(world, worldWidth, worldHeight, rand);
|
|
|
+ addEarthPatches(world, worldWidth, worldHeight, rand);
|
|
|
+ return writeWorldToFile(world, worldWidth, worldHeight);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void initializeWorld(int[][] world) {
|
|
|
+ for (int[] row : world) {
|
|
|
+ Arrays.fill(row, 0); // Fill with grass
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void createMainPath(int[][] world, int width, int height, Random rand) {
|
|
|
+ int startX = width / 2;
|
|
|
+ int startY = height / 2;
|
|
|
+ // Create a winding main path with multiple segments
|
|
|
+ drunkardsWalk(world, startX, startY, 300, width, height, rand, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void addSecondaryPaths(int[][] world, int width, int height, Random rand) {
|
|
|
+ int numBranches = 10;
|
|
|
+ for (int i = 0; i < numBranches; i++) {
|
|
|
+ // Find a random point on the existing path
|
|
|
+ int startX, startY;
|
|
|
+ do {
|
|
|
+ startX = rand.nextInt(width);
|
|
|
+ startY = rand.nextInt(height);
|
|
|
+ } while (world[startY][startX] != 5);
|
|
|
+
|
|
|
+ drunkardsWalk(world, startX, startY, 50 + rand.nextInt(50), width, height, rand, false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void drunkardsWalk(int[][] world, int startX, int startY, int steps,
|
|
|
+ int width, int height, Random rand, boolean isMain) {
|
|
|
+ int x = startX;
|
|
|
+ int y = startY;
|
|
|
+ for (int i = 0; i < steps; i++) {
|
|
|
+ int dir = rand.nextInt(4);
|
|
|
+ switch (dir) {
|
|
|
+ case 0 -> x = Math.min(x + 1, width - 1);
|
|
|
+ case 1 -> x = Math.max(x - 1, 0);
|
|
|
+ case 2 -> y = Math.min(y + 1, height - 1);
|
|
|
+ case 3 -> y = Math.max(y - 1, 0);
|
|
|
+ }
|
|
|
+ world[y][x] = 5; // Set to path
|
|
|
+
|
|
|
+ // Randomly widen path
|
|
|
+ if (rand.nextDouble() < (isMain ? 0.4 : 0.3)) {
|
|
|
+ int widen = rand.nextInt(2) + 1;
|
|
|
+ for (int dx = -widen; dx <= widen; dx++) {
|
|
|
+ for (int dy = -widen; dy <= widen; dy++) {
|
|
|
+ if (x + dx >= 0 && x + dx < width && y + dy >= 0 && y + dy < height) {
|
|
|
+ if (world[y + dy][x + dx] == 0) {
|
|
|
+ world[y + dy][x + dx] = 5;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void addWater(int[][] world, int width, int height, Random rand) {
|
|
|
+ for (int i = 0; i < 5; i++) {
|
|
|
+ int x = rand.nextInt(width);
|
|
|
+ int y = rand.nextInt(height);
|
|
|
+ if (world[y][x] == 0) {
|
|
|
+ floodFill(world, x, y, 2, 50 + rand.nextInt(50), width, height);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void floodFill(int[][] world, int x, int y, int tile, int maxSize, int width, int height) {
|
|
|
+ Queue<Point> queue = new LinkedList<>();
|
|
|
+ Set<Point> visited = new HashSet<>();
|
|
|
+ queue.add(new Point(x, y));
|
|
|
+ int filled = 0;
|
|
|
+
|
|
|
+ while (!queue.isEmpty() && filled < maxSize) {
|
|
|
+ Point p = queue.poll();
|
|
|
+ if (visited.contains(p)) continue;
|
|
|
+ visited.add(p);
|
|
|
+
|
|
|
+ if (world[p.y][p.x] == 0) {
|
|
|
+ world[p.y][p.x] = tile;
|
|
|
+ filled++;
|
|
|
+ // Add neighbors
|
|
|
+ if (p.x > 0) queue.add(new Point(p.x - 1, p.y));
|
|
|
+ if (p.x < width - 1) queue.add(new Point(p.x + 1, p.y));
|
|
|
+ if (p.y > 0) queue.add(new Point(p.x, p.y - 1));
|
|
|
+ if (p.y < height - 1) queue.add(new Point(p.x, p.y + 1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void addTrees(int[][] world, int width, int height, Random rand) {
|
|
|
+ for (int i = 0; i < 10; i++) {
|
|
|
+ int x = rand.nextInt(width);
|
|
|
+ int y = rand.nextInt(height);
|
|
|
+ if (world[y][x] == 0) {
|
|
|
+ generateTreeCluster(world, x, y, 4 + rand.nextInt(4), width, height, rand);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void generateTreeCluster(int[][] world, int x, int y, int size,
|
|
|
+ int width, int height, Random rand) {
|
|
|
+ for (int dx = -size; dx <= size; dx++) {
|
|
|
+ for (int dy = -size; dy <= size; dy++) {
|
|
|
+ if (x + dx >= 0 && x + dx < width && y + dy >= 0 && y + dy < height) {
|
|
|
+ double dist = Math.sqrt(dx*dx + dy*dy);
|
|
|
+ if (dist <= size && rand.nextDouble() < 0.7 - (dist / size) * 0.4) {
|
|
|
+ if (world[y + dy][x + dx] == 0) {
|
|
|
+ world[y + dy][x + dx] = 4;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void addEarthPatches(int[][] world, int width, int height, Random rand) {
|
|
|
+ for (int i = 0; i < width * height / 20; i++) {
|
|
|
+ int x = rand.nextInt(width);
|
|
|
+ int y = rand.nextInt(height);
|
|
|
+ if (world[y][x] == 0) {
|
|
|
+ world[y][x] = 3; // Earth
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String writeWorldToFile(int[][] world, int width, int height) {
|
|
|
+ String fileName = "gamefiles/worlds/world_" + worldGenNumber++ + ".txt";
|
|
|
+ try (PrintWriter writer = new PrintWriter(new File(fileName))) {
|
|
|
+ for (int[] row : world) {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ for (int i = 0; i < row.length; i++) {
|
|
|
+ sb.append(i > 0 ? " " : "").append(row[i]);
|
|
|
+ }
|
|
|
+ writer.println(sb);
|
|
|
+ }
|
|
|
+ } catch (IOException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return fileName;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class Point {
|
|
|
+ int x, y;
|
|
|
+ Point(int x, int y) { this.x = x; this.y = y; }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean equals(Object o) {
|
|
|
+ if (this == o) return true;
|
|
|
+ if (o == null || getClass() != o.getClass()) return false;
|
|
|
+ Point point = (Point) o;
|
|
|
+ return x == point.x && y == point.y;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int hashCode() {
|
|
|
+ return Objects.hash(x, y);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|