1336 lines
37 KiB
Java
1336 lines
37 KiB
Java
package dk.itu.mario.level;
|
|
|
|
import java.io.File;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Random;
|
|
|
|
import dk.itu.mario.MarioInterface.GamePlay;
|
|
import dk.itu.mario.MarioInterface.LevelInterface;
|
|
import dk.itu.mario.engine.DataRecorder;
|
|
import dk.itu.mario.engine.sprites.SpriteTemplate;
|
|
import dk.itu.mario.level.grammar.LevelGrammar;
|
|
import dk.itu.mario.level.grammar.LevelGrammarFactory;
|
|
import dk.itu.mario.level.grammar.LevelParseTree;
|
|
import dk.itu.mario.level.matcher.ArchetypeMatcher;
|
|
import dk.itu.mario.level.matcher.ProfileMatcher;
|
|
|
|
public class PCGLevel extends Level {
|
|
public static long lastSeed;
|
|
// disable TESTING - enable grammar-based level generation
|
|
public static boolean TESTING = false;
|
|
|
|
public int BLOCKS_COINS = 0; // the number of coin blocks
|
|
public int BLOCKS_EMPTY = 0; // the number of empty blocks
|
|
public int BLOCKS_POWER = 0; // the number of power blocks
|
|
public int COINS = 0; // These are the coins in boxes that Mario collect
|
|
|
|
// Store information about the level
|
|
public int ENEMIES = 0; // the number of enemies the level contains
|
|
private DataRecorder dataRecorder;
|
|
private HashMap<PlayerProfile.ChallengeRewardType, Double> probability = new HashMap<PlayerProfile.ChallengeRewardType, Double>();
|
|
private Random random;
|
|
private int type;
|
|
|
|
public PCGLevel(int width, int height) {
|
|
super(width, height);
|
|
}
|
|
|
|
public PCGLevel(int width, int height, long seed, int difficulty, int type,
|
|
GamePlay playerMetrics) {
|
|
this(width, height);
|
|
|
|
System.out
|
|
.println("Generating level based on previous GamePlay metrics ONLY.");
|
|
this.dataRecorder = DataRecorder.BLANK_RECORD;
|
|
|
|
generateLevel(seed, playerMetrics);
|
|
}
|
|
|
|
public PCGLevel(int width, int height, long seed, int difficulty, int type,
|
|
GamePlay playerMetrics, DataRecorder dataRecorder) {
|
|
this(width, height);
|
|
|
|
System.out
|
|
.println("Generating level based on previous GamePlay AND DataRecorder metrics.");
|
|
this.dataRecorder = dataRecorder;
|
|
|
|
generateLevel(seed, playerMetrics);
|
|
}
|
|
|
|
@Override
|
|
public RandomLevel clone() throws CloneNotSupportedException {
|
|
|
|
RandomLevel clone = new RandomLevel(width, height);
|
|
|
|
clone.xExit = xExit;
|
|
clone.yExit = yExit;
|
|
byte[][] map = getMap();
|
|
SpriteTemplate[][] st = getSpriteTemplate();
|
|
|
|
for (int i = 0; i < map.length; i++)
|
|
for (int j = 0; j < map[i].length; j++) {
|
|
clone.setBlock(i, j, map[i][j]);
|
|
clone.setSpriteTemplate(i, j, st[i][j]);
|
|
}
|
|
clone.BLOCKS_COINS = BLOCKS_COINS;
|
|
clone.BLOCKS_EMPTY = BLOCKS_EMPTY;
|
|
clone.BLOCKS_POWER = BLOCKS_POWER;
|
|
clone.ENEMIES = ENEMIES;
|
|
clone.COINS = COINS;
|
|
|
|
return clone;
|
|
|
|
}
|
|
|
|
private void blockify(Level level, boolean[][] blocks, int width, int height) {
|
|
int to = 0;
|
|
if (type == LevelInterface.TYPE_CASTLE) {
|
|
to = 4 * 2;
|
|
} else if (type == LevelInterface.TYPE_UNDERGROUND) {
|
|
to = 4 * 3;
|
|
}
|
|
|
|
boolean[][] b = new boolean[2][2];
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
for (int y = 0; y < height; y++) {
|
|
for (int xx = x; xx <= x + 1; xx++) {
|
|
for (int yy = y; yy <= y + 1; yy++) {
|
|
int _xx = xx;
|
|
int _yy = yy;
|
|
if (_xx < 0)
|
|
_xx = 0;
|
|
if (_yy < 0)
|
|
_yy = 0;
|
|
if (_xx > width - 1)
|
|
_xx = width - 1;
|
|
if (_yy > height - 1)
|
|
_yy = height - 1;
|
|
b[xx - x][yy - y] = blocks[_xx][_yy];
|
|
}
|
|
}
|
|
|
|
if (b[0][0] == b[1][0] && b[0][1] == b[1][1]) {
|
|
if (b[0][0] == b[0][1]) {
|
|
if (b[0][0]) {
|
|
level.setBlock(x, y, (byte) (1 + 9 * 16 + to));
|
|
} else {
|
|
// KEEP OLD BLOCK!
|
|
}
|
|
} else {
|
|
if (b[0][0]) {
|
|
// down grass top?
|
|
level.setBlock(x, y, (byte) (1 + 10 * 16 + to));
|
|
} else {
|
|
// up grass top
|
|
level.setBlock(x, y, (byte) (1 + 8 * 16 + to));
|
|
}
|
|
}
|
|
} else if (b[0][0] == b[0][1] && b[1][0] == b[1][1]) {
|
|
if (b[0][0]) {
|
|
// right grass top
|
|
level.setBlock(x, y, (byte) (2 + 9 * 16 + to));
|
|
} else {
|
|
// left grass top
|
|
level.setBlock(x, y, (byte) (0 + 9 * 16 + to));
|
|
}
|
|
} else if (b[0][0] == b[1][1] && b[0][1] == b[1][0]) {
|
|
level.setBlock(x, y, (byte) (1 + 9 * 16 + to));
|
|
} else if (b[0][0] == b[1][0]) {
|
|
if (b[0][0]) {
|
|
if (b[0][1]) {
|
|
level.setBlock(x, y, (byte) (3 + 10 * 16 + to));
|
|
} else {
|
|
level.setBlock(x, y, (byte) (3 + 11 * 16 + to));
|
|
}
|
|
} else {
|
|
if (b[0][1]) {
|
|
// right up grass top
|
|
level.setBlock(x, y, (byte) (2 + 8 * 16 + to));
|
|
} else {
|
|
// left up grass top
|
|
level.setBlock(x, y, (byte) (0 + 8 * 16 + to));
|
|
}
|
|
}
|
|
} else if (b[0][1] == b[1][1]) {
|
|
if (b[0][1]) {
|
|
if (b[0][0]) {
|
|
// left pocket grass
|
|
level.setBlock(x, y, (byte) (3 + 9 * 16 + to));
|
|
} else {
|
|
// right pocket grass
|
|
level.setBlock(x, y, (byte) (3 + 8 * 16 + to));
|
|
}
|
|
} else {
|
|
if (b[0][0]) {
|
|
level.setBlock(x, y, (byte) (2 + 10 * 16 + to));
|
|
} else {
|
|
level.setBlock(x, y, (byte) (0 + 10 * 16 + to));
|
|
}
|
|
}
|
|
} else {
|
|
level.setBlock(x, y, (byte) (0 + 1 * 16 + to));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private int buildBowlingAlley(int xo, int maxLength) {
|
|
if (maxLength >= 26) {
|
|
|
|
int floor = height - 1 - random.nextInt(4);
|
|
int enemyType = shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY) ? (shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY) ? SpriteTemplate.ARMORED_TURTLE
|
|
: SpriteTemplate.GREEN_TURTLE)
|
|
: SpriteTemplate.GOOMPA;
|
|
PlayerProfile.ChallengeRewardType reward = shouldAddReward();
|
|
boolean arc = random.nextBoolean();
|
|
int numEnemies = 0;
|
|
for (int i = 0; i < 10; i++) {
|
|
numEnemies += shouldAddChallenge(PlayerProfile.ChallengeRewardType.ENEMY) ? 1
|
|
: 0;
|
|
}
|
|
|
|
// Create the pit.
|
|
for (int y = floor - 1; y < height; y++) {
|
|
setBlock(xo, y, Level.GROUND);
|
|
setBlock(xo + 1, y, Level.GROUND);
|
|
}
|
|
|
|
for (int y = floor; y < height; y++) {
|
|
setBlock(xo + 2, y, Level.GROUND);
|
|
setBlock(xo + 3, y, Level.GROUND);
|
|
setBlock(xo + 4, y, Level.GROUND);
|
|
}
|
|
|
|
setSpriteTemplate(xo + 2, floor - 1, new SpriteTemplate(
|
|
SpriteTemplate.RED_TURTLE, false));
|
|
|
|
for (int x = 5; x < 26; x++) {
|
|
for (int y = floor - 3; y < height; y++) {
|
|
setBlock(xo + x, y, Level.GROUND);
|
|
}
|
|
|
|
if (arc && reward == PlayerProfile.ChallengeRewardType.COIN
|
|
&& x >= 11 && x <= 23) {
|
|
if (x == 15) {
|
|
setBlock(xo + x, floor - 6, Level.COIN);
|
|
}
|
|
|
|
else if (x == 16) {
|
|
setBlock(xo + x, floor - 7, Level.COIN);
|
|
}
|
|
|
|
else if (x == 18) {
|
|
setBlock(xo + x, floor - 8, Level.COIN);
|
|
}
|
|
|
|
else if (x == 19) {
|
|
setBlock(xo + x, floor - 8, Level.COIN);
|
|
}
|
|
|
|
else if (x == 20) {
|
|
setBlock(xo + x, floor - 8, Level.COIN);
|
|
}
|
|
|
|
else if (x == 22) {
|
|
setBlock(xo + x, floor - 7, Level.COIN);
|
|
}
|
|
|
|
else if (x == 23) {
|
|
setBlock(xo + x, floor - 6, Level.COIN);
|
|
}
|
|
}
|
|
|
|
else if (!arc
|
|
&& reward == PlayerProfile.ChallengeRewardType.COIN
|
|
&& x >= 10 && x <= 20) {
|
|
setBlock(xo + x, floor - 6, Level.COIN);
|
|
}
|
|
|
|
else if (reward == PlayerProfile.ChallengeRewardType.BLOCK
|
|
&& x >= 13 && x <= 17) {
|
|
setBlock(
|
|
xo + x,
|
|
floor - 6,
|
|
(random.nextDouble() <= probability
|
|
.get(PlayerProfile.ChallengeRewardType.POWERUP)) ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
}
|
|
|
|
if (x >= 26 - numEnemies) {
|
|
setSpriteTemplate(xo + x, floor - 4, new SpriteTemplate(
|
|
enemyType, false));
|
|
}
|
|
}
|
|
|
|
return 26;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private int buildCannonLine(int xo, int maxLength) {
|
|
if (maxLength >= 6) {
|
|
|
|
int floor = height - 1 - random.nextInt(4);
|
|
int length = 6;
|
|
|
|
for (int x = 0; x < length; x++) {
|
|
for (int y = floor; y < height; y++) {
|
|
setBlock(xo + x, y, Level.GROUND);
|
|
}
|
|
|
|
if (x == 3) {
|
|
setBlock(xo + x, floor - 1, Level.CANNON_MIDDLE);
|
|
setBlock(xo + x, floor - 2, Level.CANNON_TOP);
|
|
setBlock(
|
|
xo + x,
|
|
floor - 3,
|
|
shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY) ? Level.CANNON_TOP
|
|
: Level.CANNON_MIDDLE);
|
|
setBlock(xo + x, floor - 4, Level.CANNON_TOP);
|
|
}
|
|
}
|
|
|
|
return length;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
private int buildCoinDive(int xo, int maxLength) {
|
|
if (maxLength >= 18) {
|
|
int length = 18;
|
|
|
|
int floor = height - 1 - random.nextInt(4);
|
|
|
|
for (int x = 0; x < length; x++) {
|
|
for (int y = floor; y < height; y++) {
|
|
setBlock(xo + x, y, Level.GROUND);
|
|
}
|
|
|
|
if (x > 2 && x < 6) {
|
|
setBlock(xo + x, floor - 3, Level.BLOCK_EMPTY);
|
|
} else if (x > 2 && x < 11) {
|
|
setBlock(xo + x, floor - 5, Level.BLOCK_EMPTY);
|
|
} else if (x == 11 || x == 12) {
|
|
setBlock(xo + x, floor - 6, Level.COIN);
|
|
} else if (x == 13) {
|
|
setBlock(xo + x, floor - 5, Level.COIN);
|
|
} else if (x == 14) {
|
|
setBlock(xo + x, floor - 4, Level.COIN);
|
|
} else if (x == 15) {
|
|
setBlock(xo + x, floor - 3, Level.COIN);
|
|
}
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
private int buildFreebie(int xo, int maxLength) {
|
|
if (maxLength >= 9) {
|
|
int floor = height - 1 - random.nextInt(4);
|
|
for (int x = 0; x < 9; x++) {
|
|
for (int y = 0; y < height; y++) {
|
|
if (y >= floor) {
|
|
setBlock(xo + x, y, GROUND);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (random.nextBoolean()) {
|
|
setBlock(xo + 1, floor - 1, Level.BLOCK_EMPTY);
|
|
setBlock(xo + 7, floor - 1, Level.BLOCK_POWERUP);
|
|
|
|
setSpriteTemplate(xo + 6, floor - 1, new SpriteTemplate(
|
|
SpriteTemplate.GREEN_TURTLE, false));
|
|
}
|
|
|
|
else {
|
|
int powerupLoc = random.nextInt(5);
|
|
|
|
setBlock(xo + 2, floor - 3,
|
|
powerupLoc == 0 ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
setBlock(xo + 3, floor - 3,
|
|
powerupLoc == 1 ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
setBlock(xo + 4, floor - 3,
|
|
powerupLoc == 2 ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
setBlock(xo + 5, floor - 3,
|
|
powerupLoc == 3 ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
setBlock(xo + 6, floor - 3,
|
|
powerupLoc == 4 ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
}
|
|
|
|
return 9;
|
|
}
|
|
|
|
else if (maxLength >= 5) {
|
|
|
|
int floor = height - 1 - random.nextInt(4);
|
|
for (int x = 0; x < 5; x++) {
|
|
for (int y = floor; y < height; y++) {
|
|
setBlock(xo + x, y, GROUND);
|
|
}
|
|
}
|
|
|
|
if (random.nextBoolean()) {
|
|
setBlock(xo, floor - 1, Level.BLOCK_EMPTY);
|
|
setBlock(xo + 4, floor - 1, Level.BLOCK_POWERUP);
|
|
|
|
setSpriteTemplate(xo + 3, floor - 1, new SpriteTemplate(
|
|
SpriteTemplate.GREEN_TURTLE, false));
|
|
}
|
|
|
|
else {
|
|
setBlock(xo + 2, floor - 3, Level.BLOCK_POWERUP);
|
|
}
|
|
|
|
return 5;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
private int buildLemmingTrap(int xo, int maxLength) {
|
|
if (maxLength >= 14) {
|
|
int floor = height - 1 - random.nextInt(4);
|
|
int enemyType = shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY) ? (shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY) ? SpriteTemplate.ARMORED_TURTLE
|
|
: SpriteTemplate.GREEN_TURTLE)
|
|
: SpriteTemplate.GOOMPA;
|
|
boolean flying = shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY)
|
|
&& shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY)
|
|
&& shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY);
|
|
PlayerProfile.ChallengeRewardType reward = shouldAddReward();
|
|
|
|
for (int x = 0; x < 18; x++) {
|
|
if (x > 5) {
|
|
for (int y = floor - 4; y < height; y++) {
|
|
setBlock(xo + x, y, Level.GROUND);
|
|
}
|
|
}
|
|
|
|
else {
|
|
for (int y = floor; y < height; y++) {
|
|
setBlock(xo + x, y, Level.GROUND);
|
|
}
|
|
}
|
|
|
|
if (x > 6 && x % 2 == 0) {
|
|
setSpriteTemplate(xo + x, floor - 5, new SpriteTemplate(
|
|
enemyType, flying));
|
|
}
|
|
|
|
if (reward == PlayerProfile.ChallengeRewardType.COIN && x >= 7
|
|
&& x <= 16) {
|
|
setBlock(xo + x, floor - 7, Level.COIN);
|
|
}
|
|
|
|
else if (reward == PlayerProfile.ChallengeRewardType.BLOCK
|
|
&& x >= 10 && x <= 13) {
|
|
setBlock(
|
|
xo + x,
|
|
floor - 7,
|
|
random.nextDouble() < probability
|
|
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
}
|
|
}
|
|
return 18;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private int buildMaze(int xo, int maxLength) {
|
|
if (maxLength >= 19) {
|
|
|
|
int length = random.nextInt(maxLength - 19) + 20;
|
|
int soFar = 6;
|
|
int next;
|
|
// boolean skipUp = false;
|
|
// boolean skipDown = false;
|
|
|
|
class Stretch {
|
|
public int len;
|
|
public LevelComponent.MazeLevel lvl;
|
|
|
|
public Stretch(int lngth) {
|
|
len = lngth;
|
|
|
|
switch (random.nextInt(3)) {
|
|
case 0:
|
|
lvl = LevelComponent.MazeLevel.TOP;
|
|
break;
|
|
case 1:
|
|
lvl = LevelComponent.MazeLevel.MID;
|
|
break;
|
|
default:
|
|
lvl = LevelComponent.MazeLevel.BOT;
|
|
}
|
|
}
|
|
}
|
|
|
|
ArrayList<Stretch> maze = new ArrayList<Stretch>();
|
|
|
|
loop: while (soFar < length) {
|
|
if (soFar + 3 > length) {
|
|
length = soFar;
|
|
break loop;
|
|
}
|
|
|
|
next = random.nextInt(18) + 5;
|
|
|
|
if (soFar + next > length) {
|
|
next = length - soFar;
|
|
}
|
|
|
|
maze.add(new Stretch(next));
|
|
|
|
soFar += next;
|
|
}
|
|
|
|
setBlock(xo, this.height - 1, Level.GROUND);
|
|
setBlock(xo + 1, this.height - 1, Level.GROUND);
|
|
setBlock(xo + 2, this.height - 1, Level.GROUND);
|
|
soFar = 3;
|
|
|
|
Stretch str;
|
|
// Stretch nxt;
|
|
boolean stretchEnd;
|
|
boolean midLine;
|
|
boolean addEnemy;
|
|
int heightMod;
|
|
int enemyType;
|
|
for (int i = 0; i < maze.size(); i++) {
|
|
|
|
str = maze.get(i);
|
|
|
|
for (int x = 0; x < str.len; x++) {
|
|
|
|
setBlock(xo + soFar + x, this.height - 1, Level.GROUND);
|
|
|
|
// skipUp = (skipUp && (x == str.len - 2))
|
|
// || (str.len >= 5 && x == (str.len / 2));
|
|
// skipDown = (skipDown && (x == str.len - 2))
|
|
// || (str.len >= 5 && x == (str.len / 2));
|
|
midLine = (str.len >= 5 && x == (str.len / 2) - 1);
|
|
stretchEnd = (x == str.len - 1);
|
|
|
|
addEnemy = !midLine && !stretchEnd
|
|
&& (random.nextDouble() < .25);
|
|
|
|
if // ((stretchEnd && nxt != null && nxt.lvl !=
|
|
// MazeLevel.BOT)
|
|
// ||
|
|
(midLine && str.lvl != LevelComponent.MazeLevel.BOT) // )
|
|
{
|
|
setBlock(xo + soFar + x, this.height - 2, Level.ROCK);
|
|
setBlock(xo + soFar + x, this.height - 3, Level.ROCK);
|
|
}
|
|
if // ((stretchEnd && nxt != null && nxt.lvl !=
|
|
// MazeLevel.MID)
|
|
// ||
|
|
(midLine && str.lvl != LevelComponent.MazeLevel.MID)// )
|
|
{
|
|
setBlock(xo + soFar + x, this.height - 5, Level.ROCK);
|
|
setBlock(xo + soFar + x, this.height - 6, Level.ROCK);
|
|
}
|
|
if // ((stretchEnd && nxt != null && nxt.lvl !=
|
|
// MazeLevel.TOP)
|
|
// ||
|
|
(midLine && str.lvl != LevelComponent.MazeLevel.TOP)// )
|
|
{
|
|
setBlock(xo + soFar + x, this.height - 8, Level.ROCK);
|
|
setBlock(xo + soFar + x, this.height - 9, Level.ROCK);
|
|
setBlock(xo + soFar + x, this.height - 10, Level.ROCK);
|
|
setBlock(xo + soFar + x, this.height - 11, Level.ROCK);
|
|
setBlock(xo + soFar + x, this.height - 12, Level.ROCK);
|
|
setBlock(xo + soFar + x, this.height - 13, Level.ROCK);
|
|
setBlock(xo + soFar + x, this.height - 14, Level.ROCK);
|
|
setBlock(xo + soFar + x, this.height - 15, Level.ROCK);
|
|
}
|
|
|
|
if (!stretchEnd) {
|
|
setBlock(xo + soFar + x, this.height - 7, Level.ROCK);
|
|
}
|
|
|
|
if (!stretchEnd) {
|
|
setBlock(xo + soFar + x, this.height - 4, Level.ROCK);
|
|
}
|
|
|
|
if (addEnemy
|
|
&& shouldAddChallenge(PlayerProfile.ChallengeRewardType.ENEMY)) {
|
|
|
|
enemyType = shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY) ? (shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY) ? (shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY) ? SpriteTemplate.ARMORED_TURTLE
|
|
: SpriteTemplate.RED_TURTLE)
|
|
: SpriteTemplate.GREEN_TURTLE)
|
|
: SpriteTemplate.GOOMPA;
|
|
|
|
switch (random.nextInt(3)) {
|
|
case 0:
|
|
heightMod = 8;
|
|
break;
|
|
case 1:
|
|
heightMod = 5;
|
|
break;
|
|
default:
|
|
heightMod = 2;
|
|
}
|
|
|
|
setSpriteTemplate(xo + soFar + x, this.height
|
|
- heightMod, new SpriteTemplate(enemyType,
|
|
false));
|
|
}
|
|
}
|
|
|
|
soFar += str.len;
|
|
}
|
|
|
|
setBlock(xo + length - 1, this.height - 1, Level.GROUND);
|
|
setBlock(xo + length - 2, this.height - 1, Level.GROUND);
|
|
setBlock(xo + length - 3, this.height - 1, Level.GROUND);
|
|
|
|
return length;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
private int buildPipeJump(int xo, int maxLength) {
|
|
int length = 0;
|
|
int lastHeight = 4;
|
|
int localHeight;
|
|
int midFloor;
|
|
int pipeHeight;
|
|
int gap = 0;
|
|
boolean space;
|
|
boolean deadGaps = shouldAddChallenge(PlayerProfile.ChallengeRewardType.GAP);
|
|
|
|
int numPipes;
|
|
for (numPipes = 0; shouldAddChallenge(deadGaps ? PlayerProfile.ChallengeRewardType.GAP
|
|
: PlayerProfile.ChallengeRewardType.JUMP); numPipes++) {
|
|
}
|
|
|
|
localHeight = 4;
|
|
loop: for (int i = 0; i < numPipes; i++) {
|
|
space = (length + 2) <= maxLength;
|
|
|
|
if (space) {
|
|
|
|
lastHeight = localHeight;
|
|
|
|
pipeHeight = localHeight > 5 ? 4 + random
|
|
.nextInt(localHeight - 4) : 4;
|
|
|
|
for (int y = height - localHeight; y < height; y++) {
|
|
|
|
if (y == height - localHeight) {
|
|
setBlock(xo + length, y, Level.TUBE_TOP_LEFT);
|
|
setBlock(xo + length + 1, y, Level.TUBE_TOP_RIGHT);
|
|
}
|
|
|
|
else if (y - height - localHeight < pipeHeight) {
|
|
setBlock(xo + length, y, Level.TUBE_SIDE_LEFT);
|
|
setBlock(xo + length + 1, y, Level.TUBE_SIDE_RIGHT);
|
|
}
|
|
|
|
else {
|
|
setBlock(xo + length, y, Level.GROUND);
|
|
setBlock(xo + length + 1, y, Level.GROUND);
|
|
}
|
|
}
|
|
|
|
if (shouldAddChallenge(PlayerProfile.ChallengeRewardType.ENEMY)) {
|
|
setSpriteTemplate(xo + length, height - localHeight,
|
|
new SpriteTemplate(SpriteTemplate.JUMP_FLOWER,
|
|
false));
|
|
}
|
|
|
|
length += 2;
|
|
|
|
localHeight = random.nextInt(7) + 4;
|
|
while (Math.abs(localHeight - lastHeight) > 3) {
|
|
localHeight += localHeight > lastHeight ? -1 : 1;
|
|
}
|
|
|
|
midFloor = localHeight - (random.nextInt(4) + 1);
|
|
if (midFloor <= 0) {
|
|
midFloor = 1;
|
|
}
|
|
|
|
for (gap = 0; gap <= 4
|
|
&& length + gap <= maxLength
|
|
&& shouldAddChallenge(PlayerProfile.ChallengeRewardType.JUMP); gap++) {
|
|
|
|
if (!deadGaps) {
|
|
for (int y = 0; y < midFloor; y++) {
|
|
setBlock(xo + length, this.height - 1 - y,
|
|
Level.GROUND);
|
|
}
|
|
}
|
|
length++;
|
|
}
|
|
}
|
|
|
|
else {
|
|
break loop;
|
|
}
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
private int buildPlatformJump(int xo, int maxLength) {
|
|
int length = 0;
|
|
int gapLength;
|
|
|
|
for (gapLength = 1; shouldAddChallenge(PlayerProfile.ChallengeRewardType.JUMP)
|
|
&& gapLength < 4; gapLength++) {
|
|
}
|
|
|
|
int maxNumPlatforms = (maxLength - 3) / (4 + gapLength);
|
|
|
|
if (maxNumPlatforms == 0) {
|
|
return 0;
|
|
}
|
|
|
|
int numPlatforms;
|
|
for (numPlatforms = 0; numPlatforms < maxNumPlatforms
|
|
&& shouldAddChallenge(PlayerProfile.ChallengeRewardType.GAP); numPlatforms++) {
|
|
}
|
|
|
|
if (numPlatforms > 1) {
|
|
boolean found = false;
|
|
LevelComponent.MazeLevel nextDir = LevelComponent.MazeLevel.TOP;
|
|
LevelComponent.PlatformLevel last;
|
|
LevelComponent.PlatformLevel next;
|
|
ArrayList<LevelComponent.PlatformLevel> jumps = new ArrayList<LevelComponent.PlatformLevel>();
|
|
int heightMod = 0;
|
|
int lastHeightMod = 0;
|
|
int enemyType;
|
|
PlayerProfile.ChallengeRewardType reward;
|
|
LevelComponent.PlatformLevel hold;
|
|
|
|
jumps.add(LevelComponent.PlatformLevel.BOT);
|
|
|
|
for (int i = 1; i < numPlatforms; i++) {
|
|
last = jumps.get(i - 1);
|
|
found = false;
|
|
|
|
while (!found) {
|
|
switch (random.nextInt(5)) {
|
|
case 0:
|
|
case 1:
|
|
nextDir = LevelComponent.MazeLevel.BOT;
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
nextDir = LevelComponent.MazeLevel.TOP;
|
|
break;
|
|
default:
|
|
nextDir = LevelComponent.MazeLevel.MID;
|
|
}
|
|
|
|
found = !((last == LevelComponent.PlatformLevel.TOP && nextDir == LevelComponent.MazeLevel.TOP) || (last == LevelComponent.PlatformLevel.BOT && nextDir == LevelComponent.MazeLevel.BOT));
|
|
}
|
|
|
|
if ((last == LevelComponent.PlatformLevel.BOT && nextDir == LevelComponent.MazeLevel.MID)
|
|
|| (last == LevelComponent.PlatformLevel.MID_D && nextDir == LevelComponent.MazeLevel.BOT)) {
|
|
next = LevelComponent.PlatformLevel.BOT;
|
|
}
|
|
|
|
else if ((last == LevelComponent.PlatformLevel.MID_D && nextDir == LevelComponent.MazeLevel.MID)
|
|
|| (last == LevelComponent.PlatformLevel.MID_U && nextDir == LevelComponent.MazeLevel.BOT)
|
|
|| (last == LevelComponent.PlatformLevel.BOT && nextDir == LevelComponent.MazeLevel.TOP)) {
|
|
next = LevelComponent.PlatformLevel.MID_D;
|
|
}
|
|
|
|
else if ((last == LevelComponent.PlatformLevel.MID_U && nextDir == LevelComponent.MazeLevel.MID)
|
|
|| (last == LevelComponent.PlatformLevel.TOP && nextDir == LevelComponent.MazeLevel.BOT)
|
|
|| (last == LevelComponent.PlatformLevel.MID_D && nextDir == LevelComponent.MazeLevel.TOP)) {
|
|
next = LevelComponent.PlatformLevel.MID_U;
|
|
}
|
|
|
|
else // if ((last == PlatformLevel.TOP && nextDir ==
|
|
// MazeLevel.MID)
|
|
// || (last == PlatformLevel.MID_U && nextDir ==
|
|
// MazeLevel.TOP))
|
|
{
|
|
next = LevelComponent.PlatformLevel.TOP;
|
|
}
|
|
|
|
jumps.add(next);
|
|
}
|
|
|
|
setBlock(xo, this.height - 1, Level.GROUND);
|
|
setBlock(xo + 1, this.height - 1, Level.GROUND);
|
|
setBlock(xo + 2, this.height - 1, Level.GROUND);
|
|
length = 3;
|
|
|
|
for (int x = 0; x < jumps.size(); x++) {
|
|
if (x > 0) {
|
|
lastHeightMod = heightMod;
|
|
}
|
|
|
|
hold = jumps.get(x);
|
|
switch (hold) {
|
|
case TOP:
|
|
heightMod = 12;
|
|
break;
|
|
case MID_U:
|
|
heightMod = 9;
|
|
break;
|
|
case MID_D:
|
|
heightMod = 6;
|
|
break;
|
|
default:
|
|
heightMod = 3;
|
|
}
|
|
|
|
heightMod += (x > 0 && random.nextBoolean() ? random.nextInt(2)
|
|
: -1 * random.nextInt(2));
|
|
|
|
while (x > 0 && heightMod - lastHeightMod >= 5) {
|
|
heightMod--;
|
|
}
|
|
|
|
setBlock(xo + length, this.height - heightMod, Level.ROCK);
|
|
setBlock(xo + length + 1, this.height - heightMod, Level.ROCK);
|
|
setBlock(xo + length + 2, this.height - heightMod, Level.ROCK);
|
|
setBlock(xo + length + 3, this.height - heightMod, Level.ROCK);
|
|
|
|
if (shouldAddChallenge(PlayerProfile.ChallengeRewardType.ENEMY)) {
|
|
|
|
enemyType = random.nextBoolean() ? SpriteTemplate.RED_TURTLE
|
|
: (shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY) ? (shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY) ? SpriteTemplate.ARMORED_TURTLE
|
|
: SpriteTemplate.GREEN_TURTLE)
|
|
: SpriteTemplate.GOOMPA);
|
|
|
|
setSpriteTemplate(
|
|
xo + length + 3,
|
|
this.height - heightMod - 1,
|
|
new SpriteTemplate(
|
|
enemyType,
|
|
(enemyType == SpriteTemplate.RED_TURTLE && shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY))
|
|
|| enemyType != SpriteTemplate.RED_TURTLE));
|
|
}
|
|
|
|
reward = shouldAddReward();
|
|
if (reward == PlayerProfile.ChallengeRewardType.COIN
|
|
&& hold != LevelComponent.PlatformLevel.TOP) {
|
|
setBlock(xo + length, this.height - heightMod - 3,
|
|
Level.COIN);
|
|
setBlock(xo + length + 1, this.height - heightMod - 3,
|
|
Level.COIN);
|
|
setBlock(xo + length + 2, this.height - heightMod - 3,
|
|
Level.COIN);
|
|
setBlock(xo + length + 3, this.height - heightMod - 3,
|
|
Level.COIN);
|
|
}
|
|
|
|
else if (reward == PlayerProfile.ChallengeRewardType.BLOCK
|
|
&& hold != LevelComponent.PlatformLevel.TOP) {
|
|
setBlock(
|
|
xo + length + 1,
|
|
this.height - heightMod - 3,
|
|
random.nextDouble() < probability
|
|
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
setBlock(
|
|
xo + length + 2,
|
|
this.height - heightMod - 3,
|
|
random.nextDouble() < probability
|
|
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
}
|
|
|
|
length += 4 + gapLength;
|
|
}
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
private int buildSinglePit(int xo, int maxLength) {
|
|
if (maxLength >= 13) {
|
|
int length = 13;
|
|
int floor = height - 1 - random.nextInt(4);
|
|
|
|
for (int x = 0; x < length; x++) {
|
|
if (x != 5 && x != 6 && x != 7) {
|
|
for (int y = floor; y < height; y++) {
|
|
setBlock(xo + x, y, Level.GROUND);
|
|
}
|
|
}
|
|
|
|
if ((x >= 2 && x <= 4) || (x >= 8 && x <= 10)) {
|
|
setBlock(xo + x, floor - 1, Level.ROCK);
|
|
|
|
if (x == 3 || x == 4 || x == 8 || x == 9) {
|
|
setBlock(xo + x, floor - 2, Level.ROCK);
|
|
}
|
|
|
|
if (x == 4 || x == 8) {
|
|
setBlock(xo + x, floor - 3, Level.ROCK);
|
|
}
|
|
}
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private int buildStraight(int xo, int maxLength, boolean allowEnemies) {
|
|
int length = random.nextInt(15) + 2;
|
|
|
|
// if (safe)
|
|
// length = 10 + random.nextInt(5);
|
|
|
|
if (length > maxLength)
|
|
length = maxLength;
|
|
|
|
int floor = height - 1 - random.nextInt(4);
|
|
PlayerProfile.ChallengeRewardType reward = shouldAddReward();
|
|
|
|
// runs from the specified x position to the length of the segment
|
|
for (int x = xo; x < xo + length; x++) {
|
|
for (int y = 0; y < height; y++) {
|
|
if (y >= floor) {
|
|
setBlock(x, y, GROUND);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (shouldAddChallenge(PlayerProfile.ChallengeRewardType.ENEMY)
|
|
&& allowEnemies) {
|
|
int enemyType = shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY) ? (shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY) ? (shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY) ? (shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY)
|
|
|| (reward != null && length >= 5) ? SpriteTemplate.ARMORED_TURTLE
|
|
: SpriteTemplate.CANNON_BALL)
|
|
: SpriteTemplate.RED_TURTLE)
|
|
: SpriteTemplate.GREEN_TURTLE)
|
|
: SpriteTemplate.GOOMPA;
|
|
|
|
if (enemyType == SpriteTemplate.CANNON_BALL) {
|
|
int height = random.nextInt(4) + 1;
|
|
|
|
if (height == 1) {
|
|
setBlock(xo + (length / 2), floor - 1, Level.CANNON_TOP);
|
|
}
|
|
|
|
else if (height == 2) {
|
|
setBlock(xo + (length / 2), floor - 1, Level.CANNON_MIDDLE);
|
|
setBlock(xo + (length / 2), floor - 2, Level.CANNON_TOP);
|
|
}
|
|
|
|
else if (height == 3) {
|
|
setBlock(xo + (length / 2), floor - 1, Level.CANNON_BOTTOM);
|
|
setBlock(xo + (length / 2), floor - 2, Level.CANNON_MIDDLE);
|
|
setBlock(xo + (length / 2), floor - 3, Level.CANNON_TOP);
|
|
}
|
|
|
|
else {
|
|
setBlock(xo + (length / 2), floor - 1, Level.CANNON_BOTTOM);
|
|
setBlock(xo + (length / 2), floor - 2, Level.CANNON_BOTTOM);
|
|
setBlock(xo + (length / 2), floor - 3, Level.CANNON_MIDDLE);
|
|
setBlock(xo + (length / 2), floor - 4, Level.CANNON_TOP);
|
|
}
|
|
}
|
|
|
|
else {
|
|
setSpriteTemplate(
|
|
xo + (length / 2),
|
|
floor - 1,
|
|
new SpriteTemplate(
|
|
enemyType,
|
|
shouldAddChallenge(PlayerProfile.ChallengeRewardType.HARDER_ENEMY)));
|
|
}
|
|
}
|
|
|
|
if (reward != null) {
|
|
if (length >= 13) {
|
|
setBlock(xo + (length - 11), floor - 3, Level.COIN);
|
|
setBlock(xo + (length - 10), floor - 4, Level.COIN);
|
|
setBlock(xo + (length - 8), floor - 5, Level.COIN);
|
|
setBlock(xo + (length - 7), floor - 5, Level.COIN);
|
|
setBlock(xo + (length - 6), floor - 5, Level.COIN);
|
|
setBlock(xo + (length - 4), floor - 4, Level.COIN);
|
|
setBlock(xo + (length - 3), floor - 3, Level.COIN);
|
|
}
|
|
|
|
else if (length >= 7) {
|
|
int start = length / 2;
|
|
boolean coins = (reward == PlayerProfile.ChallengeRewardType.COIN);
|
|
|
|
setBlock(
|
|
xo + (start - 2),
|
|
floor - 3,
|
|
coins ? Level.COIN
|
|
: random.nextDouble() < probability
|
|
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
setBlock(
|
|
xo + (start - 1),
|
|
floor - 3,
|
|
coins ? Level.COIN
|
|
: random.nextDouble() < probability
|
|
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
setBlock(
|
|
xo + start,
|
|
floor - 3,
|
|
coins ? Level.COIN
|
|
: random.nextDouble() < probability
|
|
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
setBlock(
|
|
xo + (start + 1),
|
|
floor - 3,
|
|
coins ? Level.COIN
|
|
: random.nextDouble() < probability
|
|
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
if (length % 2 != 0) {
|
|
setBlock(
|
|
xo + (start + 2),
|
|
floor - 3,
|
|
coins ? Level.COIN
|
|
: random.nextDouble() < probability
|
|
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
}
|
|
}
|
|
|
|
else if (length >= 5) {
|
|
int start = length / 2;
|
|
boolean coins = (reward == PlayerProfile.ChallengeRewardType.COIN);
|
|
|
|
setBlock(
|
|
xo + (start - 1),
|
|
floor - 3,
|
|
coins ? Level.COIN
|
|
: random.nextDouble() < probability
|
|
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
setBlock(
|
|
xo + start,
|
|
floor - 3,
|
|
coins ? Level.COIN
|
|
: random.nextDouble() < probability
|
|
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
setBlock(
|
|
xo + (start + 1),
|
|
floor - 3,
|
|
coins ? Level.COIN
|
|
: random.nextDouble() < probability
|
|
.get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
|
|
: Level.BLOCK_COIN);
|
|
}
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
private void create(long seed, PlayerProfile profile,
|
|
LevelArchetype archetype, LevelGrammar grammar) {
|
|
|
|
System.out
|
|
.println("PlayerProfile.getProbability(COINS): "
|
|
+ profile
|
|
.getProbability(PlayerProfile.ChallengeRewardType.COIN));
|
|
|
|
if (dataRecorder == DataRecorder.BLANK_RECORD) {
|
|
System.out
|
|
.println("DataRecorder record is BLANK - using GamePlay metrics only.");
|
|
}
|
|
|
|
this.type = archetype.getTypeInt();
|
|
|
|
lastSeed = seed;
|
|
random = new Random(seed);
|
|
int length = 0;
|
|
|
|
if (TESTING) {
|
|
int minElements = 5;
|
|
int maxLen;
|
|
int elementsSoFar = 0;
|
|
|
|
length = buildStraight(0, width - 64, false);
|
|
|
|
while (length < width - 64) {
|
|
maxLen = elementsSoFar < minElements ? (width - 64 - length)
|
|
/ (minElements - elementsSoFar) : width - 64 - length;
|
|
|
|
switch (random.nextInt(9)) {
|
|
case 0:
|
|
length += buildStraight(length, maxLen, true);
|
|
break;
|
|
case 1:
|
|
length += buildFreebie(length, maxLen);
|
|
break;
|
|
case 2:
|
|
length += buildBowlingAlley(length, maxLen);
|
|
break;
|
|
case 3:
|
|
length += buildLemmingTrap(length, maxLen);
|
|
break;
|
|
case 4:
|
|
length += buildPipeJump(length, maxLen);
|
|
break;
|
|
case 5:
|
|
length += buildPlatformJump(length, maxLen);
|
|
break;
|
|
case 6:
|
|
length += buildCoinDive(length, maxLen);
|
|
break;
|
|
case 7:
|
|
length += buildCannonLine(length, maxLen);
|
|
break;
|
|
case 8:
|
|
length += buildSinglePit(length, maxLen);
|
|
break;
|
|
default:
|
|
length += buildMaze(length, maxLen);
|
|
}
|
|
|
|
elementsSoFar++;
|
|
}
|
|
}
|
|
|
|
else {
|
|
System.out.println("Generating level for component list: ");
|
|
LevelParseTree parseTree = grammar.generateRandomTree(seed, width);
|
|
|
|
int MAX_REGENS = 30;
|
|
int nRegens = 0;
|
|
while (!FitnessEvaluator.isFit(parseTree, profile, archetype)
|
|
&& nRegens < MAX_REGENS) {
|
|
System.out
|
|
.println("Generated level is NOT fit. Regenerating...");
|
|
parseTree = grammar.generateRandomTree(seed, width);
|
|
nRegens++;
|
|
}
|
|
|
|
List<LevelComponent> levelTemplate = parseTree.getLevelTemplate();
|
|
|
|
if (nRegens == MAX_REGENS) {
|
|
System.out.println("Failed to generate a fit level after "
|
|
+ nRegens + " attempts. Proceeding with unfit level.");
|
|
} else {
|
|
System.out.println("Found fit level:");
|
|
for (LevelComponent lc : levelTemplate) {
|
|
System.out.println(lc);
|
|
}
|
|
}
|
|
|
|
for (LevelComponent lcomp : levelTemplate) {
|
|
LevelComponent.TYPE lctype = lcomp.getType();
|
|
System.out.println("Building for: " + lcomp);
|
|
while (length < Math.min(width - 64, lcomp.getEnd())) {
|
|
int lcLength;
|
|
switch (lctype) {
|
|
case FLAT_LO:
|
|
case FLAT_HI:
|
|
lcLength = buildStraight(length, lcomp.getEnd(), true);
|
|
break;
|
|
case PIPE_JUMP:
|
|
lcLength = buildPipeJump(length, width - 64 - length);
|
|
break;
|
|
case PLATFORM_JUMP:
|
|
lcLength = buildPlatformJump(length, width - 64 - length);
|
|
break;
|
|
case MAZE:
|
|
lcLength = buildMaze(length, width - 64 - length);
|
|
break;
|
|
case BLOCKS:
|
|
lcLength = buildMaze(length, width - 64 - length);
|
|
break;
|
|
case BOWLING_ALLEY :
|
|
lcLength = buildBowlingAlley(length, width - 64 - length);
|
|
break;
|
|
case CANNON_LINE :
|
|
lcLength = buildCannonLine(length, width - 64 - length);
|
|
break;
|
|
case COIN_DIVE :
|
|
lcLength = buildCoinDive(length, width - 64 - length);
|
|
break;
|
|
case LEMMING_TRAP :
|
|
lcLength = buildLemmingTrap(length, width - 64 - length);
|
|
break;
|
|
case POWER_UP :
|
|
lcLength = buildFreebie(length, width - 64 - length);
|
|
break;
|
|
case SINGLE_PIT :
|
|
lcLength = buildSinglePit(length, width - 64 - length);
|
|
break;
|
|
default:
|
|
lcLength = 0;
|
|
System.out
|
|
.println("Cannot build level segment for unrecognized LevelComponent type: "
|
|
+ type);
|
|
}
|
|
if (lcLength == 0) {
|
|
lcLength = buildStraight(length, lcomp.getEnd(), true);
|
|
}
|
|
length += lcLength;
|
|
}
|
|
}
|
|
}
|
|
|
|
System.out.println("Total length built: " + length);
|
|
|
|
// set the end piece
|
|
int floor = height - 1 - random.nextInt(4);
|
|
|
|
xExit = length + 8;
|
|
yExit = floor;
|
|
|
|
fillEndPiece(length, floor);
|
|
}
|
|
|
|
private void fillEndPiece(int length, int floor) {
|
|
// fills the end piece
|
|
for (int x = length; x < width; x++) {
|
|
for (int y = 0; y < height; y++) {
|
|
if (y >= floor) {
|
|
setBlock(x, y, GROUND);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (type == LevelInterface.TYPE_CASTLE
|
|
|| type == LevelInterface.TYPE_UNDERGROUND) {
|
|
int ceiling = 0;
|
|
int run = 0;
|
|
for (int x = 0; x < width; x++) {
|
|
if (run-- <= 0 && x > 4) {
|
|
ceiling = random.nextInt(4);
|
|
run = random.nextInt(4) + 4;
|
|
}
|
|
for (int y = 0; y < height; y++) {
|
|
if ((x > 4 && y <= ceiling) || x < 1) {
|
|
setBlock(x, y, GROUND);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fixWalls();
|
|
}
|
|
|
|
private void fixWalls() {
|
|
boolean[][] blockMap = new boolean[width + 1][height + 1];
|
|
|
|
for (int x = 0; x < width + 1; x++) {
|
|
for (int y = 0; y < height + 1; y++) {
|
|
int blocks = 0;
|
|
for (int xx = x - 1; xx < x + 1; xx++) {
|
|
for (int yy = y - 1; yy < y + 1; yy++) {
|
|
if (getBlockCapped(xx, yy) == GROUND) {
|
|
blocks++;
|
|
}
|
|
}
|
|
}
|
|
blockMap[x][y] = blocks == 4;
|
|
}
|
|
}
|
|
blockify(this, blockMap, width + 1, height + 1);
|
|
}
|
|
|
|
private void generateLevel(long seed, GamePlay playerMetrics) {
|
|
|
|
PlayerProfile profile = ProfileMatcher.getMatchingProfile(
|
|
playerMetrics, dataRecorder);
|
|
System.out.println("PlayerProfile: " + profile);
|
|
|
|
LevelArchetype archetype = ArchetypeMatcher.getMatchingArchetype(profile);
|
|
System.out.println("LevelArchetype: " + archetype);
|
|
|
|
System.out.println("Creating level grammar");
|
|
LevelGrammar grammar;
|
|
try {
|
|
String grammarFileName = "grammars/overland.grm";
|
|
grammar = LevelGrammarFactory.createGrammar(new File(
|
|
grammarFileName));
|
|
System.out.println("Read grammar from file: " + grammarFileName);
|
|
System.out.println("==== LEVEL GRAMMAR ====");
|
|
System.out.println(grammar);
|
|
System.out.println("=======================");
|
|
} catch (Exception ex) {
|
|
System.out
|
|
.println("Failed to parse grammar file due to exception: "
|
|
+ ex.getMessage());
|
|
System.out.println("Defaulting to basic overland grammar.");
|
|
grammar = LevelGrammarFactory.createGrammar();
|
|
}
|
|
System.out
|
|
.println("Tuning grammar for PlayerProfile & LevelArchetype using RETE");
|
|
GrammarTuner.tune(grammar, profile, archetype);
|
|
|
|
System.out.println("Creating level.");
|
|
|
|
initProbabilities(profile);
|
|
|
|
create(seed, profile, archetype, grammar);
|
|
}
|
|
|
|
private void initProbabilities(PlayerProfile profile) {
|
|
probability
|
|
.put(PlayerProfile.ChallengeRewardType.BLOCK,
|
|
new Double(
|
|
profile.getProbability(PlayerProfile.ChallengeRewardType.BLOCK)));
|
|
probability
|
|
.put(PlayerProfile.ChallengeRewardType.COIN,
|
|
new Double(
|
|
profile.getProbability(PlayerProfile.ChallengeRewardType.COIN)));
|
|
probability
|
|
.put(PlayerProfile.ChallengeRewardType.POWERUP,
|
|
new Double(
|
|
profile.getProbability(PlayerProfile.ChallengeRewardType.POWERUP)));
|
|
|
|
probability
|
|
.put(PlayerProfile.ChallengeRewardType.ENEMY,
|
|
new Double(
|
|
profile.getProbability(PlayerProfile.ChallengeRewardType.ENEMY)));
|
|
probability.put(PlayerProfile.ChallengeRewardType.GAP, new Double(
|
|
profile.getProbability(PlayerProfile.ChallengeRewardType.GAP)));
|
|
probability
|
|
.put(PlayerProfile.ChallengeRewardType.HARDER_ENEMY,
|
|
new Double(
|
|
profile.getProbability(PlayerProfile.ChallengeRewardType.HARDER_ENEMY)));
|
|
probability
|
|
.put(PlayerProfile.ChallengeRewardType.JUMP,
|
|
new Double(
|
|
profile.getProbability(PlayerProfile.ChallengeRewardType.JUMP)));
|
|
}
|
|
|
|
private boolean shouldAddChallenge(PlayerProfile.ChallengeRewardType crt) {
|
|
return random.nextDouble() <= probability.get(crt);
|
|
}
|
|
|
|
private PlayerProfile.ChallengeRewardType shouldAddReward() {
|
|
double guess = random.nextDouble();
|
|
|
|
if (guess < probability.get(PlayerProfile.ChallengeRewardType.COIN)) {
|
|
return PlayerProfile.ChallengeRewardType.COIN;
|
|
} else if (guess < probability
|
|
.get(PlayerProfile.ChallengeRewardType.COIN)
|
|
+ probability.get(PlayerProfile.ChallengeRewardType.BLOCK)) {
|
|
return PlayerProfile.ChallengeRewardType.BLOCK;
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
} |