diff --git a/core/src/mindustry/maps/generators/BasicGenerator.java b/core/src/mindustry/maps/generators/BasicGenerator.java index 53c7de51bc..f8093bbe66 100644 --- a/core/src/mindustry/maps/generators/BasicGenerator.java +++ b/core/src/mindustry/maps/generators/BasicGenerator.java @@ -5,7 +5,6 @@ import arc.math.*; import arc.math.geom.*; import arc.struct.*; import arc.util.*; -import arc.util.noise.*; import mindustry.*; import mindustry.content.*; import mindustry.world.*; @@ -18,9 +17,8 @@ public abstract class BasicGenerator implements WorldGenerator{ protected static final DistanceHeuristic manhattan = (x1, y1, x2, y2) -> Math.abs(x1 - x2) + Math.abs(y1 - y2); protected static final ShortArray ints1 = new ShortArray(), ints2 = new ShortArray(); - protected Simplex sim = new Simplex(); - protected Simplex sim2 = new Simplex(); - + protected Rand rand = new Rand(); + protected int width, height; protected Tiles tiles; @@ -34,9 +32,6 @@ public abstract class BasicGenerator implements WorldGenerator{ this.tiles = tiles; this.width = tiles.width; this.height = tiles.height; - int seed = Mathf.random(99999999); - sim.setSeed(seed); - sim2.setSeed(seed + 1); generate(); } @@ -49,7 +44,7 @@ public abstract class BasicGenerator implements WorldGenerator{ public void cliffs2(){ for(Tile tile : tiles){ tile.setBlock(Blocks.air); - tile.cost = tile.floor().isLiquid ? 0 : (byte)(sim.octaveNoise2D(4, 0.5, 1.0 / 90.0, tile.x, tile.y) * 5); + tile.cost = tile.floor().isLiquid ? 0 : (byte)(noise(tile.x, tile.y, 4, 0.5f, 90f, 1) * 5); } for(Tile tile : tiles){ @@ -137,8 +132,8 @@ public abstract class BasicGenerator implements WorldGenerator{ int offsetX = x - 4, offsetY = y + 23; for(int i = ores.size - 1; i >= 0; i--){ Block entry = ores.get(i); - if(Math.abs(0.5f - sim.octaveNoise2D(2, 0.7, 1f / (40 + i * 2), offsetX, offsetY + i*999)) > 0.26f && - Math.abs(0.5f - sim2.octaveNoise2D(1, 1, 1f / (30 + i * 4), offsetX, offsetY - i*999)) > 0.37f){ + if(Math.abs(0.5f - noise(offsetX, offsetY + i*999, 2, 0.7, (40 + i * 2))) > 0.26f && + Math.abs(0.5f - noise(offsetX, offsetY - i*999, 1, 1, (30 + i * 4))) > 0.37f){ ore = entry; break; } @@ -148,7 +143,7 @@ public abstract class BasicGenerator implements WorldGenerator{ public void terrain(Block dst, float scl, float mag, float cmag){ pass((x, y) -> { - double rocks = sim.octaveNoise2D(5, 0.5, 1f / scl, x, y) * mag + double rocks = noise(x, y, 5, 0.5, scl) * mag + Mathf.dst((float)x / width, (float)y / height, 0.5f, 0.5f) * cmag; double edgeDist = Math.min(x, Math.min(y, Math.min(Math.abs(x - (width - 1)), Math.abs(y - (height - 1))))); @@ -164,9 +159,8 @@ public abstract class BasicGenerator implements WorldGenerator{ } public void noise(Block floor, Block block, int octaves, float falloff, float scl, float threshold){ - sim.setSeed(Mathf.random(99999)); pass((x, y) -> { - if(sim.octaveNoise2D(octaves, falloff, 1f / scl, x, y) > threshold){ + if(noise(octaves, falloff, scl, x, y) > threshold){ Tile tile = tiles.getn(x, y); this.floor = floor; if(tile.block().solid){ @@ -177,9 +171,8 @@ public abstract class BasicGenerator implements WorldGenerator{ } public void overlay(Block floor, Block block, float chance, int octaves, float falloff, float scl, float threshold){ - sim.setSeed(Mathf.random(99999)); pass((x, y) -> { - if(sim.octaveNoise2D(octaves, falloff, 1f / scl, x, y) > threshold && Mathf.chance(chance) && tiles.getn(x, y).floor() == floor){ + if(noise(x, y, octaves, falloff, scl) > threshold && Mathf.chance(chance) && tiles.getn(x, y).floor() == floor){ ore = block; } }); @@ -189,11 +182,13 @@ public abstract class BasicGenerator implements WorldGenerator{ Block[] blocks = {Blocks.darkPanel3}; int secSize = 20; pass((x, y) -> { + if(floor.asFloor().isLiquid) return; + int mx = x % secSize, my = y % secSize; int sclx = x / secSize, scly = y / secSize; - if(noise(sclx, scly, 10f, 1f) > 0.63f && (mx == 0 || my == 0 || mx == secSize - 1 || my == secSize - 1)){ + if(noise(sclx, scly, 0.2f, 1f) > 0.63f && noise(sclx, scly + 999, 200f, 1f) > 0.6f && (mx == 0 || my == 0 || mx == secSize - 1 || my == secSize - 1)){ if(Mathf.chance(noise(x + 0x231523, y, 40f, 1f))){ - floor = Structs.random(blocks); + floor = blocks[rand.random(0, blocks.length - 1)]; if(Mathf.dst(mx, my, secSize/2, secSize/2) > secSize/2f + 2){ floor = Blocks.darkPanel4; } @@ -212,7 +207,7 @@ public abstract class BasicGenerator implements WorldGenerator{ tiles.each((x, y) -> { int idx = y*tiles.width + x; - float cx = x + noise(x, y, scl, mag) - mag / 2f, cy = y + noise(x + 155f, y + 155f, scl, mag) - mag / 2f; + float cx = x + noise(x - 155f, y - 200f, scl, mag) - mag / 2f, cy = y + noise(x + 155f, y + 155f, scl, mag) - mag / 2f; Tile other = tiles.getn(Mathf.clamp((int)cx, 0, tiles.width-1), Mathf.clamp((int)cy, 0, tiles.height-1)); blocks[idx] = other.block().id; floors[idx] = other.floor().id; @@ -244,8 +239,51 @@ public abstract class BasicGenerator implements WorldGenerator{ } } - protected float noise(float x, float y, float scl, float mag){ - return (float)sim2.octaveNoise2D(1f, 0f, 1f / scl, x + 0x361266f, y + 0x251259f) * mag; + public void cells(int iterations){ + cells(iterations, 16, 16, 3); + } + + public void cells(int iterations, int birthLimit, int deathLimit, int cradius){ + GridBits write = new GridBits(tiles.width, tiles.height); + GridBits read = new GridBits(tiles.width, tiles.height); + + tiles.each((x, y) -> read.set(x, y, !tiles.get(x, y).block().isAir())); + + for(int i = 0; i < iterations; i++){ + tiles.each((x, y) -> { + int alive = 0; + + for(int cx = -cradius; cx <= cradius; cx++){ + for(int cy = -cradius; cy <= cradius; cy++){ + if((cx == 0 && cy == 0) || !Mathf.within(cx, cy, cradius)) continue; + if(!Structs.inBounds(x + cx, y + cy, tiles.width, tiles.height) || read.get(x + cx, y + cy)){ + alive++; + } + } + } + + if(read.get(x, y)){ + write.set(x, y, alive >= deathLimit); + }else{ + write.set(x, y, alive > birthLimit); + } + }); + + //flush results + read.set(write); + } + + tiles.each((x, y) -> tiles.get(x, y).setBlock(!read.get(x, y) ? Blocks.air : tiles.get(x, y).floor().wall)); + } + + protected float noise(float x, float y, double scl, double mag){ + return noise(x, y, 1, 1, scl, mag); + } + + protected abstract float noise(float x, float y, double octaves, double falloff, double scl, double mag); + + protected float noise(float x, float y, double octaves, double falloff, double scl){ + return noise(x, y, octaves, falloff, scl, 1); } public void pass(Intc2 r){ diff --git a/core/src/mindustry/maps/planet/TestPlanetGenerator.java b/core/src/mindustry/maps/planet/TestPlanetGenerator.java index 94a81cab0a..a8b1634411 100644 --- a/core/src/mindustry/maps/planet/TestPlanetGenerator.java +++ b/core/src/mindustry/maps/planet/TestPlanetGenerator.java @@ -11,10 +11,10 @@ import mindustry.maps.generators.*; import mindustry.type.*; import mindustry.world.*; -import static mindustry.Vars.schematics; +import static mindustry.Vars.*; //TODO refactor into generic planet class -public class TestPlanetGenerator implements PlanetGenerator{ +public class TestPlanetGenerator extends BasicGenerator implements PlanetGenerator{ Simplex noise = new Simplex(); RidgedPerlin rid = new RidgedPerlin(1, 2); float scl = 5f; @@ -38,6 +38,12 @@ public class TestPlanetGenerator implements PlanetGenerator{ {Blocks.darksandWater, Blocks.darksand, Blocks.darksand, Blocks.ice, Blocks.iceSnow, Blocks.iceSnow, Blocks.snow, Blocks.snow, Blocks.ice, Blocks.ice, Blocks.ice, Blocks.ice, Blocks.ice} }; + Array ores = Array.with(Blocks.oreCopper, Blocks.oreLead, Blocks.oreCoal, Blocks.oreCopper); + ObjectMap dec = ObjectMap.of( + Blocks.sporeMoss, Blocks.sporeCluster, + Blocks.moss, Blocks.sporeCluster + ); + float water = 2f / arr[0].length; float rawHeight(Vec3 position){ @@ -79,8 +85,9 @@ public class TestPlanetGenerator implements PlanetGenerator{ public void decorate(Tiles tiles, Sector sec){ this.tiles = tiles; this.sector = sec; + rand.setSeed(sec.id); - new Terrain().generate(tiles); + generate(tiles); } Block getBlock(Vec3 position){ @@ -96,119 +103,99 @@ public class TestPlanetGenerator implements PlanetGenerator{ return arr[Mathf.clamp((int)(temp * arr.length), 0, arr.length - 1)][Mathf.clamp((int)(height * arr[0].length), 0, arr[0].length - 1)]; } - class Terrain extends BasicGenerator{ - Array ores = Array.with(Blocks.oreCopper, Blocks.oreLead, Blocks.oreCoal, Blocks.oreCopper); - - @Override - protected void generate(){ - - class Room{ - int x, y, radius; - ObjectSet connected = new ObjectSet<>(); - - Room(int x, int y, int radius){ - this.x = x; - this.y = y; - this.radius = radius; - connected.add(this); - } - - void connect(Room to){ - if(connected.contains(to)) return; - - connected.add(to); - float nscl = Mathf.random(20f, 60f); - int stroke = Mathf.random(4, 12); - brush(pathfind(x, y, to.x, to.y, tile -> (tile.solid() ? 5f : 0f) + (float)sim.octaveNoise2D(1, 1, 1f / nscl, tile.x, tile.y) * 50, manhattan), stroke); - } - } - - cells(4); - distort(20f, 12f); - - float constraint = 1.3f; - float radius = width / 2f / Mathf.sqrt3; - int rooms = Mathf.random(2, 5); - Array array = new Array<>(); - - //TODO replace random calls with seed - - for(int i = 0; i < rooms; i++){ - Tmp.v1.trns(Mathf.random(360f), Mathf.random(radius / constraint)); - float rx = (width/2f + Tmp.v1.x); - float ry = (height/2f + Tmp.v1.y); - float maxrad = radius - Tmp.v1.len(); - float rrad = Math.min(Mathf.random(9f, maxrad / 2f), 30f); - array.add(new Room((int)rx, (int)ry, (int)rrad)); - } - - for(Room room : array){ - erase(room.x, room.y, room.radius); - } - - int connections = Mathf.random(Math.max(rooms - 1, 1), rooms + 3); - Room spawn = array.random(); - for(int i = 0; i < connections; i++){ - array.random().connect(array.random()); - } - - for(Room room : array){ - spawn.connect(room); - } - - cells(1); - distort(20f, 6f); - - inverseFloodFill(tiles.getn(spawn.x, spawn.y)); - - ores(ores); - - for(Room other : array){ - if(other != spawn){ - // tiles.getn(other.x, other.y).setOverlay(Blocks.spawn); - } - } - - trimDark(); - - median(2); - - schematics.placeLoadout(Loadouts.advancedShard, spawn.x, spawn.y); - } - - void cells(int iterations){ - GridBits write = new GridBits(tiles.width, tiles.height); - GridBits read = new GridBits(tiles.width, tiles.height); - - tiles.each((x, y) -> read.set(x, y, !tiles.get(x, y).block().isAir())); - - int birthLimit = 16, deathLimit = 16, cradius = 3; - - for(int i = 0; i < iterations; i++){ - tiles.each((x, y) -> { - int alive = 0; - - for(int cx = -cradius; cx <= cradius; cx++){ - for(int cy = -cradius; cy <= cradius; cy++){ - if((cx == 0 && cy == 0) || !Mathf.within(cx, cy, cradius)) continue; - if(!Structs.inBounds(x + cx, y + cy, tiles.width, tiles.height) || read.get(x + cx, y + cy)){ - alive++; - } - } - } - - if(read.get(x, y)){ - write.set(x, y, alive >= deathLimit); - }else{ - write.set(x, y, alive > birthLimit); - } - }); - - //flush results - read.set(write); - } - - tiles.each((x, y) -> tiles.get(x, y).setBlock(!read.get(x, y) ? Blocks.air : tiles.get(x, y).floor().wall)); - } + @Override + protected float noise(float x, float y, double octaves, double falloff, double scl, double mag){ + Vec3 v = sector.rect.project(x, y).scl(5f); + return (float)noise.octaveNoise3D(octaves, falloff, 1f / scl, v.x, v.y, v.z) * (float)mag; } + + @Override + protected void generate(){ + + class Room{ + int x, y, radius; + ObjectSet connected = new ObjectSet<>(); + + Room(int x, int y, int radius){ + this.x = x; + this.y = y; + this.radius = radius; + connected.add(this); + } + + void connect(Room to){ + if(connected.contains(to)) return; + + connected.add(to); + float nscl = rand.random(20f, 60f); + int stroke = rand.random(4, 12); + brush(pathfind(x, y, to.x, to.y, tile -> (tile.solid() ? 5f : 0f) + noise(tile.x, tile.y, 1, 1, 1f / nscl) * 50, manhattan), stroke); + } + } + + cells(4); + distort(10f, 12f); + + float constraint = 1.3f; + float radius = width / 2f / Mathf.sqrt3; + int rooms = rand.random(2, 5); + Array array = new Array<>(); + + for(int i = 0; i < rooms; i++){ + Tmp.v1.trns(rand.random(360f), rand.random(radius / constraint)); + float rx = (width/2f + Tmp.v1.x); + float ry = (height/2f + Tmp.v1.y); + float maxrad = radius - Tmp.v1.len(); + float rrad = Math.min(rand.random(9f, maxrad / 2f), 30f); + array.add(new Room((int)rx, (int)ry, (int)rrad)); + } + + for(Room room : array){ + erase(room.x, room.y, room.radius); + } + + int connections = rand.random(Math.max(rooms - 1, 1), rooms + 3); + Room spawn = array.random(); + for(int i = 0; i < connections; i++){ + array.random().connect(array.random()); + } + + for(Room room : array){ + spawn.connect(room); + } + + cells(1); + distort(10f, 6f); + + inverseFloodFill(tiles.getn(spawn.x, spawn.y)); + + ores(ores); + + for(Room other : array){ + if(other != spawn){ + // tiles.getn(other.x, other.y).setOverlay(Blocks.spawn); + } + } + + trimDark(); + + median(2); + + tech(); + + pass((x, y) -> { + for(int i = 0; i < 4; i++){ + Tile near = world.tile(x + Geometry.d4[i].x, y + Geometry.d4[i].y); + if(near != null && near.block() != Blocks.air){ + return; + } + } + if(Mathf.chance(0.01) && !floor.asFloor().isLiquid && block == Blocks.air){ + block = dec.get(floor, floor.asFloor().decoration); + } + }); + + schematics.placeLoadout(Loadouts.advancedShard, spawn.x, spawn.y); + } + } diff --git a/core/src/mindustry/world/blocks/Floor.java b/core/src/mindustry/world/blocks/Floor.java index b4eca21547..db6bf7dac8 100644 --- a/core/src/mindustry/world/blocks/Floor.java +++ b/core/src/mindustry/world/blocks/Floor.java @@ -59,6 +59,8 @@ public class Floor extends Block{ public float oreScale = 24f, oreThreshold = 0.828f; /** Wall variant of this block. May be Blocks.air if not found. */ public Block wall = Blocks.air; + /** Decoration block. Usually a rock. May be air. */ + public Block decoration = Blocks.air; protected TextureRegion[][] edges; protected byte eq = 0; @@ -106,6 +108,10 @@ public class Floor extends Block{ //keep default value if not found... if(wall == null) wall = Blocks.air; + + if(decoration == Blocks.air){ + decoration = content.blocks().min(b -> b instanceof Rock && b.breakable ? color.diff(b.color) : Float.POSITIVE_INFINITY); + } } @Override