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:
parent
ba8598132a
commit
5cbabbf402
|
@ -144,7 +144,7 @@ public class CavesBossLevel extends Level {
|
||||||
Random.Int( ROOM_TOP + 1, ROOM_BOTTOM - 1 ) * width();
|
Random.Int( ROOM_TOP + 1, ROOM_BOTTOM - 1 ) * width();
|
||||||
map[entrance] = Terrain.ENTRANCE;
|
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++) {
|
for (int i=0; i < length(); i++) {
|
||||||
if (map[i] == Terrain.EMPTY && patch[i]) {
|
if (map[i] == Terrain.EMPTY && patch[i]) {
|
||||||
map[i] = Terrain.WATER;
|
map[i] = Terrain.WATER;
|
||||||
|
|
|
@ -75,11 +75,11 @@ public class CavesLevel extends RegularLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean[] water() {
|
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() {
|
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
|
@Override
|
||||||
|
|
|
@ -70,11 +70,11 @@ public class CityLevel extends RegularLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean[] water() {
|
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() {
|
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
|
@Override
|
||||||
|
|
|
@ -123,7 +123,7 @@ public class HallsBossLevel extends Level {
|
||||||
Random.Int( ROOM_TOP + 1, ROOM_BOTTOM - 1 ) * width();
|
Random.Int( ROOM_TOP + 1, ROOM_BOTTOM - 1 ) * width();
|
||||||
map[entrance] = Terrain.ENTRANCE;
|
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++) {
|
for (int i=0; i < length(); i++) {
|
||||||
if (map[i] == Terrain.EMPTY && patch[i]) {
|
if (map[i] == Terrain.EMPTY && patch[i]) {
|
||||||
map[i] = Terrain.WATER;
|
map[i] = Terrain.WATER;
|
||||||
|
|
|
@ -83,11 +83,11 @@ public class HallsLevel extends RegularLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean[] water() {
|
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() {
|
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
|
@Override
|
||||||
|
|
|
@ -217,12 +217,12 @@ public class LastShopLevel extends RegularLevel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean[] water() {
|
protected boolean[] water() {
|
||||||
return Patch.generate( this, 0.35f, 4 );
|
return Patch.generate( width, height, 0.10f, 4, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean[] grass() {
|
protected boolean[] grass() {
|
||||||
return Patch.generate( this, 0.30f, 3 );
|
return Patch.generate( width, height, 0.10f, 3, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -21,62 +21,91 @@
|
||||||
|
|
||||||
package com.shatteredpixel.shatteredpixeldungeon.levels;
|
package com.shatteredpixel.shatteredpixeldungeon.levels;
|
||||||
|
|
||||||
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
|
|
||||||
import com.watabou.utils.Random;
|
import com.watabou.utils.Random;
|
||||||
|
|
||||||
public class Patch {
|
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 length = w * h;
|
||||||
int h = level.height();
|
|
||||||
|
|
||||||
boolean[] cur = new boolean[level.length()];
|
boolean[] cur = new boolean[length];
|
||||||
boolean[] off = new boolean[level.length()];
|
boolean[] off = new boolean[length];
|
||||||
|
|
||||||
for (int i=0; i < level.length(); i++) {
|
int fillDiff = -Math.round(length * fill);
|
||||||
off[i] = Random.Float() < seed;
|
|
||||||
|
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 i=0; i < clustering; i++) {
|
||||||
for (int x=1; x < w-1; x++) {
|
|
||||||
|
for (int y=0; y < h; y++) {
|
||||||
|
for (int x=0; x < w; x++) {
|
||||||
|
|
||||||
int pos = x + y * w;
|
int pos = x + y * w;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
if (off[pos-w-1]) {
|
int neighbours = 0;
|
||||||
count++;
|
|
||||||
}
|
if (y > 0) {
|
||||||
if (off[pos-w]) {
|
if (x > 0){
|
||||||
count++;
|
if (off[pos - w - 1]) count++;
|
||||||
}
|
neighbours++;
|
||||||
if (off[pos-w+1]) {
|
}
|
||||||
count++;
|
if (off[pos - w]) count++;
|
||||||
}
|
neighbours++;
|
||||||
if (off[pos-1]) {
|
if (x < (w - 1)){
|
||||||
count++;
|
if (off[pos - w + 1]) count++;
|
||||||
}
|
neighbours++;
|
||||||
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) {
|
if (x > 0){
|
||||||
cur[pos] = true;
|
if (off[pos - 1]) count++;
|
||||||
} else if (off[pos] && count >= 4) {
|
neighbours++;
|
||||||
cur[pos] = true;
|
|
||||||
} else {
|
|
||||||
cur[pos] = false;
|
|
||||||
}
|
}
|
||||||
|
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;
|
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;
|
return off;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,11 +67,11 @@ public class PrisonLevel extends RegularLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean[] water() {
|
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() {
|
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
|
@Override
|
||||||
|
|
|
@ -182,11 +182,11 @@ public class SewerBossLevel extends RegularLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean[] water() {
|
protected boolean[] water() {
|
||||||
return Patch.generate( this, 0.5f, 5 );
|
return Patch.generate( width, height, 0.5f, 5, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean[] grass() {
|
protected boolean[] grass() {
|
||||||
return Patch.generate( this, 0.40f, 4 );
|
return Patch.generate( width, height, 0.20f, 4, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -63,11 +63,11 @@ public class SewerLevel extends RegularLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean[] water() {
|
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() {
|
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
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue
Block a user