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.
This commit is contained in:
Evan Debenham 2017-03-01 17:17:40 -05:00
parent ba8598132a
commit 5cbabbf402
10 changed files with 111 additions and 58 deletions

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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 ) {
/*
* 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 ) {
int w = level.width();
int h = level.height();
int length = w * h;
boolean[] cur = new boolean[level.length()];
boolean[] off = new boolean[level.length()];
boolean[] cur = new boolean[length];
boolean[] off = new boolean[length];
for (int i=0; i < level.length(); i++) {
off[i] = Random.Float() < seed;
int fillDiff = -Math.round(length * fill);
if (forceFillRate && clustering > 0) {
fill += (0.5f - fill) * 0.5f;
}
for (int i=0; i < nGen; i++) {
for (int i=0; i < length; i++) {
off[i] = Random.Float() < fill;
if (off[i]) fillDiff++;
}
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++;
}
if (off[pos-w]) {
count++;
}
if (off[pos-w+1]) {
count++;
}
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++;
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] && count >= 5) {
cur[pos] = true;
} else if (off[pos] && count >= 4) {
cur[pos] = true;
} else {
cur[pos] = false;
if (x > 0){
if (off[pos - 1]) count++;
neighbours++;
}
if (off[pos]) count++;
neighbours++;
if (x < (w-1)){
if (off[pos + 1]) count++;
neighbours++;
}
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;
}
}
@ -85,6 +114,30 @@ public class Patch {
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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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