From 5cbabbf402a61a9be460d16f006e64c0e119aefc Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Wed, 1 Mar 2017 17:17:40 -0500 Subject: [PATCH] v0.6.0: refactored and improved the patch class Note that the new numbers should be roughly equivalent from a balance perspective, there should be very little change in the rates of grass and water. --- .../levels/CavesBossLevel.java | 2 +- .../levels/CavesLevel.java | 4 +- .../levels/CityLevel.java | 4 +- .../levels/HallsBossLevel.java | 2 +- .../levels/HallsLevel.java | 4 +- .../levels/LastShopLevel.java | 4 +- .../shatteredpixeldungeon/levels/Patch.java | 137 ++++++++++++------ .../levels/PrisonLevel.java | 4 +- .../levels/SewerBossLevel.java | 4 +- .../levels/SewerLevel.java | 4 +- 10 files changed, 111 insertions(+), 58 deletions(-) diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CavesBossLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CavesBossLevel.java index 9d8e1f209..d422f68b2 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CavesBossLevel.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CavesBossLevel.java @@ -144,7 +144,7 @@ public class CavesBossLevel extends Level { Random.Int( ROOM_TOP + 1, ROOM_BOTTOM - 1 ) * width(); map[entrance] = Terrain.ENTRANCE; - boolean[] patch = Patch.generate( this, 0.45f, 6 ); + boolean[] patch = Patch.generate( width, height, 0.30f, 6, true ); for (int i=0; i < length(); i++) { if (map[i] == Terrain.EMPTY && patch[i]) { map[i] = Terrain.WATER; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CavesLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CavesLevel.java index bdfa7599a..ae328e2ac 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CavesLevel.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CavesLevel.java @@ -75,11 +75,11 @@ public class CavesLevel extends RegularLevel { } protected boolean[] water() { - return Patch.generate( this, feeling == Feeling.WATER ? 0.60f : 0.45f, 6 ); + return Patch.generate( width, height, feeling == Feeling.WATER ? 0.85f : 0.30f, 6, true ); } protected boolean[] grass() { - return Patch.generate( this, feeling == Feeling.GRASS ? 0.55f : 0.35f, 3 ); + return Patch.generate( width, height, feeling == Feeling.GRASS ? 0.65f : 0.15f, 3, true ); } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CityLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CityLevel.java index 6a20cd635..949d1c645 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CityLevel.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CityLevel.java @@ -70,11 +70,11 @@ public class CityLevel extends RegularLevel { } protected boolean[] water() { - return Patch.generate( this, feeling == Feeling.WATER ? 0.65f : 0.45f, 4 ); + return Patch.generate( width, height, feeling == Feeling.WATER ? 0.90f : 0.30f, 4, true ); } protected boolean[] grass() { - return Patch.generate( this, feeling == Feeling.GRASS ? 0.60f : 0.40f, 3 ); + return Patch.generate( width, height, feeling == Feeling.GRASS ? 0.80f : 0.20f, 3, true ); } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/HallsBossLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/HallsBossLevel.java index d438d8844..fd9818532 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/HallsBossLevel.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/HallsBossLevel.java @@ -123,7 +123,7 @@ public class HallsBossLevel extends Level { Random.Int( ROOM_TOP + 1, ROOM_BOTTOM - 1 ) * width(); map[entrance] = Terrain.ENTRANCE; - boolean[] patch = Patch.generate( this, 0.45f, 6 ); + boolean[] patch = Patch.generate( width, height, 0.30f, 6, true ); for (int i=0; i < length(); i++) { if (map[i] == Terrain.EMPTY && patch[i]) { map[i] = Terrain.WATER; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/HallsLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/HallsLevel.java index b22762ec4..97d3d3eac 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/HallsLevel.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/HallsLevel.java @@ -83,11 +83,11 @@ public class HallsLevel extends RegularLevel { } protected boolean[] water() { - return Patch.generate( this, feeling == Feeling.WATER ? 0.55f : 0.40f, 6 ); + return Patch.generate( width, height, feeling == Feeling.WATER ? 0.70f : 0.15f, 6, true ); } protected boolean[] grass() { - return Patch.generate( this, feeling == Feeling.GRASS ? 0.55f : 0.30f, 3 ); + return Patch.generate( width, height, feeling == Feeling.GRASS ? 0.65f : 0.10f, 3, true ); } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/LastShopLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/LastShopLevel.java index 9fbade9b9..6c6048ec9 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/LastShopLevel.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/LastShopLevel.java @@ -217,12 +217,12 @@ public class LastShopLevel extends RegularLevel { @Override protected boolean[] water() { - return Patch.generate( this, 0.35f, 4 ); + return Patch.generate( width, height, 0.10f, 4, true ); } @Override protected boolean[] grass() { - return Patch.generate( this, 0.30f, 3 ); + return Patch.generate( width, height, 0.10f, 3, true ); } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Patch.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Patch.java index f1c94e90d..bb730802d 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Patch.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Patch.java @@ -21,62 +21,91 @@ package com.shatteredpixel.shatteredpixeldungeon.levels; -import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.watabou.utils.Random; public class Patch { - - public static boolean[] generate( Level level, float seed, int nGen ) { - - int w = level.width(); - int h = level.height(); - boolean[] cur = new boolean[level.length()]; - boolean[] off = new boolean[level.length()]; + /* + * fill is the initial seeded fill rate when creating a random boolean array. + * + * clustering is the number of clustering passes done on then array, to create patches. + * each clustering pass is basically a 3x3 mask filter but with rounding to true or false + * high clustering values will produce more concentrated patches, + * but any amount of clustering will rapidly push fill rates towards 1.0f or 0.0f + * The closer the fill rate is to 0.5f the weaker this pushing will be. + * + * forceFillRate adjusts the algorithm to force fill rate to be consistent despite clustering. + * this is achived by firstly pulling the initial fill value towards 0.5f + * and then by manually filling in or emptying cells after clustering, until the fill rate is + * achieved. This is tracked with the fillDiff variable. + */ + public static boolean[] generate( int w, int h, float fill, int clustering, boolean forceFillRate ) { - for (int i=0; i < level.length(); i++) { - off[i] = Random.Float() < seed; + int length = w * h; + + boolean[] cur = new boolean[length]; + boolean[] off = new boolean[length]; + + int fillDiff = -Math.round(length * fill); + + if (forceFillRate && clustering > 0) { + fill += (0.5f - fill) * 0.5f; + } + + for (int i=0; i < length; i++) { + off[i] = Random.Float() < fill; + if (off[i]) fillDiff++; } - for (int i=0; i < nGen; i++) { - - for (int y=1; y < h-1; y++) { - for (int x=1; x < w-1; x++) { + for (int i=0; i < clustering; i++) { + + for (int y=0; y < h; y++) { + for (int x=0; x < w; x++) { int pos = x + y * w; int count = 0; - if (off[pos-w-1]) { - count++; + int neighbours = 0; + + if (y > 0) { + if (x > 0){ + if (off[pos - w - 1]) count++; + neighbours++; + } + if (off[pos - w]) count++; + neighbours++; + if (x < (w - 1)){ + if (off[pos - w + 1]) count++; + neighbours++; + } } - if (off[pos-w]) { - count++; + + if (x > 0){ + if (off[pos - 1]) count++; + neighbours++; } - if (off[pos-w+1]) { - count++; + if (off[pos]) count++; + neighbours++; + if (x < (w-1)){ + if (off[pos + 1]) count++; + neighbours++; } - if (off[pos-1]) { - count++; - } - if (off[pos+1]) { - count++; - } - if (off[pos+w-1]) { - count++; - } - if (off[pos+w]) { - count++; - } - if (off[pos+w+1]) { - count++; - } - - if (!off[pos] && count >= 5) { - cur[pos] = true; - } else if (off[pos] && count >= 4) { - cur[pos] = true; - } else { - cur[pos] = false; + + if (y < (h-1)) { + if (x > 0){ + if (off[pos + w - 1]) count++; + neighbours++; + } + if (off[pos + w]) count++; + neighbours++; + if (x < (w-1)){ + if (off[pos + w + 1]) count++; + neighbours++; + } } + + cur[pos] = 2*count >= neighbours; + if (cur[pos] != off[pos]) fillDiff += cur[pos] ? +1 : -1; + } } @@ -84,6 +113,30 @@ public class Patch { cur = off; off = tmp; } + + if (forceFillRate) { + int[] neighbours = new int[]{-w - 1, -w, -w + 1, -1, 0, +1, +w - 1, +w, +w + 1}; + boolean growing = fillDiff < 0; + + while (fillDiff != 0) { + int cell; + int tries = 0; + + //random cell, not in the map's borders + // try length/10 times to find a cell we can grow from, and not start a new patch/hole + do { + cell = Random.Int(1, w - 1) + Random.Int(1, h - 1) * w; + tries++; + } while (off[cell] != growing && tries * 10 < length); + + for (int i : neighbours) { + if (fillDiff != 0 && off[cell + i] != growing) { + off[cell + i] = growing; + fillDiff += growing ? +1 : -1; + } + } + } + } return off; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/PrisonLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/PrisonLevel.java index c6eae66f3..a482a343b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/PrisonLevel.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/PrisonLevel.java @@ -67,11 +67,11 @@ public class PrisonLevel extends RegularLevel { } protected boolean[] water() { - return Patch.generate( this, feeling == Feeling.WATER ? 0.65f : 0.45f, 4 ); + return Patch.generate( width, height, feeling == Feeling.WATER ? 0.90f : 0.30f, 4, true ); } protected boolean[] grass() { - return Patch.generate( this, feeling == Feeling.GRASS ? 0.60f : 0.40f, 3 ); + return Patch.generate( width, height, feeling == Feeling.GRASS ? 0.80f : 0.20f, 3, true ); } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/SewerBossLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/SewerBossLevel.java index c97d559d1..bd665a351 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/SewerBossLevel.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/SewerBossLevel.java @@ -182,11 +182,11 @@ public class SewerBossLevel extends RegularLevel { } protected boolean[] water() { - return Patch.generate( this, 0.5f, 5 ); + return Patch.generate( width, height, 0.5f, 5, true ); } protected boolean[] grass() { - return Patch.generate( this, 0.40f, 4 ); + return Patch.generate( width, height, 0.20f, 4, true ); } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/SewerLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/SewerLevel.java index 98063ff21..a07b85798 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/SewerLevel.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/SewerLevel.java @@ -63,11 +63,11 @@ public class SewerLevel extends RegularLevel { } protected boolean[] water() { - return Patch.generate( this, feeling == Feeling.WATER ? 0.60f : 0.45f, 5 ); + return Patch.generate( width, height, feeling == Feeling.WATER ? 0.85f : 0.30f, 5, true ); } protected boolean[] grass() { - return Patch.generate( this, feeling == Feeling.GRASS ? 0.60f : 0.40f, 4 ); + return Patch.generate( width, height, feeling == Feeling.GRASS ? 0.80f : 0.20f, 4, true ); } @Override