=sample.buf.length)
+ {
+ buf[i] = 0;
+ alive = false;
+ }
+ else
+ {
+ buf[i]=sample.buf[(int)(pos)];
+ }
+ pos+=step;
+ }
+
+ return 1;
+ }
+
+ public void skip(int samplesToSkip, int readRate)
+ {
+ float step = sample.rate/readRate;
+ pos+=step*samplesToSkip;
+
+ if (pos>=sample.buf.length)
+ {
+ alive = false;
+ }
+ }
+
+ public boolean isLive()
+ {
+ return alive;
+ }
+}
diff --git a/src/dk/itu/mario/engine/sonar/sample/SonarSample.java b/src/dk/itu/mario/engine/sonar/sample/SonarSample.java
new file mode 100644
index 0000000..e921d8b
--- /dev/null
+++ b/src/dk/itu/mario/engine/sonar/sample/SonarSample.java
@@ -0,0 +1,13 @@
+package dk.itu.mario.engine.sonar.sample;
+
+public class SonarSample
+{
+ public final float[] buf;
+ public final float rate;
+
+ public SonarSample(float[] buf, float rate)
+ {
+ this.buf = buf;
+ this.rate = rate;
+ }
+}
\ No newline at end of file
diff --git a/src/dk/itu/mario/engine/sprites/.DS_Store b/src/dk/itu/mario/engine/sprites/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/src/dk/itu/mario/engine/sprites/.DS_Store differ
diff --git a/src/dk/itu/mario/engine/sprites/BulletBill.java b/src/dk/itu/mario/engine/sprites/BulletBill.java
new file mode 100644
index 0000000..dcf23b7
--- /dev/null
+++ b/src/dk/itu/mario/engine/sprites/BulletBill.java
@@ -0,0 +1,152 @@
+package dk.itu.mario.engine.sprites;
+
+
+import dk.itu.mario.engine.Art;
+import dk.itu.mario.scene.LevelScene;
+
+
+public class BulletBill extends Sprite
+{
+ private int width = 4;
+ int height = 24;
+
+ private LevelScene world;
+ public int facing;
+
+ public boolean avoidCliffs = false;
+ public int anim;
+
+ public boolean dead = false;
+ private int deadTime = 0;
+
+
+ public BulletBill(LevelScene world, float x, float y, int dir)
+ {
+ sheet = Art.enemies;
+
+ this.x = x;
+ this.y = y;
+ this.world = world;
+ xPicO = 8;
+ yPicO = 31;
+
+ height = 12;
+ facing = 0;
+ wPic = 16;
+ yPic = 5;
+
+ xPic = 0;
+ ya = -5;
+ this.facing = dir;
+ }
+
+ public void collideCheck()
+ {
+ if (dead) return;
+
+ float xMarioD = world.mario.x - x;
+ float yMarioD = world.mario.y - y;
+ float w = 16;
+ if (xMarioD > -16 && xMarioD < 16)
+ {
+ if (yMarioD > -height && yMarioD < world.mario.height)
+ {
+ if (world.mario.ya > 0 && yMarioD <= 0 && (!world.mario.onGround || !world.mario.wasOnGround))
+ {
+ world.mario.stomp(this);
+ dead = true;
+
+ xa = 0;
+ ya = 1;
+ deadTime = 100;
+ }
+ else
+ {
+ world.mario.getHurt(this);
+ }
+ }
+ }
+ }
+
+ public void move()
+ {
+ if (deadTime > 0)
+ {
+ deadTime--;
+
+ if (deadTime == 0)
+ {
+ deadTime = 1;
+ for (int i = 0; i < 8; i++)
+ {
+ world.addSprite(new Sparkle((int) (x + Math.random() * 16 - 8) + 4, (int) (y - Math.random() * 8) + 4, (float) (Math.random() * 2 - 1), (float) Math.random() * -1, 0, 1, 5));
+ }
+ spriteContext.removeSprite(this);
+ }
+
+ x += xa;
+ y += ya;
+ ya *= 0.95;
+ ya += 1;
+
+ return;
+ }
+
+ float sideWaysSpeed = 4f;
+
+ xa = facing * sideWaysSpeed;
+ xFlipPic = facing == -1;
+ move(xa, 0);
+ }
+
+ private boolean move(float xa, float ya)
+ {
+ x += xa;
+ return true;
+ }
+
+ public boolean fireballCollideCheck(Fireball fireball)
+ {
+ if (deadTime != 0) return false;
+
+ float xD = fireball.x - x;
+ float yD = fireball.y - y;
+
+ if (xD > -16 && xD < 16)
+ {
+ if (yD > -height && yD < fireball.height)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean shellCollideCheck(Shell shell)
+ {
+ if (deadTime != 0) return false;
+
+ float xD = shell.x - x;
+ float yD = shell.y - y;
+
+ if (xD > -16 && xD < 16)
+ {
+ if (yD > -height && yD < shell.height)
+ {
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_KICK], this, 1, 1, 1);
+
+ dead = true;
+
+ xa = 0;
+ ya = 1;
+ deadTime = 100;
+
+ if(world.recorder != null)
+ world.recorder.shellKillRecord(this);
+
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/dk/itu/mario/engine/sprites/CoinAnim.java b/src/dk/itu/mario/engine/sprites/CoinAnim.java
new file mode 100644
index 0000000..65c2d73
--- /dev/null
+++ b/src/dk/itu/mario/engine/sprites/CoinAnim.java
@@ -0,0 +1,41 @@
+package dk.itu.mario.engine.sprites;
+
+import dk.itu.mario.engine.Art;
+import dk.itu.mario.level.Level;
+
+
+
+public class CoinAnim extends Sprite
+{
+ private int life = 10;
+
+ public CoinAnim(int xTile, int yTile)
+ {
+ sheet = Art.level;
+ wPic = hPic = 16;
+
+ x = xTile * 16;
+ y = yTile * 16 - 16;
+ xa = 0;
+ ya = -6f;
+ xPic = 0;
+ yPic = 2;
+ }
+
+ public void move()
+ {
+ if (life-- < 0)
+ {
+ Sprite.spriteContext.removeSprite(this);
+ for (int xx = 0; xx < 2; xx++)
+ for (int yy = 0; yy < 2; yy++)
+ Sprite.spriteContext.addSprite(new Sparkle((int)x + xx * 8 + (int) (Math.random() * 8), (int)y + yy * 8 + (int) (Math.random() * 8), 0, 0, 0, 2, 5));
+ }
+
+ xPic = life & 3;
+
+ x += xa;
+ y += ya;
+ ya += 1;
+ }
+}
diff --git a/src/dk/itu/mario/engine/sprites/Enemy.java b/src/dk/itu/mario/engine/sprites/Enemy.java
new file mode 100644
index 0000000..21c0883
--- /dev/null
+++ b/src/dk/itu/mario/engine/sprites/Enemy.java
@@ -0,0 +1,443 @@
+package dk.itu.mario.engine.sprites;
+
+import java.awt.Graphics;
+
+
+import dk.itu.mario.engine.Art;
+import dk.itu.mario.level.Level;
+import dk.itu.mario.scene.LevelScene;
+
+
+
+public class Enemy extends Sprite
+{
+ public static final int ENEMY_RED_KOOPA = 0;
+ public static final int ENEMY_GREEN_KOOPA = 1;
+ public static final int ENEMY_GOOMBA = 2;
+ public static final int ENEMY_SPIKY = 3;
+ public static final int ENEMY_FLOWER = 4;
+
+ private static float GROUND_INERTIA = 0.89f;
+ private static float AIR_INERTIA = 0.89f;
+
+ private float runTime;
+ private boolean onGround = false;
+ private boolean mayJump = false;
+ private int jumpTime = 0;
+ private float xJumpSpeed;
+ private float yJumpSpeed;
+
+ int width = 4;
+ int height = 24;
+
+ private LevelScene world;
+ public int facing;
+ public int deadTime = 0;
+ public boolean flyDeath = false;
+
+ public boolean avoidCliffs = true;
+ public int type;
+
+ public boolean winged = true;
+ private int wingTime = 0;
+
+ public boolean noFireballDeath;
+
+ public Enemy(LevelScene world, int x, int y, int dir, int type, boolean winged)
+ {
+ this.type = type;
+ sheet = Art.enemies;
+ this.winged = winged;
+
+
+ this.world = world;
+ this.x = x;
+ this.y = y;
+
+ xPicO = 8;
+ yPicO = 31;
+
+ avoidCliffs = type == Enemy.ENEMY_RED_KOOPA;
+
+ noFireballDeath = type == Enemy.ENEMY_SPIKY;
+
+ yPic = type;
+ if (yPic > 1) height = 12;
+ facing = dir;
+ if (facing == 0) facing = 1;
+ this.wPic = 16;
+ }
+
+ public void collideCheck()
+ {
+ if (deadTime != 0)
+ {
+ return;
+ }
+
+ float xMarioD = world.mario.x - x;
+ float yMarioD = world.mario.y - y;
+ float w = 16;
+
+ //if the distance to mario is within the sprite (in x)
+ if (xMarioD > -width*2-4 && xMarioD < width*2+4)
+ {
+ //if the distance to mario is within the sprite (in y)
+ if (yMarioD > -height && yMarioD < world.mario.height)
+ {
+ if (type != Enemy.ENEMY_SPIKY && world.mario.ya > 0 && yMarioD <= 0 && (!world.mario.onGround || !world.mario.wasOnGround))
+ {
+ world.mario.stomp(this);
+
+ if (winged)
+ {
+ winged = false;
+ ya = 0;
+ }
+ else
+ {
+ this.yPicO = 31 - (32 - 8);
+ hPic = 8;
+ if (spriteTemplate != null) spriteTemplate.isDead = true;
+ deadTime = 10;
+ winged = false;
+
+ if (type == Enemy.ENEMY_RED_KOOPA)
+ {
+ spriteContext.addSprite(new Shell(world, x, y, 0));
+ }
+ else if (type == Enemy.ENEMY_GREEN_KOOPA)
+ {
+ spriteContext.addSprite(new Shell(world, x, y, 1));
+ }
+ }
+ }
+ else
+ {
+ world.mario.getHurt(this);
+ }
+ }
+ }
+ }
+
+ public void move()
+ {
+ //TODO: some kind of suicide error when one enemy is created
+ if (y > world.level.getHeight() * 16 + 16 && deadTime <= 0)
+ {
+ if(world.recorder != null)
+ world.recorder.killSuicideRecord(this);
+
+ deadTime = 1;
+ }
+
+ wingTime++;
+ if (deadTime > 0)
+ {
+ deadTime--;
+
+ if (deadTime == 0)
+ {
+ deadTime = 1;
+ for (int i = 0; i < 8; i++)
+ {
+ world.addSprite(new Sparkle((int) (x + Math.random() * 16 - 8) + 4, (int) (y - Math.random() * 8) + 4, (float) (Math.random() * 2 - 1), (float) Math.random() * -1, 0, 1, 5));
+ }
+ spriteContext.removeSprite(this);
+ }
+
+ if (flyDeath)
+ {
+ x += xa;
+ y += ya;
+ ya *= 0.95;
+ ya += 1;
+ }
+ return;
+ }
+
+
+ float sideWaysSpeed = 1.75f;
+ // float sideWaysSpeed = onGround ? 2.5f : 1.2f;
+
+ if (xa > 2)
+ {
+ facing = 1;
+ }
+ if (xa < -2)
+ {
+ facing = -1;
+ }
+
+ xa = facing * sideWaysSpeed;
+
+ mayJump = (onGround);
+
+ xFlipPic = facing == -1;
+
+ runTime += (Math.abs(xa)) + 5;
+
+ int runFrame = ((int) (runTime / 20)) % 2;
+
+ if (!onGround)
+ {
+ runFrame = 1;
+ }
+
+
+ if (!move(xa, 0)) facing = -facing;
+ onGround = false;
+ move(0, ya);
+
+ ya *= winged ? 0.95f : 0.85f;
+ if (onGround)
+ {
+ xa *= GROUND_INERTIA;
+ }
+ else
+ {
+ xa *= AIR_INERTIA;
+ }
+
+ if (!onGround)
+ {
+ if (winged)
+ {
+ ya += 0.6f;
+ }
+ else
+ {
+ ya += 2;
+ }
+ }
+ else if (winged)
+ {
+ ya = -10;
+ }
+
+ if (winged) runFrame = wingTime / 4 % 2;
+
+ xPic = runFrame;
+ }
+
+ private boolean move(float xa, float ya)
+ {
+ while (xa > 8)
+ {
+ if (!move(8, 0)) return false;
+ xa -= 8;
+ }
+ while (xa < -8)
+ {
+ if (!move(-8, 0)) return false;
+ xa += 8;
+ }
+ while (ya > 8)
+ {
+ if (!move(0, 8)) return false;
+ ya -= 8;
+ }
+ while (ya < -8)
+ {
+ if (!move(0, -8)) return false;
+ ya += 8;
+ }
+
+ boolean collide = false;
+ if (ya > 0)
+ {
+ if (isBlocking(x + xa - width, y + ya, xa, 0)) collide = true;
+ else if (isBlocking(x + xa + width, y + ya, xa, 0)) collide = true;
+ else if (isBlocking(x + xa - width, y + ya + 1, xa, ya)) collide = true;
+ else if (isBlocking(x + xa + width, y + ya + 1, xa, ya)) collide = true;
+ }
+ if (ya < 0)
+ {
+ if (isBlocking(x + xa, y + ya - height, xa, ya)) collide = true;
+ else if (collide || isBlocking(x + xa - width, y + ya - height, xa, ya)) collide = true;
+ else if (collide || isBlocking(x + xa + width, y + ya - height, xa, ya)) collide = true;
+ }
+ if (xa > 0)
+ {
+ if (isBlocking(x + xa + width, y + ya - height, xa, ya)) collide = true;
+ if (isBlocking(x + xa + width, y + ya - height / 2, xa, ya)) collide = true;
+ if (isBlocking(x + xa + width, y + ya, xa, ya)) collide = true;
+
+ if (avoidCliffs && onGround && !world.level.isBlocking((int) ((x + xa + width) / 16), (int) ((y) / 16 + 1), xa, 1)) collide = true;
+ }
+ if (xa < 0)
+ {
+ if (isBlocking(x + xa - width, y + ya - height, xa, ya)) collide = true;
+ if (isBlocking(x + xa - width, y + ya - height / 2, xa, ya)) collide = true;
+ if (isBlocking(x + xa - width, y + ya, xa, ya)) collide = true;
+
+ if (avoidCliffs && onGround && !world.level.isBlocking((int) ((x + xa - width) / 16), (int) ((y) / 16 + 1), xa, 1)) collide = true;
+ }
+
+ if (collide)
+ {
+ if (xa < 0)
+ {
+ x = (int) ((x - width) / 16) * 16 + width;
+ this.xa = 0;
+ }
+ if (xa > 0)
+ {
+ x = (int) ((x + width) / 16 + 1) * 16 - width - 1;
+ this.xa = 0;
+ }
+ if (ya < 0)
+ {
+ y = (int) ((y - height) / 16) * 16 + height;
+ jumpTime = 0;
+ this.ya = 0;
+ }
+ if (ya > 0)
+ {
+ y = (int) (y / 16 + 1) * 16 - 1;
+ onGround = true;
+ }
+ return false;
+ }
+ else
+ {
+ x += xa;
+ y += ya;
+ return true;
+ }
+ }
+
+ private boolean isBlocking(float _x, float _y, float xa, float ya)
+ {
+ int x = (int) (_x / 16);
+ int y = (int) (_y / 16);
+ if (x == (int) (this.x / 16) && y == (int) (this.y / 16)) return false;
+
+ boolean blocking = world.level.isBlocking(x, y, xa, ya);
+
+ byte block = world.level.getBlock(x, y);
+
+ return blocking;
+ }
+
+ public boolean shellCollideCheck(Shell shell)
+ {
+ if (deadTime != 0) return false;
+
+ float xD = shell.x - x;
+ float yD = shell.y - y;
+
+ if (xD > -16 && xD < 16)
+ {
+ if (yD > -height && yD < shell.height)
+ {
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_KICK], this, 1, 1, 1);
+
+ xa = shell.facing * 2;
+ ya = -5;
+ flyDeath = true;
+ if (spriteTemplate != null) spriteTemplate.isDead = true;
+ deadTime = 100;
+ winged = false;
+ hPic = -hPic;
+ yPicO = -yPicO + 16;
+
+ if(world.recorder != null)
+ world.recorder.shellKillRecord(this);
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean fireballCollideCheck(Fireball fireball)
+ {
+ if (deadTime != 0) return false;
+
+ float xD = fireball.x - x;
+ float yD = fireball.y - y;
+
+ if (xD > -16 && xD < 16)
+ {
+ if (yD > -height && yD < fireball.height)
+ {
+ if (noFireballDeath) return true;
+
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_KICK], this, 1, 1, 1);
+
+ xa = fireball.facing * 2;
+ ya = -5;
+ flyDeath = true;
+ if (spriteTemplate != null) spriteTemplate.isDead = true;
+ deadTime = 100;
+ winged = false;
+ hPic = -hPic;
+ yPicO = -yPicO + 16;
+
+
+ if(world.recorder != null)
+ world.recorder.fireKillRecord(this);
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void bumpCheck(int xTile, int yTile)
+ {
+ if (deadTime != 0) return;
+
+ if (x + width > xTile * 16 && x - width < xTile * 16 + 16 && yTile == (int) ((y - 1) / 16))
+ {
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_KICK], this, 1, 1, 1);
+
+ xa = -world.mario.facing * 2;
+ ya = -5;
+ flyDeath = true;
+ if (spriteTemplate != null) spriteTemplate.isDead = true;
+ deadTime = 100;
+ winged = false;
+ hPic = -hPic;
+ yPicO = -yPicO + 16;
+
+ }
+ }
+
+ public void render(Graphics og, float alpha)
+ {
+ if (winged)
+ {
+ int xPixel = (int) (xOld + (x - xOld) * alpha) - xPicO;
+ int yPixel = (int) (yOld + (y - yOld) * alpha) - yPicO;
+
+ if (type == Enemy.ENEMY_GREEN_KOOPA || type == Enemy.ENEMY_RED_KOOPA)
+ {
+ }
+ else
+ {
+ xFlipPic = !xFlipPic;
+ og.drawImage(sheet[wingTime / 4 % 2][4], xPixel + (xFlipPic ? wPic : 0) + (xFlipPic ? 10 : -10), yPixel + (yFlipPic ? hPic : 0) - 8, xFlipPic ? -wPic : wPic, yFlipPic ? -hPic : hPic, null);
+ xFlipPic = !xFlipPic;
+ }
+ }
+
+ super.render(og, alpha);
+
+ if (winged)
+ {
+ int xPixel = (int) (xOld + (x - xOld) * alpha) - xPicO;
+ int yPixel = (int) (yOld + (y - yOld) * alpha) - yPicO;
+
+ if (type == Enemy.ENEMY_GREEN_KOOPA || type == Enemy.ENEMY_RED_KOOPA)
+ {
+ og.drawImage(sheet[wingTime / 4 % 2][4], xPixel + (xFlipPic ? wPic : 0) + (xFlipPic ? 10 : -10), yPixel + (yFlipPic ? hPic : 0) - 10, xFlipPic ? -wPic : wPic, yFlipPic ? -hPic : hPic, null);
+ }
+ else
+ {
+ og.drawImage(sheet[wingTime / 4 % 2][4], xPixel + (xFlipPic ? wPic : 0) + (xFlipPic ? 10 : -10), yPixel + (yFlipPic ? hPic : 0) - 8, xFlipPic ? -wPic : wPic, yFlipPic ? -hPic : hPic, null);
+ }
+ }
+ }
+}
diff --git a/src/dk/itu/mario/engine/sprites/FireFlower.java b/src/dk/itu/mario/engine/sprites/FireFlower.java
new file mode 100644
index 0000000..55e9c16
--- /dev/null
+++ b/src/dk/itu/mario/engine/sprites/FireFlower.java
@@ -0,0 +1,63 @@
+package dk.itu.mario.engine.sprites;
+
+
+import dk.itu.mario.engine.Art;
+import dk.itu.mario.scene.LevelScene;
+
+
+
+public class FireFlower extends Sprite
+{
+ private int width = 4;
+ int height = 24;
+
+ private LevelScene world;
+ public int facing;
+
+ public boolean avoidCliffs = false;
+ private int life;
+
+ public FireFlower(LevelScene world, int x, int y)
+ {
+ sheet = Art.items;
+
+ this.x = x;
+ this.y = y;
+ this.world = world;
+ xPicO = 8;
+ yPicO = 15;
+
+ xPic = 1;
+ yPic = 0;
+ height = 12;
+ facing = 1;
+ wPic = hPic = 16;
+ life = 0;
+ }
+
+ public void collideCheck()
+ {
+ float xMarioD = world.mario.x - x;
+ float yMarioD = world.mario.y - y;
+ float w = 16;
+ if (xMarioD > -16 && xMarioD < 16)
+ {
+ if (yMarioD > -height && yMarioD < world.mario.height)
+ {
+ world.mario.getFlower();
+ spriteContext.removeSprite(this);
+ }
+ }
+ }
+
+ public void move()
+ {
+ if (life<9)
+ {
+ layer = 0;
+ y--;
+ life++;
+ return;
+ }
+ }
+}
diff --git a/src/dk/itu/mario/engine/sprites/Fireball.java b/src/dk/itu/mario/engine/sprites/Fireball.java
new file mode 100644
index 0000000..889f7a1
--- /dev/null
+++ b/src/dk/itu/mario/engine/sprites/Fireball.java
@@ -0,0 +1,220 @@
+package dk.itu.mario.engine.sprites;
+
+
+import dk.itu.mario.engine.Art;
+import dk.itu.mario.scene.LevelScene;
+
+
+
+public class Fireball extends Sprite
+{
+ private static float GROUND_INERTIA = 0.89f;
+ private static float AIR_INERTIA = 0.89f;
+
+ private float runTime;
+ private boolean onGround = false;
+
+ private int width = 4;
+ int height = 24;
+
+ private LevelScene world;
+ public int facing;
+
+ public boolean avoidCliffs = false;
+ public int anim;
+
+ public boolean dead = false;
+ private int deadTime = 0;
+
+ public Fireball(LevelScene world, float x, float y, int facing)
+ {
+ sheet = Art.particles;
+
+ this.x = x;
+ this.y = y;
+ this.world = world;
+ xPicO = 4;
+ yPicO = 4;
+
+ yPic = 3;
+ height = 8;
+ this.facing = facing;
+ wPic = 8;
+ hPic = 8;
+
+ xPic = 4;
+ ya = 4;
+ }
+
+ public void move()
+ {
+ if (deadTime > 0)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ world.addSprite(new Sparkle((int) (x + Math.random() * 8 - 4)+4, (int) (y + Math.random() * 8-4)+2, (float) Math.random() * 2 - 1-facing, (float) Math.random() *2 -1, 0, 1, 5));
+ }
+ spriteContext.removeSprite(this);
+
+ return;
+ }
+
+ if (facing != 0) anim++;
+
+ float sideWaysSpeed = 8f;
+ // float sideWaysSpeed = onGround ? 2.5f : 1.2f;
+
+ if (xa > 2)
+ {
+ facing = 1;
+ }
+ if (xa < -2)
+ {
+ facing = -1;
+ }
+
+ xa = facing * sideWaysSpeed;
+
+ world.checkFireballCollide(this);
+
+ xFlipPic = facing == -1;
+
+ runTime += (Math.abs(xa)) + 5;
+
+ xPic = (anim) % 4;
+
+
+
+ if (!move(xa, 0))
+ {
+ die();
+ }
+
+ onGround = false;
+ move(0, ya);
+ if (onGround) ya = -10;
+
+ ya *= 0.95f;
+ if (onGround)
+ {
+ xa *= GROUND_INERTIA;
+ }
+ else
+ {
+ xa *= AIR_INERTIA;
+ }
+
+ if (!onGround)
+ {
+ ya += 1.5;
+ }
+ }
+
+ private boolean move(float xa, float ya)
+ {
+ while (xa > 8)
+ {
+ if (!move(8, 0)) return false;
+ xa -= 8;
+ }
+ while (xa < -8)
+ {
+ if (!move(-8, 0)) return false;
+ xa += 8;
+ }
+ while (ya > 8)
+ {
+ if (!move(0, 8)) return false;
+ ya -= 8;
+ }
+ while (ya < -8)
+ {
+ if (!move(0, -8)) return false;
+ ya += 8;
+ }
+
+ boolean collide = false;
+ if (ya > 0)
+ {
+ if (isBlocking(x + xa - width, y + ya, xa, 0)) collide = true;
+ else if (isBlocking(x + xa + width, y + ya, xa, 0)) collide = true;
+ else if (isBlocking(x + xa - width, y + ya + 1, xa, ya)) collide = true;
+ else if (isBlocking(x + xa + width, y + ya + 1, xa, ya)) collide = true;
+ }
+ if (ya < 0)
+ {
+ if (isBlocking(x + xa, y + ya - height, xa, ya)) collide = true;
+ else if (collide || isBlocking(x + xa - width, y + ya - height, xa, ya)) collide = true;
+ else if (collide || isBlocking(x + xa + width, y + ya - height, xa, ya)) collide = true;
+ }
+ if (xa > 0)
+ {
+ if (isBlocking(x + xa + width, y + ya - height, xa, ya)) collide = true;
+ if (isBlocking(x + xa + width, y + ya - height / 2, xa, ya)) collide = true;
+ if (isBlocking(x + xa + width, y + ya, xa, ya)) collide = true;
+
+ if (avoidCliffs && onGround && !world.level.isBlocking((int) ((x + xa + width) / 16), (int) ((y) / 16 + 1), xa, 1)) collide = true;
+ }
+ if (xa < 0)
+ {
+ if (isBlocking(x + xa - width, y + ya - height, xa, ya)) collide = true;
+ if (isBlocking(x + xa - width, y + ya - height / 2, xa, ya)) collide = true;
+ if (isBlocking(x + xa - width, y + ya, xa, ya)) collide = true;
+
+ if (avoidCliffs && onGround && !world.level.isBlocking((int) ((x + xa - width) / 16), (int) ((y) / 16 + 1), xa, 1)) collide = true;
+ }
+
+ if (collide)
+ {
+ if (xa < 0)
+ {
+ x = (int) ((x - width) / 16) * 16 + width;
+ this.xa = 0;
+ }
+ if (xa > 0)
+ {
+ x = (int) ((x + width) / 16 + 1) * 16 - width - 1;
+ this.xa = 0;
+ }
+ if (ya < 0)
+ {
+ y = (int) ((y - height) / 16) * 16 + height;
+ this.ya = 0;
+ }
+ if (ya > 0)
+ {
+ y = (int) (y / 16 + 1) * 16 - 1;
+ onGround = true;
+ }
+ return false;
+ }
+ else
+ {
+ x += xa;
+ y += ya;
+ return true;
+ }
+ }
+
+ private boolean isBlocking(float _x, float _y, float xa, float ya)
+ {
+ int x = (int) (_x / 16);
+ int y = (int) (_y / 16);
+ if (x == (int) (this.x / 16) && y == (int) (this.y / 16)) return false;
+
+ boolean blocking = world.level.isBlocking(x, y, xa, ya);
+
+ byte block = world.level.getBlock(x, y);
+
+ return blocking;
+ }
+
+ public void die()
+ {
+ dead = true;
+
+ xa = -facing * 2;
+ ya = -5;
+ deadTime = 100;
+ }
+}
diff --git a/src/dk/itu/mario/engine/sprites/FlowerEnemy.java b/src/dk/itu/mario/engine/sprites/FlowerEnemy.java
new file mode 100644
index 0000000..4ec3a08
--- /dev/null
+++ b/src/dk/itu/mario/engine/sprites/FlowerEnemy.java
@@ -0,0 +1,103 @@
+package dk.itu.mario.engine.sprites;
+
+import dk.itu.mario.level.Level;
+import dk.itu.mario.scene.LevelScene;
+import dk.itu.mario.engine.sprites.*;
+
+public class FlowerEnemy extends Enemy
+{
+ private int tick;
+ private int yStart;
+ private int jumpTime = 0;
+ private LevelScene world;
+
+ public FlowerEnemy(LevelScene world, int x, int y)
+ {
+ super(world, x, y, 1, ENEMY_SPIKY, false);
+
+ noFireballDeath = false;
+ this.world = world;
+ this.xPic = 0;
+ this.yPic = 6;
+ this.yPicO = 24;
+ this.height = 12;
+ this.width = 2;
+
+ yStart = y;
+ ya = -8;
+
+ this.y-=1;
+
+ this.layer = 0;
+ for (int i=0; i<4; i++)
+ {
+ move();
+ }
+ }
+
+ public void move()
+ {
+ if (deadTime > 0)
+ {
+ deadTime--;
+
+ if (deadTime == 0)
+ {
+ deadTime = 1;
+ for (int i = 0; i < 8; i++)
+ {
+ world.addSprite(new Sparkle((int) (x + Math.random() * 16 - 8) + 4, (int) (y - Math.random() * 8) + 4, (float) (Math.random() * 2 - 1), (float) Math.random() * -1, 0, 1, 5));
+ }
+ spriteContext.removeSprite(this);
+ }
+
+ x += xa;
+ y += ya;
+ ya *= 0.95;
+ ya += 1;
+
+ return;
+ }
+
+ tick++;
+
+ if (y>=yStart)
+ {
+ y = yStart;
+
+ int xd = (int)(Math.abs(world.mario.x-x));
+ jumpTime++;
+ if (jumpTime>40 && xd>24)
+ {
+ ya = -8;
+ }
+ else
+ {
+ ya = 0;
+ }
+ }
+ else
+ {
+ jumpTime = 0;
+ }
+
+ y+=ya;
+ ya*=0.9;
+ ya+=0.1f;
+
+ xPic = ((tick/2)&1)*2+((tick/6)&1);
+ }
+
+/* public void render(Graphics og, float alpha)
+ {
+ if (!visible) return;
+
+ int xPixel = (int)(xOld+(x-xOld)*alpha)-xPicO;
+ int yPixel = (int)(yOld+(y-yOld)*alpha)-yPicO;
+
+ int a = ((tick/3)&1)*2;
+// a += ((tick/8)&1);
+ og.drawImage(sheet[a*2+0][6], xPixel-8, yPixel+8, 16, 32, null);
+ og.drawImage(sheet[a*2+1][6], xPixel+8, yPixel+8, 16, 32, null);
+ }*/
+}
diff --git a/src/dk/itu/mario/engine/sprites/Mario.java b/src/dk/itu/mario/engine/sprites/Mario.java
new file mode 100644
index 0000000..fbce871
--- /dev/null
+++ b/src/dk/itu/mario/engine/sprites/Mario.java
@@ -0,0 +1,918 @@
+package dk.itu.mario.engine.sprites;
+
+
+
+
+
+import dk.itu.mario.engine.Art;
+import dk.itu.mario.engine.DataRecorder;
+import dk.itu.mario.engine.sonar.FixedSoundSource;
+import dk.itu.mario.level.Level;
+import dk.itu.mario.scene.LevelScene;
+import dk.itu.mario.scene.Scene;
+
+
+
+
+public class Mario extends Sprite
+{
+ public static boolean large = false;
+ public static boolean fire = false;
+ public static int coins = 0;
+ public static int lives = 3;
+ public static String levelString = "none";
+
+ public static void resetStatic()
+ {
+ large = false;
+ fire = false;
+ coins = 0;
+ lives = 3;
+ levelString = "none";
+ }
+
+ public static final int KEY_LEFT = 0;
+ public static final int KEY_RIGHT = 1;
+ public static final int KEY_DOWN = 2;
+ public static final int KEY_UP = 3;
+ public static final int KEY_JUMP = 4;
+ public static final int KEY_SPEED = 5;
+ public static final int KEY_ENTER = 6;
+
+ private static float GROUND_INERTIA = 0.89f;
+ private static float AIR_INERTIA = 0.89f;
+
+ public boolean[] keys;
+ private float runTime;
+ boolean wasOnGround = false;
+ boolean onGround = false;
+ private boolean mayJump = false;
+ public boolean ducking = false;
+ public boolean running = false;
+ public int direction = 0;
+ private boolean sliding = false;
+ private int jumpTime = 0;
+ private float xJumpSpeed;
+ private float yJumpSpeed;
+ private boolean canShoot = false;
+
+ int width = 4;
+ int height = 24;
+
+ private LevelScene world;
+ public int facing;
+ private int powerUpTime = 0;
+
+ public int xDeathPos, yDeathPos;
+
+ public int deathTime = 0;
+ public int winTime = 0;
+ private int invulnerableTime = 0;
+
+ public Sprite carried = null;
+ private static Mario instance;
+
+ public Mario(LevelScene world)
+ {
+ Mario.instance = this;
+ this.world = world;
+ keys = Scene.keys;
+ x = 32;
+ y = 0;
+
+ facing = 1;
+
+ //TODO: REMOVE THESE TEST VARIABLES
+// Mario.large = true;
+// Mario.fire = true;
+
+ setLarge(Mario.large, Mario.fire);
+ }
+
+ private boolean lastLarge;
+ private boolean lastFire;
+ private boolean newLarge;
+ private boolean newFire;
+
+ private void blink(boolean on)
+ {
+ Mario.large = on?newLarge:lastLarge;
+ Mario.fire = on?newFire:lastFire;
+
+ if (large)
+ {
+ sheet = Art.mario;
+ if (fire)
+ sheet = Art.fireMario;
+
+ xPicO = 16;
+ yPicO = 31;
+ wPic = hPic = 32;
+ }
+ else
+ {
+ sheet = Art.smallMario;
+
+ xPicO = 8;
+ yPicO = 15;
+ wPic = hPic = 16;
+ }
+
+ calcPic();
+ }
+
+ void setLarge(boolean large, boolean fire)
+ {
+ if (fire) large = true;
+ if (!large) fire = false;
+
+ lastLarge = Mario.large;
+ lastFire = Mario.fire;
+
+ Mario.large = large;
+ Mario.fire = fire;
+
+ newLarge = Mario.large;
+ newFire = Mario.fire;
+
+ blink(true);
+ }
+
+ public void move()
+ {
+ if(deathTime == 0 && winTime == 0){
+ if (keys[KEY_DOWN] && large && ducking)
+ {
+ if(world.recorder != null && world.recorder.recording)
+ world.recorder.startDuckRecord();
+ }
+ else
+ {
+ if(world.recorder != null && world.recorder.recording && !ducking)
+ world.recorder.endDuckRecord();
+ }
+ }
+
+ if(deathTime == 0 && winTime == 0){
+ if(keys[KEY_SPEED]){
+ if(world.recorder!= null && world.recorder.recording){
+ world.recorder.startRunningRecord();
+ }
+
+ running = true;
+ }
+ else{
+ if(world.recorder!= null && world.recorder.recording){
+ world.recorder.endRunningRecord();
+ }
+
+ running = false;
+ }
+ }
+
+ if (winTime > 0)
+ {
+
+ winTime++;
+
+ xa = 0;
+ ya = 0;
+ return;
+ }
+
+ if (deathTime > 0)
+ {
+ deathTime++;
+ if (deathTime < 11)
+ {
+ xa = 0;
+ ya = 0;
+ }
+ else if (deathTime == 11)
+ {
+ ya = -15;
+ }
+ else
+ {
+ ya += 2;
+ }
+ x += xa;
+ y += ya;
+ return;
+ }
+
+ if (powerUpTime != 0)
+ {
+ if (powerUpTime > 0)
+ {
+ powerUpTime--;
+ blink(((powerUpTime / 3) & 1) == 0);
+ }
+ else
+ {
+ powerUpTime++;
+ blink(((-powerUpTime / 3) & 1) == 0);
+ }
+
+ if (powerUpTime == 0) world.paused = false;
+
+ calcPic();
+ return;
+ }
+
+ if (invulnerableTime > 0) invulnerableTime--;
+ visible = ((invulnerableTime / 2) & 1) == 0;
+
+ wasOnGround = onGround;
+ float sideWaysSpeed = keys[KEY_SPEED] ? 1.2f : 0.6f;
+ // float sideWaysSpeed = onGround ? 2.5f : 1.2f;
+
+ if (onGround)
+ {
+ if (keys[KEY_DOWN] && large)
+ {
+ if(world.recorder != null)
+ world.recorder.startDuckRecord();
+
+ ducking = true;
+ }
+ else
+ {
+ if(world.recorder != null)
+ world.recorder.endDuckRecord();
+
+ ducking = false;
+ }
+
+ if(world.recorder != null)
+ world.recorder.recordJumpLand();
+ }
+
+ if (xa > 2)
+ {
+ facing = 1;
+ }
+ if (xa < -2)
+ {
+ facing = -1;
+ }
+
+ if (keys[KEY_JUMP] || (jumpTime < 0 && !onGround && !sliding))
+ {
+ if (jumpTime < 0)
+ {
+ xa = xJumpSpeed;
+ ya = -jumpTime * yJumpSpeed;
+ jumpTime++;
+ }
+ else if (onGround && mayJump)
+ {
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_JUMP], this, 1, 1, 1);
+ xJumpSpeed = 0;
+ yJumpSpeed = -1.9f;
+ jumpTime = 7;
+ ya = jumpTime * yJumpSpeed;
+ onGround = false;
+ sliding = false;
+
+ if(world.recorder != null){
+ world.recorder.isInAir = true;
+ world.recorder.recordJump();
+ }
+ }
+ else if (sliding && mayJump)
+ {
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_JUMP], this, 1, 1, 1);
+ xJumpSpeed = -facing * 6.0f;
+ yJumpSpeed = -2.0f;
+ jumpTime = -6;
+ xa = xJumpSpeed;
+ ya = -jumpTime * yJumpSpeed;
+ onGround = false;
+ sliding = false;
+ facing = -facing;
+ }
+ else if (jumpTime > 0)
+ {
+ xa += xJumpSpeed;
+ ya = jumpTime * yJumpSpeed;
+ jumpTime--;
+ }
+ }
+ else
+ {
+ jumpTime = 0;
+ }
+
+ if(world.mario.xa > 0){
+
+ if(direction != 1){
+ direction = 1;
+ if(world.recorder != null){
+// world.recorder.switchRecord();
+ world.recorder.startRightMoveRecord();
+ }
+ }
+
+ }
+ else if(world.mario.xa < 0){
+ if(direction != -1){
+ direction = -1;
+ if(world.recorder != null){
+// world.recorder.switchRecord();
+ world.recorder.startLeftMoveRecord();
+ }
+ }
+
+ }
+ else{
+ //was moving right
+ if(direction == 1 && world.recorder!= null){
+ world.recorder.endRightMoveRecord();
+ }
+ //was moving left
+ else if(direction == -1 && world.recorder!= null){
+ world.recorder.endLeftMoveRecord();
+ }
+
+ direction = 0;
+ }
+
+ if (keys[KEY_LEFT] && !ducking)
+ {
+ if (facing == 1) sliding = false;
+ xa -= sideWaysSpeed;
+ if (jumpTime >= 0) facing = -1;
+ }
+
+ if (keys[KEY_RIGHT] && !ducking)
+ {
+ if (facing == -1) sliding = false;
+ xa += sideWaysSpeed;
+ if (jumpTime >= 0) facing = 1;
+ }
+
+ if ((!keys[KEY_LEFT] && !keys[KEY_RIGHT]) || ducking || ya < 0 || onGround)
+ {
+ sliding = false;
+ }
+
+ if (keys[KEY_SPEED] && canShoot && Mario.fire && world.fireballsOnScreen<2)
+ {
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_FIREBALL], this, 1, 1, 1);
+ world.addSprite(new Fireball(world, x+facing*6, y-20, facing));
+ }
+
+ canShoot = !keys[KEY_SPEED];
+
+ mayJump = (onGround || sliding) && !keys[KEY_JUMP];
+
+ xFlipPic = facing == -1;
+
+ runTime += (Math.abs(xa)) + 5;
+ if (Math.abs(xa) < 0.5f)
+ {
+ runTime = 0;
+ xa = 0;
+ }
+
+ calcPic();
+
+ if (sliding)
+ {
+ for (int i = 0; i < 1; i++)
+ {
+ world.addSprite(new Sparkle((int) (x + Math.random() * 4 - 2) + facing * 8, (int) (y + Math.random() * 4) - 24, (float) (Math.random() * 2 - 1), (float) Math.random() * 1, 0, 1, 5));
+ }
+ ya *= 0.5f;
+ }
+
+ onGround = false;
+ move(xa, 0);
+ move(0, ya);
+
+ if (y > world.level.getHeight() * 16 + 16)
+ {
+ dieJump();
+ }
+
+ if (x < 0)
+ {
+ x = 0;
+ xa = 0;
+ }
+
+// if(x > world.level.xExit * 16 && ! world.level.flipped || x < world.level.xExit * 16 && world.level.flipped )
+ if(x > world.level.getxExit() * 16 )
+
+ {
+ win();
+ }
+
+ if (x > world.level.getWidth() * 16)
+ {
+ x = world.level.getWidth() * 16;
+ xa = 0;
+ }
+
+ ya *= 0.85f;
+ if (onGround)
+ {
+ xa *= GROUND_INERTIA;
+ }
+ else
+ {
+ xa *= AIR_INERTIA;
+ }
+
+ if (!onGround)
+ {
+ ya += 3;
+ }
+
+ if (carried != null)
+ {
+ carried.x = x + facing * 8;
+ carried.y = y - 2;
+ if (!keys[KEY_SPEED])
+ {
+ carried.release(this);
+ carried = null;
+
+ if(world.recorder != null){
+ world.recorder.shellUnleashedRecord();
+ }
+ }
+ }
+ }
+
+ private void calcPic()
+ {
+ int runFrame = 0;
+
+ if (large)
+ {
+ runFrame = ((int) (runTime / 20)) % 4;
+ if (runFrame == 3) runFrame = 1;
+ if (carried == null && Math.abs(xa) > 10) runFrame += 3;
+ if (carried != null) runFrame += 10;
+ if (!onGround)
+ {
+ if (carried != null) runFrame = 12;
+ else if (Math.abs(xa) > 10) runFrame = 7;
+ else runFrame = 6;
+ }
+ }
+ else
+ {
+ runFrame = ((int) (runTime / 20)) % 2;
+ if (carried == null && Math.abs(xa) > 10) runFrame += 2;
+ if (carried != null) runFrame += 8;
+ if (!onGround)
+ {
+ if (carried != null) runFrame = 9;
+ else if (Math.abs(xa) > 10) runFrame = 5;
+ else runFrame = 4;
+ }
+ }
+
+ if (onGround && ((facing == -1 && xa > 0) || (facing == 1 && xa < 0)))
+ {
+ if (xa > 1 || xa < -1) runFrame = large ? 9 : 7;
+
+ if (xa > 3 || xa < -3)
+ {
+ for (int i = 0; i < 3; i++)
+ {
+ world.addSprite(new Sparkle((int) (x + Math.random() * 8 - 4), (int) (y + Math.random() * 4), (float) (Math.random() * 2 - 1), (float) Math.random() * -1, 0, 1, 5));
+ }
+ }
+ }
+
+ if (large)
+ {
+ if (ducking) runFrame = 14;
+ height = ducking ? 12 : 24;
+ }
+ else
+ {
+ height = 12;
+ }
+
+ xPic = runFrame;
+ }
+
+ private boolean move(float xa, float ya)
+ {
+
+ while (xa > 8)
+ {
+ if (!move(8, 0)) return false;
+ xa -= 8;
+ }
+ while (xa < -8)
+ {
+ if (!move(-8, 0)) return false;
+ xa += 8;
+ }
+ while (ya > 8)
+ {
+ if (!move(0, 8)) return false;
+ ya -= 8;
+ }
+ while (ya < -8)
+ {
+ if (!move(0, -8)) return false;
+ ya += 8;
+ }
+
+ boolean collide = false;
+ if (ya > 0)
+ {
+ if (isBlocking(x + xa - width, y + ya, xa, 0)) collide = true;
+ else if (isBlocking(x + xa + width, y + ya, xa, 0)) collide = true;
+ else if (isBlocking(x + xa - width, y + ya + 1, xa, ya)) collide = true;
+ else if (isBlocking(x + xa + width, y + ya + 1, xa, ya)) collide = true;
+ }
+ if (ya < 0)
+ {
+ if (isBlocking(x + xa, y + ya - height, xa, ya)) collide = true;
+ else if (collide || isBlocking(x + xa - width, y + ya - height, xa, ya)) collide = true;
+ else if (collide || isBlocking(x + xa + width, y + ya - height, xa, ya)) collide = true;
+ }
+ if (xa > 0)
+ {
+ sliding = true;
+ if (isBlocking(x + xa + width, y + ya - height, xa, ya)) collide = true;
+ else sliding = false;
+ if (isBlocking(x + xa + width, y + ya - height / 2, xa, ya)) collide = true;
+ else sliding = false;
+ if (isBlocking(x + xa + width, y + ya, xa, ya)) collide = true;
+ else sliding = false;
+ }
+ if (xa < 0)
+ {
+ sliding = true;
+ if (isBlocking(x + xa - width, y + ya - height, xa, ya)) collide = true;
+ else sliding = false;
+ if (isBlocking(x + xa - width, y + ya - height / 2, xa, ya)) collide = true;
+ else sliding = false;
+ if (isBlocking(x + xa - width, y + ya, xa, ya)) collide = true;
+ else sliding = false;
+ }
+
+ if (collide)
+ {
+ if (xa < 0)
+ {
+ x = (int) ((x - width) / 16) * 16 + width;
+ this.xa = 0;
+ }
+ if (xa > 0)
+ {
+ x = (int) ((x + width) / 16 + 1) * 16 - width - 1;
+ this.xa = 0;
+ }
+ if (ya < 0)
+ {
+ y = (int) ((y - height) / 16) * 16 + height;
+ jumpTime = 0;
+ this.ya = 0;
+ }
+ if (ya > 0)
+ {
+ y = (int) ((y - 1) / 16 + 1) * 16 - 1;
+ onGround = true;
+ }
+ return false;
+ }
+ else
+ {
+ x += xa;
+ y += ya;
+ return true;
+ }
+ }
+
+ private boolean isBlocking(float _x, float _y, float xa, float ya)
+ {
+ //translate into block mode (since blocks are 16x16)
+ int x = (int) (_x / 16);
+ int y = (int) (_y / 16);
+
+ if (x==(int)(this.x/16) && y==(int)(this.y/16))
+ return false;
+
+ boolean blocking = world.level.isBlocking(x, y, xa, ya);
+
+ byte block = world.level.getBlock(x, y);
+
+ if (((Level.TILE_BEHAVIORS[block & 0xff]) & Level.BIT_PICKUPABLE) > 0)
+ {
+ if(world.recorder != null)
+ world.recorder.recordCoin();
+
+ Mario.getCoin();
+ world.sound.play(Art.samples[Art.SAMPLE_GET_COIN], new FixedSoundSource(x * 16 + 8, y * 16 + 8), 1, 1, 1);
+ world.level.setBlock(x, y, (byte) 0);
+ for (int xx = 0; xx < 2; xx++)
+ for (int yy = 0; yy < 2; yy++)
+ world.addSprite(new Sparkle(x * 16 + xx * 8 + (int) (Math.random() * 8), y * 16 + yy * 8 + (int) (Math.random() * 8), 0, 0, 0, 2, 5));
+ }
+
+ if (blocking && ya < 0)
+ {
+ world.bump(x, y, large);
+ }
+
+ return blocking;
+ }
+
+ public void stomp(Enemy enemy)
+ {
+ if (deathTime > 0 || world.paused) return;
+
+ float targetY = enemy.y - enemy.height / 2;
+ move(0, targetY - y);
+
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_KICK], this, 1, 1, 1);
+ xJumpSpeed = 0;
+ yJumpSpeed = -1.9f;
+ jumpTime = 8;
+ ya = jumpTime * yJumpSpeed;
+ onGround = false;
+ sliding = false;
+ invulnerableTime = 1;
+
+ if(world.recorder!=null)
+ world.recorder.killStompRecord(enemy);
+ }
+
+ public void stomp(Shell shell)
+ {
+ if (deathTime > 0 || world.paused) return;
+
+ if (keys[KEY_SPEED] && shell.facing == 0)
+ {
+ carried = shell;
+ shell.carried = true;
+ }
+ else
+ {
+ float targetY = shell.y - shell.height / 2;
+ move(0, targetY - y);
+
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_KICK], this, 1, 1, 1);
+ xJumpSpeed = 0;
+ yJumpSpeed = -1.9f;
+ jumpTime = 8;
+ ya = jumpTime * yJumpSpeed;
+ onGround = false;
+ sliding = false;
+ invulnerableTime = 1;
+
+ if(shell.xa == 0 && world.recorder!= null){
+ world.recorder.shellUnleashedRecord();
+ }
+ }
+ }
+
+ public void getHurt(Sprite sprite)
+ {
+ if (deathTime > 0 || world.paused) return;
+ if (invulnerableTime > 0) return;
+
+ if (large)
+ {
+ world.paused = true;
+ powerUpTime = -3 * 6;
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_POWER_DOWN], this, 1, 1, 1);
+ if (fire)
+ {
+ world.mario.setLarge(true, false);
+
+ if(world.recorder != null){
+ world.recorder.endFireRecord();
+ world.recorder.startLargeRecord();
+ }
+ }
+ else
+ {
+ world.mario.setLarge(false, false);
+
+ if(world.recorder != null){
+ world.recorder.endLargeRecord();
+ world.recorder.startLittleRecord();
+ }
+ }
+ invulnerableTime = 32;
+ }
+ else
+ {
+ dieSprite(sprite);
+ }
+ }
+
+ private void win()
+ {
+ xDeathPos = (int) x;
+ yDeathPos = (int) y;
+ world.paused = true;
+ winTime = 1;
+ Art.stopMusic();
+ world.sound.play(Art.samples[Art.SAMPLE_LEVEL_EXIT], this, 1, 1, 1);
+ }
+
+ public void dieSprite(Sprite sprite){
+ die();
+
+ if(world.recorder != null){
+ world.recorder.dieRecord(sprite);
+ }
+ }
+
+ public void dieTime(){
+ die();
+
+ if(world.recorder!=null){
+ world.recorder.dieTimeRecord();
+ }
+ }
+
+ public void dieJump(){
+ die();
+
+ if(world.recorder!=null){
+ world.recorder.dieJumpRecord();
+ }
+ }
+
+ public void die()
+ {
+ xDeathPos = (int) x;
+ yDeathPos = (int) y;
+ world.paused = true;
+ deathTime = 1;
+ Art.stopMusic();
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_DEATH], this, 1, 1, 1);
+
+ if(world.recorder != null){
+
+
+ if(running)
+ world.recorder.endRunningRecord();
+
+ if(large && !fire){
+ world.recorder.endLargeRecord();
+ }
+
+ if(fire){
+ world.recorder.endFireRecord();
+ }
+
+ if(!large && !fire)
+ world.recorder.endLittleRecord();
+
+ if(ducking)
+ world.recorder.endDuckRecord();
+
+ world.recorder.endTime();
+ world.recorder.recordJumpLand();
+ }
+
+ large = false;
+ fire = false;
+ }
+
+
+ public void getFlower()
+ {
+ if (deathTime > 0 || world.paused) return;
+
+ if (!fire)
+ {
+ world.paused = true;
+ powerUpTime = 3 * 6;
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_POWER_UP], this, 1, 1, 1);
+ world.mario.setLarge(true, true);
+
+ if(world.recorder != null){
+ if(large){
+ world.recorder.endLargeRecord();
+
+ }
+ else{
+ world.recorder.endLittleRecord();
+ }
+
+ world.recorder.startFireRecord();
+ }
+ }
+ else
+ {
+ Mario.getCoin();
+ world.sound.play(Art.samples[Art.SAMPLE_GET_COIN], this, 1, 1, 1);
+ }
+ }
+
+ public void getMushroom()
+ {
+ if (deathTime > 0 || world.paused) return;
+
+ if (!large)
+ {
+ world.paused = true;
+ powerUpTime = 3 * 6;
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_POWER_UP], this, 1, 1, 1);
+ world.mario.setLarge(true, false);
+
+ if(world.recorder != null){
+ world.recorder.endLittleRecord();
+ world.recorder.startLargeRecord();
+ }
+ }
+ else
+ {
+
+ Mario.getCoin();
+ world.sound.play(Art.samples[Art.SAMPLE_GET_COIN], this, 1, 1, 1);
+ }
+ }
+
+ public void kick(Shell shell)
+ {
+ if (deathTime > 0 || world.paused) return;
+
+ if (keys[KEY_SPEED])
+ {
+ carried = shell;
+ shell.carried = true;
+ }
+ else
+ {
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_KICK], this, 1, 1, 1);
+ invulnerableTime = 1;
+
+ if(world.recorder!=null)
+ world.recorder.shellUnleashedRecord();
+ }
+ }
+
+ public void stomp(BulletBill bill)
+ {
+ if (deathTime > 0 || world.paused) return;
+
+ float targetY = bill.y - bill.height / 2;
+ move(0, targetY - y);
+
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_KICK], this, 1, 1, 1);
+ xJumpSpeed = 0;
+ yJumpSpeed = -1.9f;
+ jumpTime = 8;
+ ya = jumpTime * yJumpSpeed;
+ onGround = false;
+ sliding = false;
+ invulnerableTime = 1;
+
+ if(world.recorder!=null)
+ world.recorder.killStompRecord(bill);
+ }
+
+ public byte getKeyMask()
+ {
+ int mask = 0;
+ for (int i = 0; i < 7; i++)
+ {
+ if (keys[i]) mask |= (1 << i);
+ }
+ return (byte) mask;
+ }
+
+ public void setKeys(byte mask)
+ {
+ for (int i = 0; i < 7; i++)
+ {
+ keys[i] = (mask & (1 << i)) > 0;
+ }
+ }
+
+ public static void get1Up()
+ {
+ instance.world.sound.play(Art.samples[Art.SAMPLE_MARIO_1UP], instance, 1, 1, 1);
+ lives++;
+ if (lives==99)
+ {
+ lives = 99;
+ }
+ }
+
+ public static void getCoin()
+ {
+ coins++;
+ if (coins==100)
+ {
+ coins = 0;
+ get1Up();
+ }
+ }
+}
diff --git a/src/dk/itu/mario/engine/sprites/Mushroom.java b/src/dk/itu/mario/engine/sprites/Mushroom.java
new file mode 100644
index 0000000..cdd44d8
--- /dev/null
+++ b/src/dk/itu/mario/engine/sprites/Mushroom.java
@@ -0,0 +1,223 @@
+package dk.itu.mario.engine.sprites;
+
+
+import dk.itu.mario.engine.Art;
+import dk.itu.mario.scene.LevelScene;
+
+
+
+public class Mushroom extends Sprite
+{
+ private static float GROUND_INERTIA = 0.89f;
+ private static float AIR_INERTIA = 0.89f;
+
+ private float runTime;
+ private boolean onGround = false;
+ private boolean mayJump = false;
+ private int jumpTime = 0;
+ private float xJumpSpeed;
+ private float yJumpSpeed;
+
+ private int width = 4;
+ int height = 24;
+
+ private LevelScene world;
+ public int facing;
+
+ public boolean avoidCliffs = false;
+ private int life;
+
+ public Mushroom(LevelScene world, int x, int y)
+ {
+ sheet = Art.items;
+
+ this.x = x;
+ this.y = y;
+ this.world = world;
+ xPicO = 8;
+ yPicO = 15;
+
+ yPic = 0;
+ height = 12;
+ facing = 1;
+ wPic = hPic = 16;
+ life = 0;
+ }
+
+ public void collideCheck()
+ {
+ float xMarioD = world.mario.x - x;
+ float yMarioD = world.mario.y - y;
+ float w = 16;
+ if (xMarioD > -16 && xMarioD < 16)
+ {
+ if (yMarioD > -height && yMarioD < world.mario.height)
+ {
+ world.mario.getMushroom();
+ spriteContext.removeSprite(this);
+ }
+ }
+ }
+
+ public void move()
+ {
+ if (life<9)
+ {
+ layer = 0;
+ y--;
+ life++;
+ return;
+ }
+ float sideWaysSpeed = 1.75f;
+ layer = 1;
+ // float sideWaysSpeed = onGround ? 2.5f : 1.2f;
+
+ if (xa > 2)
+ {
+ facing = 1;
+ }
+ if (xa < -2)
+ {
+ facing = -1;
+ }
+
+ xa = facing * sideWaysSpeed;
+
+ mayJump = (onGround);
+
+ xFlipPic = facing == -1;
+
+ runTime += (Math.abs(xa)) + 5;
+
+
+
+ if (!move(xa, 0)) facing = -facing;
+ onGround = false;
+ move(0, ya);
+
+ ya *= 0.85f;
+ if (onGround)
+ {
+ xa *= GROUND_INERTIA;
+ }
+ else
+ {
+ xa *= AIR_INERTIA;
+ }
+
+ if (!onGround)
+ {
+ ya += 2;
+ }
+ }
+
+ private boolean move(float xa, float ya)
+ {
+ while (xa > 8)
+ {
+ if (!move(8, 0)) return false;
+ xa -= 8;
+ }
+ while (xa < -8)
+ {
+ if (!move(-8, 0)) return false;
+ xa += 8;
+ }
+ while (ya > 8)
+ {
+ if (!move(0, 8)) return false;
+ ya -= 8;
+ }
+ while (ya < -8)
+ {
+ if (!move(0, -8)) return false;
+ ya += 8;
+ }
+
+ boolean collide = false;
+ if (ya > 0)
+ {
+ if (isBlocking(x + xa - width, y + ya, xa, 0)) collide = true;
+ else if (isBlocking(x + xa + width, y + ya, xa, 0)) collide = true;
+ else if (isBlocking(x + xa - width, y + ya + 1, xa, ya)) collide = true;
+ else if (isBlocking(x + xa + width, y + ya + 1, xa, ya)) collide = true;
+ }
+ if (ya < 0)
+ {
+ if (isBlocking(x + xa, y + ya - height, xa, ya)) collide = true;
+ else if (collide || isBlocking(x + xa - width, y + ya - height, xa, ya)) collide = true;
+ else if (collide || isBlocking(x + xa + width, y + ya - height, xa, ya)) collide = true;
+ }
+ if (xa > 0)
+ {
+ if (isBlocking(x + xa + width, y + ya - height, xa, ya)) collide = true;
+ if (isBlocking(x + xa + width, y + ya - height / 2, xa, ya)) collide = true;
+ if (isBlocking(x + xa + width, y + ya, xa, ya)) collide = true;
+
+ if (avoidCliffs && onGround && !world.level.isBlocking((int) ((x + xa + width) / 16), (int) ((y) / 16 + 1), xa, 1)) collide = true;
+ }
+ if (xa < 0)
+ {
+ if (isBlocking(x + xa - width, y + ya - height, xa, ya)) collide = true;
+ if (isBlocking(x + xa - width, y + ya - height / 2, xa, ya)) collide = true;
+ if (isBlocking(x + xa - width, y + ya, xa, ya)) collide = true;
+
+ if (avoidCliffs && onGround && !world.level.isBlocking((int) ((x + xa - width) / 16), (int) ((y) / 16 + 1), xa, 1)) collide = true;
+ }
+
+ if (collide)
+ {
+ if (xa < 0)
+ {
+ x = (int) ((x - width) / 16) * 16 + width;
+ this.xa = 0;
+ }
+ if (xa > 0)
+ {
+ x = (int) ((x + width) / 16 + 1) * 16 - width - 1;
+ this.xa = 0;
+ }
+ if (ya < 0)
+ {
+ y = (int) ((y - height) / 16) * 16 + height;
+ jumpTime = 0;
+ this.ya = 0;
+ }
+ if (ya > 0)
+ {
+ y = (int) (y / 16 + 1) * 16 - 1;
+ onGround = true;
+ }
+ return false;
+ }
+ else
+ {
+ x += xa;
+ y += ya;
+ return true;
+ }
+ }
+
+ private boolean isBlocking(float _x, float _y, float xa, float ya)
+ {
+ int x = (int) (_x / 16);
+ int y = (int) (_y / 16);
+ if (x == (int) (this.x / 16) && y == (int) (this.y / 16)) return false;
+
+ boolean blocking = world.level.isBlocking(x, y, xa, ya);
+
+ byte block = world.level.getBlock(x, y);
+
+ return blocking;
+ }
+
+ public void bumpCheck(int xTile, int yTile)
+ {
+ if (x + width > xTile * 16 && x - width < xTile * 16 + 16 && yTile==(int)((y-1)/16))
+ {
+ facing = -world.mario.facing;
+ ya = -10;
+ }
+ }
+
+}
diff --git a/src/dk/itu/mario/engine/sprites/Particle.java b/src/dk/itu/mario/engine/sprites/Particle.java
new file mode 100644
index 0000000..60a6ad2
--- /dev/null
+++ b/src/dk/itu/mario/engine/sprites/Particle.java
@@ -0,0 +1,40 @@
+package dk.itu.mario.engine.sprites;
+
+import dk.itu.mario.engine.Art;
+
+
+public class Particle extends Sprite
+{
+ public int life;
+
+ public Particle(int x, int y, float xa, float ya)
+ {
+ this(x, y, xa, ya, (int)(Math.random()*2), 0);
+ }
+
+ public Particle(int x, int y, float xa, float ya, int xPic, int yPic)
+ {
+ sheet = Art.particles;
+ this.x = x;
+ this.y = y;
+ this.xa = xa;
+ this.ya = ya;
+ this.xPic = xPic;
+ this.yPic = yPic;
+ this.xPicO = 4;
+ this.yPicO = 4;
+
+ wPic = 8;
+ hPic = 8;
+ life = 10;
+ }
+
+ public void move()
+ {
+ if (life--<0) Sprite.spriteContext.removeSprite(this);
+ x+=xa;
+ y+=ya;
+ ya*=0.95f;
+ ya+=3;
+ }
+}
diff --git a/src/dk/itu/mario/engine/sprites/Shell.java b/src/dk/itu/mario/engine/sprites/Shell.java
new file mode 100644
index 0000000..fea02b4
--- /dev/null
+++ b/src/dk/itu/mario/engine/sprites/Shell.java
@@ -0,0 +1,370 @@
+package dk.itu.mario.engine.sprites;
+
+
+import dk.itu.mario.engine.Art;
+import dk.itu.mario.scene.LevelScene;
+
+
+
+
+public class Shell extends Sprite
+{
+ private static float GROUND_INERTIA = 0.89f;
+ private static float AIR_INERTIA = 0.89f;
+
+ private float runTime;
+ private boolean onGround = false;
+
+ private int width = 4;
+ int height = 24;
+
+ private LevelScene world;
+ public int facing;
+
+ public boolean avoidCliffs = false;
+ public int anim;
+
+ public boolean dead = false;
+ private int deadTime = 0;
+ public boolean carried;
+
+
+ public Shell(LevelScene world, float x, float y, int type)
+ {
+ sheet = Art.enemies;
+
+ this.x = x;
+ this.y = y;
+ this.world = world;
+ xPicO = 8;
+ yPicO = 31;
+
+ yPic = type;
+ height = 12;
+ facing = 0;
+ wPic = 16;
+
+ xPic = 4;
+ ya = -5;
+ }
+
+ public boolean fireballCollideCheck(Fireball fireball)
+ {
+ if (deadTime != 0) return false;
+
+ float xD = fireball.x - x;
+ float yD = fireball.y - y;
+
+ if (xD > -16 && xD < 16)
+ {
+ if (yD > -height && yD < fireball.height)
+ {
+ if (facing!=0) return true;
+
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_KICK], this, 1, 1, 1);
+
+ xa = fireball.facing * 2;
+ ya = -5;
+ if (spriteTemplate != null) spriteTemplate.isDead = true;
+ deadTime = 100;
+ hPic = -hPic;
+ yPicO = -yPicO + 16;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void collideCheck()
+ {
+ if (carried || dead || deadTime>0) return;
+
+ float xMarioD = world.mario.x - x;
+ float yMarioD = world.mario.y - y;
+ float w = 16;
+ if (xMarioD > -16 && xMarioD < 16)
+ {
+ if (yMarioD > -height && yMarioD < world.mario.height)
+ {
+ if (world.mario.ya > 0 && yMarioD <= 0 && (!world.mario.onGround || !world.mario.wasOnGround))
+ {
+ world.mario.stomp(this);
+ if (facing != 0)
+ {
+ xa = 0;
+ facing = 0;
+ }
+ else
+ {
+ facing = world.mario.facing;
+ }
+ }
+ else
+ {
+ if (facing != 0)
+ {
+ world.mario.getHurt(this);
+ }
+ else
+ {
+ world.mario.kick(this);
+ facing = world.mario.facing;
+ }
+ }
+ }
+ }
+ }
+
+ public void move()
+ {
+ if (y > world.level.getHeight() * 16 + 16 && deadTime == 0)
+ {
+ die();
+ spriteContext.removeSprite(this);
+ return;
+ }
+
+ if (carried)
+ {
+ world.checkShellCollide(this);
+ return;
+ }
+
+ if (deadTime > 0)
+ {
+ deadTime--;
+
+ if (deadTime == 0)
+ {
+ deadTime = 1;
+ for (int i = 0; i < 8; i++)
+ {
+ world.addSprite(new Sparkle((int) (x + Math.random() * 16 - 8) + 4, (int) (y - Math.random() * 8) + 4, (float) (Math.random() * 2 - 1), (float) Math.random() * -1, 0, 1, 5));
+ }
+ spriteContext.removeSprite(this);
+ }
+
+ x += xa;
+ y += ya;
+ ya *= 0.95;
+ ya += 1;
+
+ return;
+ }
+
+ if (facing != 0) anim++;
+
+ float sideWaysSpeed = 11f;
+ // float sideWaysSpeed = onGround ? 2.5f : 1.2f;
+
+ if (xa > 2)
+ {
+ facing = 1;
+ }
+ if (xa < -2)
+ {
+ facing = -1;
+ }
+
+ xa = facing * sideWaysSpeed;
+
+ if (facing != 0)
+ {
+ world.checkShellCollide(this);
+ }
+
+ xFlipPic = facing == -1;
+
+ runTime += (Math.abs(xa)) + 5;
+
+ xPic = (anim / 2) % 4 + 3;
+
+
+
+ if (!move(xa, 0))
+ {
+ world.sound.play(Art.samples[Art.SAMPLE_SHELL_BUMP], this, 1, 1, 1);
+
+ facing = -facing;
+ }
+ onGround = false;
+ move(0, ya);
+
+ ya *= 0.85f;
+ if (onGround)
+ {
+ xa *= GROUND_INERTIA;
+ }
+ else
+ {
+ xa *= AIR_INERTIA;
+ }
+
+ if (!onGround)
+ {
+ ya += 2;
+ }
+ }
+
+ private boolean move(float xa, float ya)
+ {
+ while (xa > 8)
+ {
+ if (!move(8, 0)) return false;
+ xa -= 8;
+ }
+ while (xa < -8)
+ {
+ if (!move(-8, 0)) return false;
+ xa += 8;
+ }
+ while (ya > 8)
+ {
+ if (!move(0, 8)) return false;
+ ya -= 8;
+ }
+ while (ya < -8)
+ {
+ if (!move(0, -8)) return false;
+ ya += 8;
+ }
+
+ boolean collide = false;
+ if (ya > 0)
+ {
+ if (isBlocking(x + xa - width, y + ya, xa, 0)) collide = true;
+ else if (isBlocking(x + xa + width, y + ya, xa, 0)) collide = true;
+ else if (isBlocking(x + xa - width, y + ya + 1, xa, ya)) collide = true;
+ else if (isBlocking(x + xa + width, y + ya + 1, xa, ya)) collide = true;
+ }
+ if (ya < 0)
+ {
+ if (isBlocking(x + xa, y + ya - height, xa, ya)) collide = true;
+ else if (collide || isBlocking(x + xa - width, y + ya - height, xa, ya)) collide = true;
+ else if (collide || isBlocking(x + xa + width, y + ya - height, xa, ya)) collide = true;
+ }
+ if (xa > 0)
+ {
+ if (isBlocking(x + xa + width, y + ya - height, xa, ya)) collide = true;
+ if (isBlocking(x + xa + width, y + ya - height / 2, xa, ya)) collide = true;
+ if (isBlocking(x + xa + width, y + ya, xa, ya)) collide = true;
+
+ if (avoidCliffs && onGround && !world.level.isBlocking((int) ((x + xa + width) / 16), (int) ((y) / 16 + 1), xa, 1)) collide = true;
+ }
+ if (xa < 0)
+ {
+ if (isBlocking(x + xa - width, y + ya - height, xa, ya)) collide = true;
+ if (isBlocking(x + xa - width, y + ya - height / 2, xa, ya)) collide = true;
+ if (isBlocking(x + xa - width, y + ya, xa, ya)) collide = true;
+
+ if (avoidCliffs && onGround && !world.level.isBlocking((int) ((x + xa - width) / 16), (int) ((y) / 16 + 1), xa, 1)) collide = true;
+ }
+
+ if (collide)
+ {
+ if (xa < 0)
+ {
+ x = (int) ((x - width) / 16) * 16 + width;
+ this.xa = 0;
+ }
+ if (xa > 0)
+ {
+ x = (int) ((x + width) / 16 + 1) * 16 - width - 1;
+ this.xa = 0;
+ }
+ if (ya < 0)
+ {
+ y = (int) ((y - height) / 16) * 16 + height;
+ this.ya = 0;
+ }
+ if (ya > 0)
+ {
+ y = (int) (y / 16 + 1) * 16 - 1;
+ onGround = true;
+ }
+ return false;
+ }
+ else
+ {
+ x += xa;
+ y += ya;
+ return true;
+ }
+ }
+
+ private boolean isBlocking(float _x, float _y, float xa, float ya)
+ {
+ int x = (int) (_x / 16);
+ int y = (int) (_y / 16);
+ if (x == (int) (this.x / 16) && y == (int) (this.y / 16)) return false;
+
+ boolean blocking = world.level.isBlocking(x, y, xa, ya);
+
+ byte block = world.level.getBlock(x, y);
+
+ if (blocking && ya == 0 && xa!=0)
+ {
+ world.bump(x, y, true);
+ }
+
+ return blocking;
+ }
+
+ public void bumpCheck(int xTile, int yTile)
+ {
+ if (x + width > xTile * 16 && x - width < xTile * 16 + 16 && yTile == (int) ((y - 1) / 16))
+ {
+ facing = -world.mario.facing;
+ ya = -10;
+ }
+ }
+
+ public void die()
+ {
+ dead = true;
+
+ carried = false;
+
+ xa = -facing * 2;
+ ya = -5;
+ deadTime = 100;
+
+ if(world.recorder != null)
+ world.recorder.killRecord(this);
+ }
+
+ public boolean shellCollideCheck(Shell shell)
+ {
+ if (deadTime != 0) return false;
+
+ float xD = shell.x - x;
+ float yD = shell.y - y;
+
+ if (xD > -16 && xD < 16)
+ {
+ if (yD > -height && yD < shell.height)
+ {
+ world.sound.play(Art.samples[Art.SAMPLE_MARIO_KICK], this, 1, 1, 1);
+
+ if (world.mario.carried == shell || world.mario.carried == this)
+ {
+ world.mario.carried = null;
+ }
+
+ die();
+ shell.die();
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ public void release(Mario mario)
+ {
+ carried = false;
+ facing = mario.facing;
+ x += facing * 8;
+ }
+}
diff --git a/src/dk/itu/mario/engine/sprites/Sparkle.java b/src/dk/itu/mario/engine/sprites/Sparkle.java
new file mode 100644
index 0000000..3a06850
--- /dev/null
+++ b/src/dk/itu/mario/engine/sprites/Sparkle.java
@@ -0,0 +1,46 @@
+package dk.itu.mario.engine.sprites;
+
+import dk.itu.mario.engine.Art;
+
+
+public class Sparkle extends Sprite
+{
+ public int life;
+ public int xPicStart;
+
+ public Sparkle(int x, int y, float xa, float ya)
+ {
+ this(x, y, xa, ya, (int)(Math.random()*2), 0, 5);
+ }
+
+ public Sparkle(int x, int y, float xa, float ya, int xPic, int yPic, int timeSpan)
+ {
+ sheet = Art.particles;
+ this.x = x;
+ this.y = y;
+ this.xa = xa;
+ this.ya = ya;
+ this.xPic = xPic;
+ xPicStart = xPic;
+ this.yPic = yPic;
+ this.xPicO = 4;
+ this.yPicO = 4;
+
+ wPic = 8;
+ hPic = 8;
+ life = 10+(int)(Math.random()*timeSpan);
+ }
+
+ public void move()
+ {
+ if (life>10)
+ xPic = 7;
+ else
+ xPic = xPicStart+(10-life)*4/10;
+
+ if (life--<0) Sprite.spriteContext.removeSprite(this);
+
+ x+=xa;
+ y+=ya;
+ }
+}
diff --git a/src/dk/itu/mario/engine/sprites/Sprite.java b/src/dk/itu/mario/engine/sprites/Sprite.java
new file mode 100644
index 0000000..0ebed58
--- /dev/null
+++ b/src/dk/itu/mario/engine/sprites/Sprite.java
@@ -0,0 +1,117 @@
+package dk.itu.mario.engine.sprites;
+
+import java.awt.Graphics;
+import java.awt.Image;
+
+
+
+
+
+import dk.itu.mario.engine.sonar.SoundSource;
+
+public class Sprite implements SoundSource
+{
+ public static SpriteContext spriteContext;
+
+ public float xOld, yOld, x, y, xa, ya;
+
+ public int xPic, yPic;
+ public int wPic = 32;
+ public int hPic = 32;
+ public int xPicO, yPicO;
+ public boolean xFlipPic = false;
+ public boolean yFlipPic = false;
+ public Image[][] sheet;
+ public boolean visible = true;
+
+ public int layer = 1;
+
+ public SpriteTemplate spriteTemplate;
+
+ public void move()
+ {
+ x+=xa;
+ y+=ya;
+ }
+
+ public void render(Graphics og, float alpha)
+ {
+ if (!visible) return;
+
+ int xPixel = (int)(xOld+(x-xOld)*alpha)-xPicO;
+ int yPixel = (int)(yOld+(y-yOld)*alpha)-yPicO;
+
+ og.drawImage(sheet[xPic][yPic], xPixel+(xFlipPic?wPic:0), yPixel+(yFlipPic?hPic:0), xFlipPic?-wPic:wPic, yFlipPic?-hPic:hPic, null);
+ }
+
+/* private void blit(Graphics og, Image bitmap, int x0, int y0, int x1, int y1, int w, int h)
+ {
+ if (!xFlipPic)
+ {
+ if (!yFlipPic)
+ {
+ og.drawImage(bitmap, x0, y0, x0+w, y0+h, x1, y1, x1+w, y1+h, null);
+ }
+ else
+ {
+ og.drawImage(bitmap, x0, y0, x0+w, y0+h, x1, y1+h, x1+w, y1, null);
+ }
+ }
+ else
+ {
+ if (!yFlipPic)
+ {
+ og.drawImage(bitmap, x0, y0, x0+w, y0+h, x1+w, y1, x1, y1+h, null);
+ }
+ else
+ {
+ og.drawImage(bitmap, x0, y0, x0+w, y0+h, x1+w, y1+h, x1, y1, null);
+ }
+ }
+ }*/
+
+ public final void tick()
+ {
+ xOld = x;
+ yOld = y;
+ move();
+ }
+
+ public final void tickNoMove()
+ {
+ xOld = x;
+ yOld = y;
+ }
+
+ public float getX(float alpha)
+ {
+ return (xOld+(x-xOld)*alpha)-xPicO;
+ }
+
+ public float getY(float alpha)
+ {
+ return (yOld+(y-yOld)*alpha)-yPicO;
+ }
+
+ public void collideCheck()
+ {
+ }
+
+ public void bumpCheck(int xTile, int yTile)
+ {
+ }
+
+ public boolean shellCollideCheck(Shell shell)
+ {
+ return false;
+ }
+
+ public void release(Mario mario)
+ {
+ }
+
+ public boolean fireballCollideCheck(Fireball fireball)
+ {
+ return false;
+ }
+}
diff --git a/src/dk/itu/mario/engine/sprites/SpriteContext.java b/src/dk/itu/mario/engine/sprites/SpriteContext.java
new file mode 100644
index 0000000..5246861
--- /dev/null
+++ b/src/dk/itu/mario/engine/sprites/SpriteContext.java
@@ -0,0 +1,18 @@
+package dk.itu.mario.engine.sprites;
+
+/**
+ * Title:
+ *
+ * Description:
+ *
+ * Copyright: Copyright (c) 2010
+ *
+ * Company:
+ *
+ * @author not attributable
+ * @version 1.0
+ */
+public interface SpriteContext {
+ public void addSprite(Sprite sprite);
+ public void removeSprite(Sprite sprite);
+}
diff --git a/src/dk/itu/mario/engine/sprites/SpriteTemplate.java b/src/dk/itu/mario/engine/sprites/SpriteTemplate.java
new file mode 100644
index 0000000..931c392
--- /dev/null
+++ b/src/dk/itu/mario/engine/sprites/SpriteTemplate.java
@@ -0,0 +1,60 @@
+package dk.itu.mario.engine.sprites;
+
+import dk.itu.mario.scene.LevelScene;
+
+public class SpriteTemplate
+{
+ public static final int RED_TURTLE = 0;
+ public static final int GREEN_TURTLE = 1;
+ public static final int GOOMPA = 2;
+ public static final int ARMORED_TURTLE = 3;
+ public static final int JUMP_FLOWER = 4;
+ public static final int CANNON_BALL = 5;
+ public static final int CHOMP_FLOWER = 6;
+
+ public static int enemiesSpawned = 0;
+ public static int enemiesMax = 1000;
+
+ public boolean hasSpawned = false;
+
+ public int lastVisibleTick = -1;
+ public Sprite sprite;
+ public boolean isDead = false;
+ private boolean winged;
+
+ public int type;
+ public int direction = 1;
+
+ public SpriteTemplate(int type, boolean winged)
+ {
+ this.type = type;
+ this.winged = winged;
+ }
+
+ public void spawn(LevelScene world, int x, int y, int dir)
+ {
+ if (isDead || enemiesSpawned >= enemiesMax) return;
+
+ if (type==Enemy.ENEMY_FLOWER)
+ {
+ sprite = new FlowerEnemy(world, x*16+15, y*16+24);
+ }
+ else
+ {
+ sprite = new Enemy(world, x*16+8, y*16+15, dir, type, winged);
+ }
+
+ //correct for flipping
+ if(direction == -1){
+ sprite.x -= 14;
+ }
+
+ sprite.spriteTemplate = this;
+ world.addSprite(sprite);
+
+ if(!hasSpawned)
+ enemiesSpawned++;
+
+ hasSpawned = true;
+ }
+}
diff --git a/src/dk/itu/mario/engine/util/.DS_Store b/src/dk/itu/mario/engine/util/.DS_Store
new file mode 100644
index 0000000..b29de3e
Binary files /dev/null and b/src/dk/itu/mario/engine/util/.DS_Store differ
diff --git a/src/dk/itu/mario/engine/util/FileHandler.java b/src/dk/itu/mario/engine/util/FileHandler.java
new file mode 100644
index 0000000..3a99b60
--- /dev/null
+++ b/src/dk/itu/mario/engine/util/FileHandler.java
@@ -0,0 +1,31 @@
+package dk.itu.mario.engine.util;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+
+public class FileHandler {
+
+ public static String readFile(String fileName) {
+ String info = "";
+ try {
+ FileReader input = new FileReader(fileName);
+ BufferedReader bufRead = new BufferedReader(input);
+
+ String line;
+
+ line = bufRead.readLine();
+ info = line + "\n";
+
+ while (line != null) {
+ line = bufRead.readLine();
+ info += line + "\n";
+ }
+
+ bufRead.close();
+
+ } catch (Exception e) {
+ System.out.println("Unable to read from file: " + fileName +", returning empty String.");
+ }
+ return info;
+ }
+}
diff --git a/src/dk/itu/mario/engine/util/UIItem.java b/src/dk/itu/mario/engine/util/UIItem.java
new file mode 100644
index 0000000..c8aac1d
--- /dev/null
+++ b/src/dk/itu/mario/engine/util/UIItem.java
@@ -0,0 +1,22 @@
+package dk.itu.mario.engine.util;
+
+import java.awt.Graphics;
+
+public abstract class UIItem{
+
+ protected int x,y;
+ protected boolean selected;
+ protected int width,height;
+
+ public abstract void render(Graphics g);
+ public abstract void prev();
+ public abstract void next();
+
+ public void setSelected(){
+ selected = true;
+ }
+
+ public void setUnselected(){
+ selected = false;
+ }
+}
diff --git a/src/dk/itu/mario/engine/util/UITextArea.java b/src/dk/itu/mario/engine/util/UITextArea.java
new file mode 100644
index 0000000..8154abe
--- /dev/null
+++ b/src/dk/itu/mario/engine/util/UITextArea.java
@@ -0,0 +1,83 @@
+package dk.itu.mario.engine.util;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+
+public class UITextArea extends UIItem{
+
+ protected String text;
+ protected int width, height;
+ protected Font font;
+ protected ArrayList lines = new ArrayList();
+
+ public static boolean showBox = false;
+
+ public UITextArea(String text, Font font, int x, int y, int width){
+ this.text = text;
+ this.font = font;
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ }
+
+ public void next() {
+ }
+
+ public void prev() {
+ }
+
+ public void render(Graphics g) {
+ Graphics2D g2 = (Graphics2D)g;
+
+ g.setFont(font);
+
+ lines.clear();
+
+ Rectangle2D rec = font.getStringBounds(text,g2.getFontRenderContext());
+
+ String tempText = new String();
+
+ if(rec.getWidth()>width){
+ for(int i=0;i=width){
+ lines.add(tempText);
+ tempText = new String();
+ }
+ }
+
+ lines.add(tempText);
+ }
+ else{
+ lines.add(text);
+ }
+
+ for(int j=0;j h1 ? 1 : 0;
+ if (h0 == oh)
+ s = 1 - s;
+ s += distant ? 2 : 0;
+ level.setBlock(x, y, (byte) (s + 8));
+ }
+ }
+ }
+ break;
+ }
+ case LevelInterface.TYPE_UNDERGROUND: {
+ if (distant) {
+ int tt = 0;
+ for (int x = 0; x < width; x++) {
+ if (random.nextDouble() < 0.75)
+ tt = 1 - tt;
+ for (int y = 0; y < height; y++) {
+ int t = tt;
+ int yy = y - 2;
+ if (yy < 0 || yy > 4) {
+ yy = 2;
+ t = 0;
+ }
+ level.setBlock(x, y, (byte) (4 + t + (3 + yy) * 8));
+ }
+ }
+ } else {
+ for (int x = 0; x < width; x++) {
+ for (int y = 0; y < height; y++) {
+ int t = x % 2;
+ int yy = y - 1;
+ if (yy < 0 || yy > 7) {
+ yy = 7;
+ t = 0;
+ }
+ if (t == 0 && yy > 1 && yy < 5) {
+ t = -1;
+ yy = 0;
+ }
+ level.setBlock(x, y, (byte) (6 + t + (yy) * 8));
+ }
+ }
+ }
+ break;
+ }
+ case LevelInterface.TYPE_CASTLE: {
+ if (distant) {
+ for (int x = 0; x < width; x++) {
+ for (int y = 0; y < height; y++) {
+ int t = x % 2;
+ int yy = y - 1;
+ if (yy > 2 && yy < 5) {
+ yy = 2;
+ } else if (yy >= 5) {
+ yy -= 2;
+ }
+ if (yy < 0) {
+ t = 0;
+ yy = 5;
+ } else if (yy > 4) {
+ t = 1;
+ yy = 5;
+ } else if (t < 1 && yy == 3) {
+ t = 0;
+ yy = 3;
+ } else if (t < 1 && yy > 0 && yy < 3) {
+ t = 0;
+ yy = 2;
+ }
+ level.setBlock(x, y, (byte) (1 + t + (yy + 4) * 8));
+ }
+ }
+ } else {
+ for (int x = 0; x < width; x++) {
+ for (int y = 0; y < height; y++) {
+ int t = x % 3;
+ int yy = y - 1;
+ if (yy > 2 && yy < 5) {
+ yy = 2;
+ } else if (yy >= 5) {
+ yy -= 2;
+ }
+ if (yy < 0) {
+ t = 1;
+ yy = 5;
+ } else if (yy > 4) {
+ t = 2;
+ yy = 5;
+ } else if (t < 2 && yy == 4) {
+ t = 2;
+ yy = 4;
+ } else if (t < 2 && yy > 0 && yy < 4) {
+ t = 4;
+ yy = -3;
+ }
+ level.setBlock(x, y, (byte) (1 + t + (yy + 3) * 8));
+ }
+ }
+ }
+ break;
+ }
+ }
+ return level;
+ }
+}
diff --git a/src/dk/itu/mario/level/CustomizedLevel.java b/src/dk/itu/mario/level/CustomizedLevel.java
new file mode 100644
index 0000000..19f0aa3
--- /dev/null
+++ b/src/dk/itu/mario/level/CustomizedLevel.java
@@ -0,0 +1,574 @@
+package dk.itu.mario.level;
+
+import java.util.Random;
+
+import dk.itu.mario.MarioInterface.Constraints;
+import dk.itu.mario.MarioInterface.GamePlay;
+import dk.itu.mario.MarioInterface.LevelInterface;
+import dk.itu.mario.engine.DataRecorder;
+import dk.itu.mario.engine.sprites.Enemy;
+import dk.itu.mario.engine.sprites.SpriteTemplate;
+
+public class CustomizedLevel extends Level implements LevelInterface {
+ private static final int ODDS_STRAIGHT = 0;
+ private static final int ODDS_HILL_STRAIGHT = 1;
+ private static final int ODDS_TUBES = 2;
+ private static final int ODDS_JUMP = 3;
+ private static final int ODDS_CANNONS = 4;
+ private static final int JumpingThreshold = 3;
+
+ private DataRecorder dataRecorder;
+ private GamePlay playerM;
+ private int[] odds = new int[5];
+ private int totalOdds;
+ private int difficulty;
+ private int type;
+ private int gaps;
+ private int turtles;
+ private int coins;
+ private Random random;
+
+ public CustomizedLevel(int width, int height, long seed, int difficulty,
+ int type, GamePlay playerMetrics, DataRecorder dataRecorder) {
+ super(width, height);
+ System.out.println("Generating level based on previous GamePlay AND DataRecorder metrics.");
+ this.dataRecorder = dataRecorder;
+ this.playerM = playerMetrics;
+ create(seed, difficulty, type);
+ }
+
+ public CustomizedLevel(int width, int height, long seed, int difficulty,
+ int type, GamePlay playerMetrics) {
+ super(width, height);
+ System.out.println("Generating level based on previous GamePlay metrics ONLY.");
+ this.playerM = playerMetrics;
+ create(seed, difficulty, type);
+ }
+
+ public void create(long seed, int difficulty, int type) {
+ if (dataRecorder == DataRecorder.BLANK_RECORD) {
+ System.out.println("DataRecorder record is BLANK - using GamePlay metrics only.");
+ }
+ this.type = type;
+ this.difficulty = difficulty;
+ odds[ODDS_STRAIGHT] = 30;
+ odds[ODDS_HILL_STRAIGHT] = 20;
+ odds[ODDS_TUBES] = 2 + 2 * difficulty;
+ int jumpDifficulty = 1;
+ // adapt the game so that it has a number of gaps proportional to the
+ // number of jumps the player made in the test level. The more the
+ // jumps,
+ // the more the gaps.
+ if (playerM.jumpsNumber > JumpingThreshold)
+ jumpDifficulty = 2;
+ odds[ODDS_JUMP] = jumpDifficulty;
+ odds[ODDS_CANNONS] = -10 + 5 * difficulty;
+
+ if (type != LevelInterface.TYPE_OVERGROUND) {
+ odds[ODDS_HILL_STRAIGHT] = 0;
+ }
+
+ for (int i = 0; i < odds.length; i++) {
+ // failsafe (no negative odds)
+ if (odds[i] < 0) {
+ odds[i] = 0;
+ }
+
+ totalOdds += odds[i];
+ odds[i] = totalOdds - odds[i];
+ }
+
+ random = new Random(seed);
+
+ // create the start location
+ int length = 0;
+ length += buildStraight(0, getWidth(), true);
+
+ // create all of the medium sections
+ while (length < getWidth() - 64) {
+ length += buildZone(length, getWidth() - length);
+ }
+
+ // set the end piece
+ int floor = height - 1 - random.nextInt(4);
+
+ // create the exit
+ xExit = length + 8;
+ yExit = floor;
+
+ for (int x = length; x < getWidth(); x++) {
+ for (int y = 0; y < height; y++) {
+ if (y >= floor) {
+ setBlock(x, y, Level.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 int buildZone(int x, int maxLength) {
+ int t = random.nextInt(totalOdds);
+ int type = 0;
+
+ for (int i = 0; i < odds.length; i++) {
+ if (odds[ODDS_JUMP] <= t * 2 + 30) {
+ type = ODDS_JUMP;
+ break;
+ }
+ if (odds[i] <= t) {
+ type = i;
+ }
+ }
+
+ switch (type) {
+ case ODDS_STRAIGHT:
+ return buildStraight(x, maxLength, false);
+ case ODDS_HILL_STRAIGHT:
+ return buildHillStraight(x, maxLength);
+ case ODDS_TUBES:
+ return buildTubes(x, maxLength);
+ case ODDS_JUMP:
+ if (gaps < Constraints.gaps)
+ return buildJump(x, maxLength);
+ else
+ return buildStraight(x, maxLength, false);
+ case ODDS_CANNONS:
+ return buildCannons(x, maxLength);
+ }
+ return 0;
+ }
+
+ private int buildJump(int xo, int maxLength) {
+ gaps++;
+ // jl: jump length
+ // js: the number of blocks that are available at either side for free
+ int js = random.nextInt(4) + 2;
+ int jl = random.nextInt(2) + 2;
+ int length = js * 2 + jl;
+
+ boolean hasStairs = random.nextInt(3) == 0;
+
+ int floor = height - 1 - random.nextInt(4);
+ // run for the from the start x position, for the whole length
+ for (int x = xo; x < xo + length; x++) {
+ if (x < xo + js || x > xo + length - js - 1) {
+ // run for all y's since we need to paint blocks upward
+ for (int y = 0; y < height; y++) { // paint ground up until the
+ // floor
+ if (y >= floor) {
+ setBlock(x, y, GROUND);
+ }
+ // if it is above ground, start making stairs of rocks
+ else if (hasStairs) { // LEFT SIDE
+ if (x < xo + js) { // we need to max it out and level
+ // because it wont
+ // paint ground correctly unless two bricks are side
+ // by side
+ if (y >= floor - (x - xo) + 1) {
+ setBlock(x, y, ROCK);
+ }
+ } else { // RIGHT SIDE
+ if (y >= floor - ((xo + length) - x) + 2) {
+ setBlock(x, y, ROCK);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return length;
+ }
+
+ private int buildCannons(int xo, int maxLength) {
+ int length = random.nextInt(10) + 2;
+ if (length > maxLength)
+ length = maxLength;
+
+ int floor = height - 1 - random.nextInt(4);
+ int xCannon = xo + 1 + random.nextInt(4);
+ for (int x = xo; x < xo + length; x++) {
+ if (x > xCannon) {
+ xCannon += 2 + random.nextInt(4);
+ }
+ if (xCannon == xo + length - 1)
+ xCannon += 10;
+ int cannonHeight = floor - random.nextInt(4) - 1;
+
+ for (int y = 0; y < height; y++) {
+ if (y >= floor) {
+ setBlock(x, y, (byte) (1 + 9 * 16));
+ } else {
+ if (x == xCannon && y >= cannonHeight) {
+ if (y == cannonHeight) {
+ setBlock(x, y, (byte) (14 + 0 * 16));
+ } else if (y == cannonHeight + 1) {
+ setBlock(x, y, (byte) (14 + 1 * 16));
+ } else {
+ setBlock(x, y, (byte) (14 + 2 * 16));
+ }
+ }
+ }
+ }
+ }
+
+ return length;
+ }
+
+ private int buildHillStraight(int xo, int maxLength) {
+ int length = random.nextInt(10) + 10;
+ if (length > maxLength)
+ length = maxLength;
+
+ int floor = height - 1 - random.nextInt(4);
+ for (int x = xo; x < xo + length; x++) {
+ for (int y = 0; y < height; y++) {
+ if (y >= floor) {
+ setBlock(x, y, Level.GROUND);
+ }
+ }
+ }
+
+ addEnemyLine(xo + 1, xo + length - 1, floor - 1);
+
+ int h = floor;
+
+ boolean keepGoing = true;
+
+ boolean[] occupied = new boolean[length];
+ while (keepGoing) {
+ h = h - 2 - random.nextInt(3);
+
+ if (h <= 0) {
+ keepGoing = false;
+ } else {
+ int l = random.nextInt(5) + 3;
+ int xxo = random.nextInt(length - l - 2) + xo + 1;
+
+ if (occupied[xxo - xo] || occupied[xxo - xo + l]
+ || occupied[xxo - xo - 1] || occupied[xxo - xo + l + 1]) {
+ keepGoing = false;
+ } else {
+ occupied[xxo - xo] = true;
+ occupied[xxo - xo + l] = true;
+ addEnemyLine(xxo, xxo + l, h - 1);
+ if (random.nextInt(4) == 0) {
+ decorate(xxo - 1, xxo + l + 1, h);
+ keepGoing = false;
+ }
+ for (int x = xxo; x < xxo + l; x++) {
+ for (int y = h; y < floor; y++) {
+ int xx = 5;
+ if (x == xxo)
+ xx = 4;
+ if (x == xxo + l - 1)
+ xx = 6;
+ int yy = 9;
+ if (y == h)
+ yy = 8;
+
+ if (getBlock(x, y) == 0) {
+ setBlock(x, y, (byte) (xx + yy * 16));
+ } else {
+ if (getBlock(x, y) == Level.HILL_TOP_LEFT)
+ setBlock(x, y, Level.HILL_TOP_LEFT_IN);
+ if (getBlock(x, y) == Level.HILL_TOP_RIGHT)
+ setBlock(x, y, Level.HILL_TOP_RIGHT_IN);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return length;
+ }
+
+ private void addEnemyLine(int x0, int x1, int y) {
+ for (int x = x0; x < x1; x++) {
+ if (random.nextInt(50) < 25) {
+ int type = random.nextInt(4);
+
+ type = random.nextInt(3);
+ if (turtles < Constraints.turtels) {
+ if (type == Enemy.ENEMY_GREEN_KOOPA
+ || type == Enemy.ENEMY_RED_KOOPA) {
+ turtles++;
+ setSpriteTemplate(x, y,
+ new SpriteTemplate(type,
+ random.nextInt(35) < difficulty));
+ } else {
+ setSpriteTemplate(x, y,
+ new SpriteTemplate(type,
+ random.nextInt(35) < difficulty));
+ }
+ } else {
+ setSpriteTemplate(
+ x,
+ y,
+ new SpriteTemplate(Enemy.ENEMY_GOOMBA, random
+ .nextInt(35) < difficulty));
+ }
+ }
+ }
+ }
+
+ private int buildTubes(int xo, int maxLength) {
+ int length = random.nextInt(10) + 5;
+ if (length > maxLength)
+ length = maxLength;
+
+ int floor = height - 1 - random.nextInt(4);
+ int xTube = xo + 1 + random.nextInt(4);
+ int tubeHeight = floor - random.nextInt(2) - 2;
+ for (int x = xo; x < xo + length; x++) {
+ if (x > xTube + 1) {
+ xTube += 3 + random.nextInt(4);
+ tubeHeight = floor - random.nextInt(2) - 2;
+ }
+ if (xTube >= xo + length - 2)
+ xTube += 10;
+
+ if (x == xTube && random.nextInt(11) < difficulty + 1) {
+ setSpriteTemplate(x, tubeHeight, new SpriteTemplate(
+ Enemy.ENEMY_FLOWER, false));
+ }
+
+ for (int y = 0; y < height; y++) {
+ if (y >= floor) {
+ setBlock(x, y, (byte) (1 + 9 * 16));
+
+ } else {
+ if ((x == xTube || x == xTube + 1) && y >= tubeHeight) {
+ int xPic = 10 + x - xTube;
+
+ if (y == tubeHeight) {
+ // tube top
+ setBlock(x, y, (byte) (xPic + 0 * 16));
+ } else {
+ // tube side
+ setBlock(x, y, (byte) (xPic + 1 * 16));
+ }
+ }
+ }
+ }
+ }
+
+ return length;
+ }
+
+ private int buildStraight(int xo, int maxLength, boolean safe) {
+ int length = random.nextInt(10) + 2;
+
+ if (safe)
+ length = 10 + random.nextInt(5);
+
+ if (length > maxLength)
+ length = maxLength;
+
+ int floor = height - 1 - random.nextInt(4);
+
+ // 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, Level.GROUND);
+ }
+ }
+ }
+
+ if (!safe) {
+ if (length > 5) {
+ decorate(xo, xo + length, floor);
+ }
+ }
+
+ return length;
+ }
+
+ private void decorate(int xStart, int xLength, int floor) {
+ // if its at the very top, just return
+ if (floor < 1)
+ return;
+ boolean rocks = true;
+
+ // add an enemy line above the box
+ addEnemyLine(xStart + 1, xLength - 1, floor - 1);
+
+ int s = random.nextInt(4);
+ int e = random.nextInt(4);
+
+ if (floor - 2 > 0) {
+ if ((xLength - 1 - e) - (xStart + 1 + s) > 1) {
+ for (int x = xStart + 1 + s; x < xLength - 1 - e; x++) {
+ setBlock(x, floor - 2, (byte) (2 + 2 * 16));
+ }
+ }
+ }
+
+ s = random.nextInt(4);
+ e = random.nextInt(4);
+
+ if (floor - 4 > 0) {
+ if ((xLength - 1 - e) - (xStart + 1 + s) > 2) {
+ for (int x = xStart + 1 + s; x < xLength - 1 - e; x++) {
+ if (rocks) {
+ if (x != xStart + 1 && x != xLength - 2
+ && random.nextInt(2) == 0) {
+ if (random.nextInt(2) == 0) {
+ setBlock(x, floor - 4, BLOCK_POWERUP);
+ } else {
+ if (coins < Constraints.coinBlocks) {
+ coins++;
+ setBlock(x, floor - 4, BLOCK_COIN);
+ } else {
+ setBlock(x, floor - 4, BLOCK_EMPTY);
+ }
+ }
+ } else if (random.nextInt(4) == 0) {
+ if (random.nextInt(4) == 0) {
+ setBlock(x, floor - 4, (byte) (2 + 1 * 16));
+ } else {
+ setBlock(x, floor - 4, (byte) (1 + 1 * 16));
+ }
+ } else {
+ setBlock(x, floor - 4, BLOCK_EMPTY);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ 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 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));
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/dk/itu/mario/level/FitnessEvaluator.java b/src/dk/itu/mario/level/FitnessEvaluator.java
new file mode 100644
index 0000000..495b4d3
--- /dev/null
+++ b/src/dk/itu/mario/level/FitnessEvaluator.java
@@ -0,0 +1,30 @@
+package dk.itu.mario.level;
+
+import java.util.List;
+
+import dk.itu.mario.level.grammar.LevelParseTree;
+
+public class FitnessEvaluator {
+ public static boolean isFit(LevelParseTree parseTree, PlayerProfile playerProfile, LevelArchetype levelArchetype) {
+ System.out.println("Evaluating LevelParseTree for fitness");
+ List levelTemplate = parseTree.getLevelTemplate();
+ //a good level has 8-24 components, plus some additional complexity depending on the player's skill level
+
+ if (levelTemplate.size() < 8) {
+ return false;
+ }
+
+ if (levelTemplate.size() > 24) {
+ return false;
+ }
+
+ for (LevelComponent lc : levelTemplate) {
+ if (!playerProfile.isEnabled(lc.getType())) {
+ System.out.println("Level is not fit: " + lc.getType() + " is not enabled.");
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/src/dk/itu/mario/level/GrammarTuner.java b/src/dk/itu/mario/level/GrammarTuner.java
new file mode 100644
index 0000000..d2b1e33
--- /dev/null
+++ b/src/dk/itu/mario/level/GrammarTuner.java
@@ -0,0 +1,54 @@
+package dk.itu.mario.level;
+
+import java.io.File;
+import org.drools.KnowledgeBase;
+import org.drools.KnowledgeBaseFactory;
+import org.drools.builder.KnowledgeBuilder;
+import org.drools.builder.KnowledgeBuilderFactory;
+import org.drools.builder.ResourceType;
+import org.drools.io.ResourceFactory;
+import org.drools.runtime.StatefulKnowledgeSession;
+
+import dk.itu.mario.level.PlayerProfile;
+import dk.itu.mario.level.grammar.LevelGrammar;
+
+public class GrammarTuner {
+ /**
+ * @param args
+ */
+ public static void tune(LevelGrammar levelGrammar, PlayerProfile playerProfile, LevelArchetype archetype) {
+ KnowledgeBase knowledgeBase = createKnowledgeBase();
+
+ try {
+ StatefulKnowledgeSession session = knowledgeBase
+ .newStatefulKnowledgeSession();
+ session.insert(playerProfile);
+ session.fireAllRules();
+ } catch (RuntimeException rte) {
+ System.out
+ .println("Unable to initialize StatefulKnowledgeSession for Rete grammar tuning - all challenges will be enabled! Exception: ");
+ rte.printStackTrace(System.out);
+ }
+ }
+
+ private static KnowledgeBase createKnowledgeBase() {
+ KnowledgeBuilder builder = KnowledgeBuilderFactory
+ .newKnowledgeBuilder();
+
+ File accountRules = new File("rules/LevelTunerRules.drl");
+ builder.add(ResourceFactory.newFileResource(accountRules),
+ ResourceType.DRL);
+
+ if (builder.hasErrors()) {
+ throw new RuntimeException(builder.getErrors().toString());
+ }
+
+ KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase();
+
+ // Add to Knowledge Base packages (rules from the drl file).
+ knowledgeBase.addKnowledgePackages(builder.getKnowledgePackages());
+
+ return knowledgeBase;
+ }
+
+}
\ No newline at end of file
diff --git a/src/dk/itu/mario/level/Level.java b/src/dk/itu/mario/level/Level.java
new file mode 100644
index 0000000..ba01a01
--- /dev/null
+++ b/src/dk/itu/mario/level/Level.java
@@ -0,0 +1,235 @@
+package dk.itu.mario.level;
+
+import java.io.*;
+
+import dk.itu.mario.MarioInterface.LevelInterface;
+import dk.itu.mario.engine.sprites.SpriteTemplate;
+
+public class Level implements LevelInterface {
+
+ protected static final byte BLOCK_EMPTY = (byte) (0 + 1 * 16);
+ protected static final byte BLOCK_POWERUP = (byte) (4 + 2 + 1 * 16);
+ protected static final byte BLOCK_COIN = (byte) (4 + 1 + 1 * 16);
+ protected static final byte GROUND = (byte) (1 + 9 * 16);
+ protected static final byte ROCK = (byte) (9 + 0 * 16);
+ protected static final byte COIN = (byte) (2 + 2 * 16);
+
+ protected static final byte CANNON_TOP = (byte) (14 + 0 * 16);
+ protected static final byte CANNON_MIDDLE = (byte) (14 + 1 * 16);
+ protected static final byte CANNON_BOTTOM = (byte) (14 + 2 * 16);
+
+ protected static final byte LEFT_GRASS_EDGE = (byte) (0 + 9 * 16);
+ protected static final byte RIGHT_GRASS_EDGE = (byte) (2 + 9 * 16);
+ protected static final byte RIGHT_UP_GRASS_EDGE = (byte) (2 + 8 * 16);
+ protected static final byte LEFT_UP_GRASS_EDGE = (byte) (0 + 8 * 16);
+ protected static final byte LEFT_POCKET_GRASS = (byte) (3 + 9 * 16);
+ protected static final byte RIGHT_POCKET_GRASS = (byte) (3 + 8 * 16);
+
+ protected static final byte HILL_FILL = (byte) (5 + 9 * 16);
+ protected static final byte HILL_LEFT = (byte) (4 + 9 * 16);
+ protected static final byte HILL_RIGHT = (byte) (6 + 9 * 16);
+ protected static final byte HILL_TOP = (byte) (5 + 8 * 16);
+ protected static final byte HILL_TOP_LEFT = (byte) (4 + 8 * 16);
+ protected static final byte HILL_TOP_RIGHT = (byte) (6 + 8 * 16);
+
+ protected static final byte HILL_TOP_LEFT_IN = (byte) (4 + 11 * 16);
+ protected static final byte HILL_TOP_RIGHT_IN = (byte) (6 + 11 * 16);
+
+ protected static final byte TUBE_TOP_LEFT = (byte) (10 + 0 * 16);
+ protected static final byte TUBE_TOP_RIGHT = (byte) (11 + 0 * 16);
+
+ protected static final byte TUBE_SIDE_LEFT = (byte) (10 + 1 * 16);
+ protected static final byte TUBE_SIDE_RIGHT = (byte) (11 + 1 * 16);
+
+ // The level's width and height
+ protected int width;
+ protected int height;
+
+ // This map of WIDTH * HEIGHT that contains the level's design
+ private byte[][] map;
+
+ // This is a map of WIDTH * HEIGHT that contains the placement and type
+ // enemies
+ private SpriteTemplate[][] spriteTemplates;
+
+ // These are the place of the end of the level
+ protected int xExit;
+ protected int yExit;
+
+ public Level() {
+
+ }
+
+ public Level(int width, int height) {
+ this.width = width;
+ this.height = height;
+
+ xExit = 10;
+ yExit = 10;
+ map = new byte[width][height];
+ spriteTemplates = new SpriteTemplate[width][height];
+ }
+
+ public static void loadBehaviors(DataInputStream dis) throws IOException {
+ dis.readFully(Level.TILE_BEHAVIORS);
+ }
+
+ public static void saveBehaviors(DataOutputStream dos) throws IOException {
+ dos.write(Level.TILE_BEHAVIORS);
+ }
+
+ /**
+ * Clone the level data so that we can load it when Mario die
+ */
+ public Level clone() throws CloneNotSupportedException {
+
+ Level clone = new Level(width, height);
+
+ clone.map = new byte[width][height];
+ clone.spriteTemplates = new SpriteTemplate[width][height];
+ clone.xExit = xExit;
+ clone.yExit = yExit;
+
+ for (int i = 0; i < map.length; i++)
+ for (int j = 0; j < map[i].length; j++) {
+ clone.map[i][j] = map[i][j];
+ clone.spriteTemplates[i][j] = spriteTemplates[i][j];
+ }
+
+ return clone;
+
+ }
+
+ public void tick() {
+ }
+
+ public byte getBlockCapped(int x, int y) {
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+ if (x >= width)
+ x = width - 1;
+ if (y >= height)
+ y = height - 1;
+ return map[x][y];
+ }
+
+ public byte getBlock(int x, int y) {
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ return 0;
+ if (x >= width)
+ x = width - 1;
+ if (y >= height)
+ y = height - 1;
+ return map[x][y];
+ }
+
+ public void setBlock(int x, int y, byte b) {
+ if (x < 0)
+ return;
+ if (y < 0)
+ return;
+ if (x >= width)
+ return;
+ if (y >= height)
+ return;
+ map[x][y] = b;
+ }
+
+ public boolean isBlocking(int x, int y, float xa, float ya) {
+ byte block = getBlock(x, y);
+
+ boolean blocking = ((TILE_BEHAVIORS[block & 0xff]) & BIT_BLOCK_ALL) > 0;
+ blocking |= (ya > 0)
+ && ((TILE_BEHAVIORS[block & 0xff]) & BIT_BLOCK_UPPER) > 0;
+ blocking |= (ya < 0)
+ && ((TILE_BEHAVIORS[block & 0xff]) & BIT_BLOCK_LOWER) > 0;
+
+ return blocking;
+ }
+
+ public SpriteTemplate getSpriteTemplate(int x, int y) {
+ if (x < 0)
+ return null;
+ if (y < 0)
+ return null;
+ if (x >= width)
+ return null;
+ if (y >= height)
+ return null;
+ return spriteTemplates[x][y];
+ }
+
+ public void setSpriteTemplate(int x, int y, SpriteTemplate spriteTemplate) {
+ if (x < 0)
+ return;
+ if (y < 0)
+ return;
+ if (x >= width)
+ return;
+ if (y >= height)
+ return;
+ spriteTemplates[x][y] = spriteTemplate;
+ }
+
+ public SpriteTemplate[][] getSpriteTemplate() {
+ return this.spriteTemplates;
+ }
+
+ public void resetSpriteTemplate() {
+ for (int i = 0; i < spriteTemplates.length; i++) {
+ for (int j = 0; j < spriteTemplates[i].length; j++) {
+
+ SpriteTemplate st = spriteTemplates[i][j];
+ if (st != null)
+ st.isDead = false;
+ }
+ }
+ }
+
+ public void print(byte[][] array) {
+ for (int i = 0; i < array.length; i++) {
+ for (int j = 0; j < array[i].length; j++) {
+ System.out.print(array[i][j]);
+ }
+ System.out.println();
+ }
+ }
+
+ public byte[][] getMap() {
+ return map;
+ }
+
+ public SpriteTemplate[][] getSpriteTemplates() {
+ return spriteTemplates;
+ }
+
+ public int getxExit() {
+ // TODO Auto-generated method stub
+ return xExit;
+ }
+
+ public int getyExit() {
+ // TODO Auto-generated method stub
+ return yExit;
+ }
+
+ public int getWidth() {
+ // TODO Auto-generated method stub
+ return width;
+ }
+
+ public int getHeight() {
+ // TODO Auto-generated method stub
+ return height;
+ }
+
+ public String getName() {
+ // TODO Auto-generated method stub
+ return "";
+ }
+
+}
diff --git a/src/dk/itu/mario/level/LevelArchetype.java b/src/dk/itu/mario/level/LevelArchetype.java
new file mode 100644
index 0000000..ea0415d
--- /dev/null
+++ b/src/dk/itu/mario/level/LevelArchetype.java
@@ -0,0 +1,53 @@
+package dk.itu.mario.level;
+
+import dk.itu.mario.MarioInterface.LevelInterface;
+
+public class LevelArchetype {
+ public static final int MAX_DIFFICULTY_LEVEL = 10;
+
+ // The canonical Super Mario level types.
+ public enum TYPE {
+ OVERGROUND, UNDERGROUND, CASTLE
+ }
+
+ private int difficultyLevel;
+ private TYPE type;
+
+ public LevelArchetype(TYPE type, int difficultyLevel) {
+ if (difficultyLevel < 1 || difficultyLevel > MAX_DIFFICULTY_LEVEL) {
+ throw new IllegalArgumentException(
+ "DifficultyLevel must be in the range [1.."
+ + MAX_DIFFICULTY_LEVEL + "]");
+ }
+ this.difficultyLevel = difficultyLevel;
+ this.type = type;
+ }
+
+ public int getDifficultyLevel() {
+ return difficultyLevel;
+ }
+
+ public TYPE getType() {
+ return type;
+ }
+
+ public int getTypeInt() {
+ switch (type) {
+ case CASTLE:
+ return LevelInterface.TYPE_CASTLE;
+ case OVERGROUND:
+ return LevelInterface.TYPE_OVERGROUND;
+ case UNDERGROUND:
+ return LevelInterface.TYPE_UNDERGROUND;
+ default:
+ throw new UnsupportedOperationException("LevelArchetype.TYPE "
+ + type + " does not have a known integer value");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return type + " (Difficulty " + difficultyLevel + "/"
+ + MAX_DIFFICULTY_LEVEL + ")";
+ }
+}
diff --git a/src/dk/itu/mario/level/LevelComponent.java b/src/dk/itu/mario/level/LevelComponent.java
new file mode 100644
index 0000000..705d832
--- /dev/null
+++ b/src/dk/itu/mario/level/LevelComponent.java
@@ -0,0 +1,61 @@
+package dk.itu.mario.level;
+
+public class LevelComponent {
+ public enum TYPE {
+ BLOCKS,
+ BOWLING_ALLEY,
+ CANNON_LINE,
+ COIN_DIVE,
+ FLAT_HI,
+ FLAT_LO,
+ FLAT_SAFE,
+ HI_LO,
+ HI_PATH,
+ LEMMING_TRAP,
+ LEVEL_SEGMENT,
+ LEVEL,
+ LO_HI,
+ LO_PATH,
+ MAZE,
+ PIPE_JUMP,
+ PLATFORM_JUMP,
+ POWER_UP,
+ SINGLE_PIT,
+ CHALLENGE
+ };
+
+ public enum MazeLevel {
+ BOT, MID, TOP
+ }
+
+ public enum PlatformLevel {
+ BOT, MID_D, MID_U, TOP
+ }
+
+ private TYPE type;
+ private int start;
+ private int end;
+
+ public LevelComponent(TYPE type, int start, int end) {
+ this.type = type;
+ this.end = end;
+ this.start = start;
+ }
+
+ public TYPE getType() {
+ return type;
+ }
+
+ public int getStart() {
+ return start;
+ }
+
+ public int getEnd() {
+ return end;
+ }
+
+ @Override
+ public String toString() {
+ return type + "[" + start + ".." + end + "]";
+ }
+}
diff --git a/src/dk/itu/mario/level/LevelEditView.java b/src/dk/itu/mario/level/LevelEditView.java
new file mode 100644
index 0000000..bc9289c
--- /dev/null
+++ b/src/dk/itu/mario/level/LevelEditView.java
@@ -0,0 +1,130 @@
+//package dk.itu.mario.level;
+//
+//import java.awt.Color;
+//import java.awt.Dimension;
+//import java.awt.Graphics;
+//import java.awt.event.MouseEvent;
+//import java.awt.event.MouseListener;
+//import java.awt.event.MouseMotionListener;
+//
+//import javax.swing.*;
+//import com.mojang.mario.*;
+//import com.mojang.mario.level.*;
+//
+//import dk.itu.mario.engine.Art;
+//import dk.itu.mario.engine.LevelRenderer;
+//
+//
+//
+//public class LevelEditView extends JComponent implements MouseListener, MouseMotionListener
+//{
+// private static final long serialVersionUID = -7696446733303717142L;
+//
+// private LevelRenderer levelRenderer;
+// private Level level;
+//
+// private int xTile = -1;
+// private int yTile = -1;
+// private TilePicker tilePicker;
+//
+// public LevelEditView(TilePicker tilePicker)
+// {
+// this.tilePicker = tilePicker;
+// level = new Level(256, 15);
+// Dimension size = new Dimension(level.width * 16, level.height * 16);
+// setPreferredSize(size);
+// setMinimumSize(size);
+// setMaximumSize(size);
+//
+// addMouseListener(this);
+// addMouseMotionListener(this);
+// }
+//
+// public void setLevel(Level level)
+// {
+// this.level = level;
+// Dimension size = new Dimension(level.width * 16, level.height * 16);
+// setPreferredSize(size);
+// setMinimumSize(size);
+// setMaximumSize(size);
+// repaint();
+// levelRenderer.setLevel(level);
+// }
+//
+// public Level getLevel()
+// {
+// return level;
+// }
+//
+// public void addNotify()
+// {
+// super.addNotify();
+// Art.init(getGraphicsConfiguration(), null);
+// levelRenderer = new LevelRenderer(level, getGraphicsConfiguration(), level.width * 16, level.height * 16);
+// levelRenderer.renderBehaviors = true;
+// }
+//
+// public void paintComponent(Graphics g)
+// {
+// g.setColor(new Color(0x8090ff));
+// g.fillRect(0, 0, level.width * 16, level.height * 16);
+// levelRenderer.render(g, 0, 0);
+// g.setColor(Color.BLACK);
+// g.drawRect(xTile * 16 - 1, yTile * 16 - 1, 17, 17);
+// }
+//
+// public void mouseClicked(MouseEvent e)
+// {
+// }
+//
+// public void mouseEntered(MouseEvent e)
+// {
+// }
+//
+// public void mouseExited(MouseEvent e)
+// {
+// xTile = -1;
+// yTile = -1;
+// repaint();
+// }
+//
+// public void mousePressed(MouseEvent e)
+// {
+// xTile = e.getX() / 16;
+// yTile = e.getY() / 16;
+//
+// if (e.getButton() == 3)
+// {
+// tilePicker.setPickedTile(level.getBlock(xTile, yTile));
+// }
+// else
+// {
+// level.setBlock(xTile, yTile, tilePicker.pickedTile);
+// levelRenderer.repaint(xTile - 1, yTile - 1, 3, 3);
+//
+// repaint();
+// }
+// }
+//
+// public void mouseReleased(MouseEvent e)
+// {
+// }
+//
+// public void mouseDragged(MouseEvent e)
+// {
+// xTile = e.getX() / 16;
+// yTile = e.getY() / 16;
+//
+// level.setBlock(xTile, yTile, tilePicker.pickedTile);
+// levelRenderer.repaint(xTile - 1, yTile - 1, 3, 3);
+//
+// repaint();
+// }
+//
+// public void mouseMoved(MouseEvent e)
+// {
+// xTile = e.getX() / 16;
+// yTile = e.getY() / 16;
+// repaint();
+// }
+//}
diff --git a/src/dk/itu/mario/level/MyLevel.java b/src/dk/itu/mario/level/MyLevel.java
new file mode 100644
index 0000000..e2012d6
--- /dev/null
+++ b/src/dk/itu/mario/level/MyLevel.java
@@ -0,0 +1,522 @@
+package dk.itu.mario.level;
+
+import java.util.Random;
+
+import dk.itu.mario.MarioInterface.GamePlay;
+import dk.itu.mario.MarioInterface.LevelInterface;
+import dk.itu.mario.engine.sprites.Enemy;
+import dk.itu.mario.engine.sprites.SpriteTemplate;
+
+public class MyLevel extends Level {
+ // Store information about the level
+ public int ENEMIES = 0; // the number of enemies the level contains
+ public int BLOCKS_EMPTY = 0; // the number of empty blocks
+ public int BLOCKS_COINS = 0; // the number of coin blocks
+ public int BLOCKS_POWER = 0; // the number of power blocks
+ public int COINS = 0; // These are the coins in boxes that Mario collect
+
+ public static long lastSeed;
+
+ Random random;
+
+ private int difficulty;
+ private int type;
+
+ public MyLevel(int width, int height) {
+ super(width, height);
+ }
+
+ public MyLevel(int width, int height, long seed, int difficulty, int type,
+ GamePlay playerMetrics) {
+ this(width, height);
+ creat(seed, difficulty, type);
+ }
+
+ public void creat(long seed, int difficulty, int type) {
+ this.type = type;
+ this.difficulty = difficulty;
+
+ lastSeed = seed;
+ random = new Random(seed);
+
+ // create the start location
+ int length = 0;
+ length += buildStraight(0, width, true);
+
+ // create all of the medium sections
+ while (length < width - 64) {
+ length += buildStraight(length, width - length, false);
+ length += buildStraight(length, width - length, false);
+ length += buildHillStraight(length, width - length);
+ length += buildJump(length, width - length);
+ length += buildTubes(length, width - length);
+ length += buildCannons(length, width - length);
+ }
+
+ // set the end piece
+ int floor = height - 1 - random.nextInt(4);
+
+ xExit = length + 8;
+ yExit = 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 int buildJump(int xo, int maxLength) {
+ // jl: jump length
+ // js: the number of blocks that are available at either side for free
+ int js = random.nextInt(4) + 2;
+ int jl = random.nextInt(2) + 2;
+ int length = js * 2 + jl;
+
+ boolean hasStairs = random.nextInt(3) == 0;
+
+ int floor = height - 1 - random.nextInt(4);
+ // run from the start x position, for the whole length
+ for (int x = xo; x < xo + length; x++) {
+ if (x < xo + js || x > xo + length - js - 1) {
+ // run for all y's since we need to paint blocks upward
+ for (int y = 0; y < height; y++) { // paint ground up until the
+ // floor
+ if (y >= floor) {
+ setBlock(x, y, GROUND);
+ }
+ // if it is above ground, start making stairs of rocks
+ else if (hasStairs) { // LEFT SIDE
+ if (x < xo + js) { // we need to max it out and level
+ // because it wont
+ // paint ground correctly unless two
+ // bricks are side by side
+ if (y >= floor - (x - xo) + 1) {
+ setBlock(x, y, ROCK);
+ }
+ } else { // RIGHT SIDE
+ if (y >= floor - ((xo + length) - x) + 2) {
+ setBlock(x, y, ROCK);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return length;
+ }
+
+ private int buildCannons(int xo, int maxLength) {
+ int length = random.nextInt(10) + 2;
+ if (length > maxLength)
+ length = maxLength;
+
+ int floor = height - 1 - random.nextInt(4);
+ int xCannon = xo + 1 + random.nextInt(4);
+ for (int x = xo; x < xo + length; x++) {
+ if (x > xCannon) {
+ xCannon += 2 + random.nextInt(4);
+ }
+ if (xCannon == xo + length - 1)
+ xCannon += 10;
+ int cannonHeight = floor - random.nextInt(4) - 1;
+
+ for (int y = 0; y < height; y++) {
+ if (y >= floor) {
+ setBlock(x, y, GROUND);
+ } else {
+ if (x == xCannon && y >= cannonHeight) {
+ if (y == cannonHeight) {
+ setBlock(x, y, (byte) (14 + 0 * 16));
+ } else if (y == cannonHeight + 1) {
+ setBlock(x, y, (byte) (14 + 1 * 16));
+ } else {
+ setBlock(x, y, (byte) (14 + 2 * 16));
+ }
+ }
+ }
+ }
+ }
+
+ return length;
+ }
+
+ private int buildHillStraight(int xo, int maxLength) {
+ int length = random.nextInt(10) + 10;
+ if (length > maxLength)
+ length = maxLength;
+
+ int floor = height - 1 - random.nextInt(4);
+ for (int x = xo; x < xo + length; x++) {
+ for (int y = 0; y < height; y++) {
+ if (y >= floor) {
+ setBlock(x, y, GROUND);
+ }
+ }
+ }
+
+ addEnemyLine(xo + 1, xo + length - 1, floor - 1);
+
+ int h = floor;
+
+ boolean keepGoing = true;
+
+ boolean[] occupied = new boolean[length];
+ while (keepGoing) {
+ h = h - 2 - random.nextInt(3);
+
+ if (h <= 0) {
+ keepGoing = false;
+ } else {
+ int l = random.nextInt(5) + 3;
+ int xxo = random.nextInt(length - l - 2) + xo + 1;
+
+ if (occupied[xxo - xo] || occupied[xxo - xo + l]
+ || occupied[xxo - xo - 1] || occupied[xxo - xo + l + 1]) {
+ keepGoing = false;
+ } else {
+ occupied[xxo - xo] = true;
+ occupied[xxo - xo + l] = true;
+ addEnemyLine(xxo, xxo + l, h - 1);
+ if (random.nextInt(4) == 0) {
+ decorate(xxo - 1, xxo + l + 1, h);
+ keepGoing = false;
+ }
+ for (int x = xxo; x < xxo + l; x++) {
+ for (int y = h; y < floor; y++) {
+ int xx = 5;
+ if (x == xxo)
+ xx = 4;
+ if (x == xxo + l - 1)
+ xx = 6;
+ int yy = 9;
+ if (y == h)
+ yy = 8;
+
+ if (getBlock(x, y) == 0) {
+ setBlock(x, y, (byte) (xx + yy * 16));
+ } else {
+ if (getBlock(x, y) == HILL_TOP_LEFT)
+ setBlock(x, y, HILL_TOP_LEFT_IN);
+ if (getBlock(x, y) == HILL_TOP_RIGHT)
+ setBlock(x, y, HILL_TOP_RIGHT_IN);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return length;
+ }
+
+ private void addEnemyLine(int x0, int x1, int y) {
+ for (int x = x0; x < x1; x++) {
+ if (random.nextInt(35) < difficulty + 1) {
+ int type = random.nextInt(4);
+
+ if (difficulty < 1) {
+ type = Enemy.ENEMY_GOOMBA;
+ } else if (difficulty < 3) {
+ type = random.nextInt(3);
+ }
+
+ setSpriteTemplate(x, y,
+ new SpriteTemplate(type,
+ random.nextInt(35) < difficulty));
+ ENEMIES++;
+ }
+ }
+ }
+
+ private int buildTubes(int xo, int maxLength) {
+ int length = random.nextInt(10) + 5;
+ if (length > maxLength)
+ length = maxLength;
+
+ int floor = height - 1 - random.nextInt(4);
+ int xTube = xo + 1 + random.nextInt(4);
+ int tubeHeight = floor - random.nextInt(2) - 2;
+ for (int x = xo; x < xo + length; x++) {
+ if (x > xTube + 1) {
+ xTube += 3 + random.nextInt(4);
+ tubeHeight = floor - random.nextInt(2) - 2;
+ }
+ if (xTube >= xo + length - 2)
+ xTube += 10;
+
+ if (x == xTube && random.nextInt(11) < difficulty + 1) {
+ setSpriteTemplate(x, tubeHeight, new SpriteTemplate(
+ Enemy.ENEMY_FLOWER, false));
+ ENEMIES++;
+ }
+
+ for (int y = 0; y < height; y++) {
+ if (y >= floor) {
+ setBlock(x, y, GROUND);
+
+ } else {
+ if ((x == xTube || x == xTube + 1) && y >= tubeHeight) {
+ int xPic = 10 + x - xTube;
+
+ if (y == tubeHeight) {
+ // tube top
+ setBlock(x, y, (byte) (xPic + 0 * 16));
+ } else {
+ // tube side
+ setBlock(x, y, (byte) (xPic + 1 * 16));
+ }
+ }
+ }
+ }
+ }
+
+ return length;
+ }
+
+ private int buildStraight(int xo, int maxLength, boolean safe) {
+ int length = random.nextInt(10) + 2;
+
+ if (safe)
+ length = 10 + random.nextInt(5);
+
+ if (length > maxLength)
+ length = maxLength;
+
+ int floor = height - 1 - random.nextInt(4);
+
+ // 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 (!safe) {
+ if (length > 5) {
+ decorate(xo, xo + length, floor);
+ }
+ }
+
+ return length;
+ }
+
+ private void decorate(int xStart, int xLength, int floor) {
+ // if its at the very top, just return
+ if (floor < 1)
+ return;
+
+ // boolean coins = random.nextInt(3) == 0;
+ boolean rocks = true;
+
+ // add an enemy line above the box
+ addEnemyLine(xStart + 1, xLength - 1, floor - 1);
+
+ int s = random.nextInt(4);
+ int e = random.nextInt(4);
+
+ if (floor - 2 > 0) {
+ if ((xLength - 1 - e) - (xStart + 1 + s) > 1) {
+ for (int x = xStart + 1 + s; x < xLength - 1 - e; x++) {
+ setBlock(x, floor - 2, COIN);
+ COINS++;
+ }
+ }
+ }
+
+ s = random.nextInt(4);
+ e = random.nextInt(4);
+
+ // this fills the set of blocks and the hidden objects inside them
+ if (floor - 4 > 0) {
+ if ((xLength - 1 - e) - (xStart + 1 + s) > 2) {
+ for (int x = xStart + 1 + s; x < xLength - 1 - e; x++) {
+ if (rocks) {
+ if (x != xStart + 1 && x != xLength - 2
+ && random.nextInt(3) == 0) {
+ if (random.nextInt(4) == 0) {
+ setBlock(x, floor - 4, BLOCK_POWERUP);
+ BLOCKS_POWER++;
+ } else { // the fills a block with a hidden coin
+ setBlock(x, floor - 4, BLOCK_COIN);
+ BLOCKS_COINS++;
+ }
+ } else if (random.nextInt(4) == 0) {
+ if (random.nextInt(4) == 0) {
+ setBlock(x, floor - 4, (byte) (2 + 1 * 16));
+ } else {
+ setBlock(x, floor - 4, (byte) (1 + 1 * 16));
+ }
+ } else {
+ setBlock(x, floor - 4, BLOCK_EMPTY);
+ BLOCKS_EMPTY++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ 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 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));
+ }
+ }
+ }
+ }
+
+ 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;
+
+ }
+
+}
diff --git a/src/dk/itu/mario/level/PCGLevel.java b/src/dk/itu/mario/level/PCGLevel.java
new file mode 100644
index 0000000..0a7b055
--- /dev/null
+++ b/src/dk/itu/mario/level/PCGLevel.java
@@ -0,0 +1,1493 @@
+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 probability = new HashMap();
+ 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));
+ ENEMIES++;
+
+ 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);
+ COINS++;
+ }
+
+ else if (x == 16) {
+ setBlock(xo + x, floor - 7, Level.COIN);
+ COINS++;
+ }
+
+ else if (x == 18) {
+ setBlock(xo + x, floor - 8, Level.COIN);
+ COINS++;
+ }
+
+ else if (x == 19) {
+ setBlock(xo + x, floor - 8, Level.COIN);
+ COINS++;
+ }
+
+ else if (x == 20) {
+ setBlock(xo + x, floor - 8, Level.COIN);
+ COINS++;
+ }
+
+ else if (x == 22) {
+ setBlock(xo + x, floor - 7, Level.COIN);
+ COINS++;
+ }
+
+ else if (x == 23) {
+ setBlock(xo + x, floor - 6, Level.COIN);
+ COINS++;
+ }
+ }
+
+ else if (!arc
+ && reward == PlayerProfile.ChallengeRewardType.COIN
+ && x >= 10 && x <= 20) {
+ setBlock(xo + x, floor - 6, Level.COIN);
+ COINS++;
+ }
+
+ else if (reward == PlayerProfile.ChallengeRewardType.BLOCK
+ && x >= 13 && x <= 17) {
+ byte tile = (random.nextDouble() <= probability
+ .get(PlayerProfile.ChallengeRewardType.POWERUP)) ? Level.BLOCK_POWERUP
+ : Level.BLOCK_COIN;
+
+ setBlock(xo + x, floor - 6, tile);
+
+ if (tile == Level.BLOCK_COIN) {
+ BLOCKS_COINS++;
+ }
+
+ else if (tile == Level.BLOCK_POWERUP) {
+ BLOCKS_POWER++;
+ }
+ }
+
+ if (x >= 26 - numEnemies) {
+ setSpriteTemplate(xo + x, floor - 4, new SpriteTemplate(
+ enemyType, false));
+ ENEMIES++;
+ }
+ }
+
+ 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);
+ this.BLOCKS_EMPTY++;
+ } else if (x > 2 && x < 11) {
+ setBlock(xo + x, floor - 5, Level.BLOCK_EMPTY);
+ this.BLOCKS_EMPTY++;
+ } else if (x == 11 || x == 12) {
+ setBlock(xo + x, floor - 6, Level.COIN);
+ COINS++;
+ } else if (x == 13) {
+ setBlock(xo + x, floor - 5, Level.COIN);
+ COINS++;
+ } else if (x == 14) {
+ setBlock(xo + x, floor - 4, Level.COIN);
+ COINS++;
+ } else if (x == 15) {
+ setBlock(xo + x, floor - 3, Level.COIN);
+ COINS++;
+ }
+ }
+
+ 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));
+
+ this.BLOCKS_EMPTY++;
+ this.BLOCKS_POWER++;
+ this.ENEMIES++;
+ }
+
+ 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);
+
+ this.BLOCKS_COINS += 4;
+ this.BLOCKS_POWER++;
+ }
+
+ 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);
+
+ this.BLOCKS_EMPTY++;
+ this.BLOCKS_POWER++;
+
+ setSpriteTemplate(xo + 3, floor - 1, new SpriteTemplate(
+ SpriteTemplate.GREEN_TURTLE, false));
+
+ this.ENEMIES++;
+ }
+
+ else {
+ setBlock(xo + 2, floor - 3, Level.BLOCK_POWERUP);
+ this.BLOCKS_POWER++;
+ }
+
+ 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));
+ ENEMIES++;
+ }
+
+ if (reward == PlayerProfile.ChallengeRewardType.COIN && x >= 7
+ && x <= 16) {
+ setBlock(xo + x, floor - 7, Level.COIN);
+ COINS++;
+ }
+
+ else if (reward == PlayerProfile.ChallengeRewardType.BLOCK
+ && x >= 10 && x <= 13) {
+ byte tile = random.nextDouble() < probability
+ .get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
+ : Level.BLOCK_COIN;
+
+ setBlock(xo + x, floor - 7, tile);
+
+ if (tile == Level.BLOCK_COIN) {
+ this.BLOCKS_COINS++;
+ }
+
+ else {
+ this.BLOCKS_POWER++;
+ }
+ }
+ }
+ 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 maze = new ArrayList();
+
+ 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));
+ ENEMIES++;
+ }
+ }
+
+ 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));
+ ENEMIES++;
+ }
+
+ 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 jumps = new ArrayList();
+ 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));
+ ENEMIES++;
+ }
+
+ 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);
+
+ COINS += 4;
+ }
+
+ else if (reward == PlayerProfile.ChallengeRewardType.BLOCK
+ && hold != LevelComponent.PlatformLevel.TOP) {
+
+ byte tile = random.nextDouble() < probability
+ .get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
+ : Level.BLOCK_COIN;
+ setBlock(xo + length + 1, this.height - heightMod - 3, tile);
+
+ if (tile == Level.BLOCK_COIN) {
+ this.BLOCKS_COINS++;
+ }
+
+ else {
+ this.BLOCKS_POWER++;
+ }
+
+ tile = random.nextDouble() < probability
+ .get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
+ : Level.BLOCK_COIN;
+ setBlock(xo + length + 2, this.height - heightMod - 3, tile);
+
+ if (tile == Level.BLOCK_COIN) {
+ this.BLOCKS_COINS++;
+ }
+
+ else {
+ this.BLOCKS_POWER++;
+ }
+ }
+
+ 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)));
+ ENEMIES++;
+ }
+ }
+
+ if (reward != null) {
+ byte tile;
+ 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);
+
+ COINS += 7;
+ }
+
+ else if (length >= 7) {
+ int start = length / 2;
+ boolean coins = (reward == PlayerProfile.ChallengeRewardType.COIN);
+
+ tile = coins ? Level.COIN
+ : random.nextDouble() < probability
+ .get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
+ : Level.BLOCK_COIN;
+ setBlock(xo + (start - 2), floor - 3, tile);
+
+ if (coins) {
+ COINS++;
+ }
+
+ else if (tile == Level.BLOCK_COIN) {
+ this.BLOCKS_COINS++;
+ }
+
+ else if (tile == Level.BLOCK_POWERUP) {
+ this.BLOCKS_POWER++;
+ }
+
+ tile = coins ? Level.COIN
+ : random.nextDouble() < probability
+ .get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
+ : Level.BLOCK_COIN;
+ setBlock(xo + (start - 1), floor - 3, tile);
+
+ if (coins) {
+ COINS++;
+ }
+
+ else if (tile == Level.BLOCK_COIN) {
+ this.BLOCKS_COINS++;
+ }
+
+ else if (tile == Level.BLOCK_POWERUP) {
+ this.BLOCKS_POWER++;
+ }
+
+ tile = coins ? Level.COIN
+ : random.nextDouble() < probability
+ .get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
+ : Level.BLOCK_COIN;
+ setBlock(xo + start, floor - 3, tile);
+
+ if (coins) {
+ COINS++;
+ }
+
+ else if (tile == Level.BLOCK_COIN) {
+ this.BLOCKS_COINS++;
+ }
+
+ else if (tile == Level.BLOCK_POWERUP) {
+ this.BLOCKS_POWER++;
+ }
+
+ tile = coins ? Level.COIN
+ : random.nextDouble() < probability
+ .get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
+ : Level.BLOCK_COIN;
+ setBlock(xo + (start + 1), floor - 3, tile);
+
+ if (coins) {
+ COINS++;
+ }
+
+ else if (tile == Level.BLOCK_COIN) {
+ this.BLOCKS_COINS++;
+ }
+
+ else if (tile == Level.BLOCK_POWERUP) {
+ this.BLOCKS_POWER++;
+ }
+
+ if (length % 2 != 0) {
+ tile = coins ? Level.COIN
+ : random.nextDouble() < probability
+ .get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
+ : Level.BLOCK_COIN;
+ setBlock(xo + (start + 2), floor - 3, tile);
+
+ if (coins) {
+ COINS++;
+ }
+
+ else if (tile == Level.BLOCK_COIN) {
+ this.BLOCKS_COINS++;
+ }
+
+ else if (tile == Level.BLOCK_POWERUP) {
+ this.BLOCKS_POWER++;
+ }
+ }
+ }
+
+ else if (length >= 5) {
+ int start = length / 2;
+ boolean coins = (reward == PlayerProfile.ChallengeRewardType.COIN);
+
+ tile = coins ? Level.COIN
+ : random.nextDouble() < probability
+ .get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
+ : Level.BLOCK_COIN;
+ setBlock(xo + (start - 1), floor - 3, tile);
+
+ if (coins) {
+ COINS++;
+ }
+
+ else if (tile == Level.BLOCK_COIN) {
+ this.BLOCKS_COINS++;
+ }
+
+ else if (tile == Level.BLOCK_POWERUP) {
+ this.BLOCKS_POWER++;
+ }
+
+ tile = coins ? Level.COIN
+ : random.nextDouble() < probability
+ .get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
+ : Level.BLOCK_COIN;
+ setBlock(xo + start, floor - 3, tile);
+
+ if (coins) {
+ COINS++;
+ }
+
+ else if (tile == Level.BLOCK_COIN) {
+ this.BLOCKS_COINS++;
+ }
+
+ else if (tile == Level.BLOCK_POWERUP) {
+ this.BLOCKS_POWER++;
+ }
+
+ tile = coins ? Level.COIN
+ : random.nextDouble() < probability
+ .get(PlayerProfile.ChallengeRewardType.POWERUP) ? Level.BLOCK_POWERUP
+ : Level.BLOCK_COIN;
+ setBlock(xo + (start + 1), floor - 3, tile);
+
+ if (coins) {
+ COINS++;
+ }
+
+ else if (tile == Level.BLOCK_COIN) {
+ this.BLOCKS_COINS++;
+ }
+
+ else if (tile == Level.BLOCK_POWERUP) {
+ this.BLOCKS_POWER++;
+ }
+ }
+ }
+
+ 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 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;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/dk/itu/mario/level/PlayerProfile.java b/src/dk/itu/mario/level/PlayerProfile.java
new file mode 100644
index 0000000..e97af47
--- /dev/null
+++ b/src/dk/itu/mario/level/PlayerProfile.java
@@ -0,0 +1,151 @@
+package dk.itu.mario.level;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import dk.itu.mario.level.matcher.ProfileMatcher.SKILL;
+
+public class PlayerProfile {
+ // From Bartle/Yee models of player psychology: achiever, killer, explorer,
+ // manipulator
+ public enum TYPE {
+ BUMPER, COLLECTOR, RUNNER, SHOOTER, JUMPER
+ }
+
+ /*
+ * Certain probabilities are boosted by this constant based on the player's
+ * type.
+ */
+ public static final double TYPE_MULTIPLIER = 1.25;
+
+ public enum ChallengeRewardType {
+ GAP, ENEMY, HARDER_ENEMY, JUMP, COIN, BLOCK, POWERUP
+ };
+
+ // Dreyfus model of skill acquisition:
+ public enum SKILL_LEVEL {
+ NOVICE, BEGINNER, COMPETENT, PROFICIENT, EXPERT
+ }
+
+ private Set enabledComponents = new HashSet();
+
+ private SKILL_LEVEL skillLevel;
+ private Map skillVector;
+ private TYPE type;
+
+ public PlayerProfile(SKILL_LEVEL skillLevel, TYPE type,
+ Map skillVector) {
+ this.skillLevel = skillLevel;
+ this.type = type;
+ this.skillVector = skillVector;
+ for (LevelComponent.TYPE lcType : LevelComponent.TYPE.values()) {
+ setEnabled(lcType);
+ }
+ }
+
+ public boolean isEnabled(LevelComponent.TYPE type) {
+ return enabledComponents.contains(type);
+ }
+
+ public SKILL_LEVEL getSkillLevel() {
+ return skillLevel;
+ }
+
+ public int getBumpSkill() {
+ return skillVector.get(SKILL.BUMP);
+ }
+
+ public int getCollectSkill() {
+ return skillVector.get(SKILL.COLLECT);
+ }
+
+ public int getJumpSkill() {
+ return skillVector.get(SKILL.JUMP);
+ }
+
+ public int getRunSkill() {
+ return skillVector.get(SKILL.RUN);
+ }
+
+ public int getShootSkill() {
+ return skillVector.get(SKILL.SHOOT);
+ }
+
+ public int getStompSkill() {
+ return skillVector.get(SKILL.STOMP);
+ }
+
+ public double getProbability(ChallengeRewardType crt) {
+ switch (crt) {
+ case GAP:
+ return Math.min(.5, (.5) * (skillVector.get(SKILL.JUMP) / 100.0)
+ * ((type == TYPE.JUMPER) ? TYPE_MULTIPLIER : 1));
+ case ENEMY:
+ double skillSet = ((100 - skillVector.get(SKILL.RUN)) / 100.0)
+ * ((type == TYPE.RUNNER) ? -1 * TYPE_MULTIPLIER : 1);
+ skillSet += (skillVector.get(SKILL.STOMP) / 100.0);
+ skillSet /= 2;
+ return Math.min(.5, (.5) * skillSet);
+ case HARDER_ENEMY:
+ return Math.min(.5, (.5) * (skillVector.get(SKILL.SHOOT) / 100.0)
+ * ((type == TYPE.SHOOTER) ? TYPE_MULTIPLIER : 1));
+ case JUMP:
+ return Math.min(.5, (.5) * (skillVector.get(SKILL.JUMP) / 100.0)
+ * ((type == TYPE.JUMPER) ? TYPE_MULTIPLIER : 1));
+ case BLOCK:
+ return Math.min(.5, (.5) * (skillVector.get(SKILL.BUMP) / 100.0)
+ * ((type == TYPE.BUMPER) ? TYPE_MULTIPLIER : 1));
+ case COIN:
+ return Math.min(.5, (.5) * (skillVector.get(SKILL.COLLECT) / 100.0)
+ * ((type == TYPE.COLLECTOR) ? TYPE_MULTIPLIER : 1));
+ case POWERUP:
+ double skillMod = 0;
+ switch (skillLevel) {
+ case BEGINNER:
+ skillMod = .8;
+ break;
+ case COMPETENT:
+ skillMod = .6;
+ break;
+ case PROFICIENT:
+ skillMod = .4;
+ break;
+ case EXPERT:
+ skillMod = .2;
+ break;
+ default:
+ skillMod = 1;
+ }
+
+ skillMod = (skillMod + (skillVector.get(SKILL.SHOOT) / 100.0)) / 2.0;
+
+ return (.3) * skillMod;
+ default:
+ return 0.1;
+ }
+ }
+
+ public TYPE getType() {
+ return type;
+ }
+
+ public void setDisabled(LevelComponent.TYPE type) {
+ if (enabledComponents.contains(type)) {
+ System.out.println("Component type disabled: " + type);
+ enabledComponents.remove(type);
+ }
+ }
+
+ public void setEnabled(LevelComponent.TYPE type) {
+ if (!enabledComponents.contains(type)) {
+ System.out.println("Component type enabled: " + type);
+ enabledComponents.add(type);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return skillLevel + " " + type;
+ }
+}
diff --git a/src/dk/itu/mario/level/RandomLevel.java b/src/dk/itu/mario/level/RandomLevel.java
new file mode 100644
index 0000000..7be08c0
--- /dev/null
+++ b/src/dk/itu/mario/level/RandomLevel.java
@@ -0,0 +1,576 @@
+package dk.itu.mario.level;
+
+import java.util.Random;
+
+import dk.itu.mario.MarioInterface.Constraints;
+import dk.itu.mario.MarioInterface.LevelInterface;
+import dk.itu.mario.engine.sprites.SpriteTemplate;
+import dk.itu.mario.engine.sprites.Enemy;
+
+public class RandomLevel extends Level {
+ // Store information about the level
+ public int ENEMIES = 0; // the number of enemies the level contains
+ public int BLOCKS_EMPTY = 0; // the number of empty blocks
+ public int BLOCKS_COINS = 0; // the number of coin blocks
+ public int BLOCKS_POWER = 0; // the number of power blocks
+ public int COINS = 0; // These are the coins in boxes that Mario collect
+
+ public static long lastSeed;
+
+ Random random;
+
+ private static final int ODDS_STRAIGHT = 0;
+ private static final int ODDS_HILL_STRAIGHT = 1;
+ private static final int ODDS_TUBES = 2;
+ private static final int ODDS_JUMP = 3;
+ private static final int ODDS_CANNONS = 4;
+
+ private int[] odds = new int[5];
+
+ private int totalOdds;
+
+ private int difficulty;
+ private int type;
+ private int gaps;
+
+ public RandomLevel(int width, int height) {
+ super(width, height);
+ }
+
+ public RandomLevel(int width, int height, long seed, int difficulty,
+ int type) {
+ this(width, height);
+ creat(seed, difficulty, type);
+ }
+
+ public void creat(long seed, int difficulty, int type) {
+ this.type = type;
+ this.difficulty = difficulty;
+ odds[ODDS_STRAIGHT] = 20;
+ odds[ODDS_HILL_STRAIGHT] = 10;
+ odds[ODDS_TUBES] = 2 + 1 * difficulty;
+ odds[ODDS_JUMP] = 2 * difficulty;
+ odds[ODDS_CANNONS] = -10 + 5 * difficulty;
+
+ if (type != LevelInterface.TYPE_OVERGROUND) {
+ odds[ODDS_HILL_STRAIGHT] = 0;
+ }
+
+ for (int i = 0; i < odds.length; i++) {
+ // failsafe (no negative odds)
+ if (odds[i] < 0) {
+ odds[i] = 0;
+ }
+
+ totalOdds += odds[i];
+ odds[i] = totalOdds - odds[i];
+ }
+
+ lastSeed = seed;
+ random = new Random(seed);
+
+ // create the start location
+ int length = 0;
+ length += buildStraight(0, width, true);
+
+ // create all of the medium sections
+ while (length < width - 64) {
+ length += buildZone(length, width - length);
+ }
+
+ // set the end piece
+ int floor = height - 1 - random.nextInt(4);
+
+ xExit = length + 8;
+ yExit = 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 int buildZone(int x, int maxLength) {
+ int t = random.nextInt(totalOdds);
+ int type = 0;
+
+ for (int i = 0; i < odds.length; i++) {
+ if (odds[i] <= t) {
+ type = i;
+ }
+ }
+
+ switch (type) {
+ case ODDS_STRAIGHT:
+ return buildStraight(x, maxLength, false);
+ case ODDS_HILL_STRAIGHT:
+ return buildHillStraight(x, maxLength);
+ case ODDS_TUBES:
+ return buildTubes(x, maxLength);
+ case ODDS_JUMP:
+ if (gaps < Constraints.gaps)
+ return buildJump(x, maxLength);
+ else
+ return buildStraight(x, maxLength, false);
+ case ODDS_CANNONS:
+ return buildCannons(x, maxLength);
+ }
+ return 0;
+ }
+
+ private int buildJump(int xo, int maxLength) {
+ gaps++;
+ // jl: jump length
+ // js: the number of blocks that are available at either side for free
+ int js = random.nextInt(4) + 2;
+ int jl = random.nextInt(2) + 2;
+ int length = js * 2 + jl;
+
+ boolean hasStairs = random.nextInt(3) == 0;
+
+ int floor = height - 1 - random.nextInt(4);
+ // run from the start x position, for the whole length
+ for (int x = xo; x < xo + length; x++) {
+ if (x < xo + js || x > xo + length - js - 1) {
+ // run for all y's since we need to paint blocks upward
+ for (int y = 0; y < height; y++) { // paint ground up until the
+ // floor
+ if (y >= floor) {
+ setBlock(x, y, GROUND);
+ }
+ // if it is above ground, start making stairs of rocks
+ else if (hasStairs) { // LEFT SIDE
+ if (x < xo + js) { // we need to max it out and level
+ // because it wont
+ // paint ground correctly unless two
+ // bricks are side by side
+ if (y >= floor - (x - xo) + 1) {
+ setBlock(x, y, ROCK);
+ }
+ } else { // RIGHT SIDE
+ if (y >= floor - ((xo + length) - x) + 2) {
+ setBlock(x, y, ROCK);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return length;
+ }
+
+ private int buildCannons(int xo, int maxLength) {
+ int length = random.nextInt(10) + 2;
+ if (length > maxLength)
+ length = maxLength;
+
+ int floor = height - 1 - random.nextInt(4);
+ int xCannon = xo + 1 + random.nextInt(4);
+ for (int x = xo; x < xo + length; x++) {
+ if (x > xCannon) {
+ xCannon += 2 + random.nextInt(4);
+ }
+ if (xCannon == xo + length - 1)
+ xCannon += 10;
+ int cannonHeight = floor - random.nextInt(4) - 1;
+
+ for (int y = 0; y < height; y++) {
+ if (y >= floor) {
+ setBlock(x, y, GROUND);
+ } else {
+ if (x == xCannon && y >= cannonHeight) {
+ if (y == cannonHeight) {
+ setBlock(x, y, (byte) (14 + 0 * 16));
+ } else if (y == cannonHeight + 1) {
+ setBlock(x, y, (byte) (14 + 1 * 16));
+ } else {
+ setBlock(x, y, (byte) (14 + 2 * 16));
+ }
+ }
+ }
+ }
+ }
+
+ return length;
+ }
+
+ private int buildHillStraight(int xo, int maxLength) {
+ int length = random.nextInt(10) + 10;
+ if (length > maxLength)
+ length = maxLength;
+
+ int floor = height - 1 - random.nextInt(4);
+ for (int x = xo; x < xo + length; x++) {
+ for (int y = 0; y < height; y++) {
+ if (y >= floor) {
+ setBlock(x, y, GROUND);
+ }
+ }
+ }
+
+ addEnemyLine(xo + 1, xo + length - 1, floor - 1);
+
+ int h = floor;
+
+ boolean keepGoing = true;
+
+ boolean[] occupied = new boolean[length];
+ while (keepGoing) {
+ h = h - 2 - random.nextInt(3);
+
+ if (h <= 0) {
+ keepGoing = false;
+ } else {
+ int l = random.nextInt(5) + 3;
+ int xxo = random.nextInt(length - l - 2) + xo + 1;
+
+ if (occupied[xxo - xo] || occupied[xxo - xo + l]
+ || occupied[xxo - xo - 1] || occupied[xxo - xo + l + 1]) {
+ keepGoing = false;
+ } else {
+ occupied[xxo - xo] = true;
+ occupied[xxo - xo + l] = true;
+ addEnemyLine(xxo, xxo + l, h - 1);
+ if (random.nextInt(4) == 0) {
+ decorate(xxo - 1, xxo + l + 1, h);
+ keepGoing = false;
+ }
+ for (int x = xxo; x < xxo + l; x++) {
+ for (int y = h; y < floor; y++) {
+ int xx = 5;
+ if (x == xxo)
+ xx = 4;
+ if (x == xxo + l - 1)
+ xx = 6;
+ int yy = 9;
+ if (y == h)
+ yy = 8;
+
+ if (getBlock(x, y) == 0) {
+ setBlock(x, y, (byte) (xx + yy * 16));
+ } else {
+ if (getBlock(x, y) == HILL_TOP_LEFT)
+ setBlock(x, y, HILL_TOP_LEFT_IN);
+ if (getBlock(x, y) == HILL_TOP_RIGHT)
+ setBlock(x, y, HILL_TOP_RIGHT_IN);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return length;
+ }
+
+ private void addEnemyLine(int x0, int x1, int y) {
+ for (int x = x0; x < x1; x++) {
+ if (random.nextInt(35) < difficulty + 1) {
+ int type = random.nextInt(4);
+
+ if (difficulty < 1) {
+ type = Enemy.ENEMY_GOOMBA;
+ } else if (difficulty < 3) {
+ type = random.nextInt(3);
+ }
+
+ setSpriteTemplate(x, y,
+ new SpriteTemplate(type,
+ random.nextInt(35) < difficulty));
+ ENEMIES++;
+ }
+ }
+ }
+
+ private int buildTubes(int xo, int maxLength) {
+ int length = random.nextInt(10) + 5;
+ if (length > maxLength)
+ length = maxLength;
+
+ int floor = height - 1 - random.nextInt(4);
+ int xTube = xo + 1 + random.nextInt(4);
+ int tubeHeight = floor - random.nextInt(2) - 2;
+ for (int x = xo; x < xo + length; x++) {
+ if (x > xTube + 1) {
+ xTube += 3 + random.nextInt(4);
+ tubeHeight = floor - random.nextInt(2) - 2;
+ }
+ if (xTube >= xo + length - 2)
+ xTube += 10;
+
+ if (x == xTube && random.nextInt(11) < difficulty + 1) {
+ setSpriteTemplate(x, tubeHeight, new SpriteTemplate(
+ Enemy.ENEMY_FLOWER, false));
+ ENEMIES++;
+ }
+
+ for (int y = 0; y < height; y++) {
+ if (y >= floor) {
+ setBlock(x, y, GROUND);
+
+ } else {
+ if ((x == xTube || x == xTube + 1) && y >= tubeHeight) {
+ int xPic = 10 + x - xTube;
+
+ if (y == tubeHeight) {
+ // tube top
+ setBlock(x, y, (byte) (xPic + 0 * 16));
+ } else {
+ // tube side
+ setBlock(x, y, (byte) (xPic + 1 * 16));
+ }
+ }
+ }
+ }
+ }
+
+ return length;
+ }
+
+ private int buildStraight(int xo, int maxLength, boolean safe) {
+ int length = random.nextInt(10) + 2;
+
+ if (safe)
+ length = 10 + random.nextInt(5);
+
+ if (length > maxLength)
+ length = maxLength;
+
+ int floor = height - 1 - random.nextInt(4);
+
+ // 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 (!safe) {
+ if (length > 5) {
+ decorate(xo, xo + length, floor);
+ }
+ }
+
+ return length;
+ }
+
+ private void decorate(int xStart, int xLength, int floor) {
+ // if its at the very top, just return
+ if (floor < 1)
+ return;
+
+ // boolean coins = random.nextInt(3) == 0;
+ boolean rocks = true;
+
+ // add an enemy line above the box
+ addEnemyLine(xStart + 1, xLength - 1, floor - 1);
+
+ int s = random.nextInt(4);
+ int e = random.nextInt(4);
+
+ if (floor - 2 > 0) {
+ if ((xLength - 1 - e) - (xStart + 1 + s) > 1) {
+ for (int x = xStart + 1 + s; x < xLength - 1 - e; x++) {
+ setBlock(x, floor - 2, COIN);
+ COINS++;
+ }
+ }
+ }
+
+ s = random.nextInt(4);
+ e = random.nextInt(4);
+
+ // this fills the set of blocks and the hidden objects inside them
+ if (floor - 4 > 0) {
+ if ((xLength - 1 - e) - (xStart + 1 + s) > 2) {
+ for (int x = xStart + 1 + s; x < xLength - 1 - e; x++) {
+ if (rocks) {
+ if (x != xStart + 1 && x != xLength - 2
+ && random.nextInt(3) == 0) {
+ if (random.nextInt(4) == 0) {
+ setBlock(x, floor - 4, BLOCK_POWERUP);
+ BLOCKS_POWER++;
+ } else { // the fills a block with a hidden coin
+ setBlock(x, floor - 4, BLOCK_COIN);
+ BLOCKS_COINS++;
+ }
+ } else if (random.nextInt(4) == 0) {
+ if (random.nextInt(4) == 0) {
+ setBlock(x, floor - 4, (byte) (2 + 1 * 16));
+ } else {
+ setBlock(x, floor - 4, (byte) (1 + 1 * 16));
+ }
+ } else {
+ setBlock(x, floor - 4, BLOCK_EMPTY);
+ BLOCKS_EMPTY++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ 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 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));
+ }
+ }
+ }
+ }
+
+ 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;
+
+ }
+
+}
diff --git a/src/dk/itu/mario/level/generator/.DS_Store b/src/dk/itu/mario/level/generator/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/src/dk/itu/mario/level/generator/.DS_Store differ
diff --git a/src/dk/itu/mario/level/generator/CustomizedLevelGenerator.java b/src/dk/itu/mario/level/generator/CustomizedLevelGenerator.java
new file mode 100644
index 0000000..322ba58
--- /dev/null
+++ b/src/dk/itu/mario/level/generator/CustomizedLevelGenerator.java
@@ -0,0 +1,45 @@
+package dk.itu.mario.level.generator;
+
+import java.util.Random;
+
+import dk.itu.mario.MarioInterface.GamePlay;
+import dk.itu.mario.MarioInterface.LevelGenerator;
+import dk.itu.mario.MarioInterface.LevelInterface;
+import dk.itu.mario.engine.DataRecorder;
+import dk.itu.mario.level.CustomizedLevel;
+import dk.itu.mario.level.Level;
+
+public class CustomizedLevelGenerator implements LevelGenerator {
+ @Override
+ public int generateLevelDifficulty(GamePlay playerMetrics, DataRecorder dataRecorder) {
+ System.out.println("Generating level difficulty based on playerMetrics, dataRecorder: 1");
+ return 1;
+ }
+
+ @Override
+ public LevelInterface generateLevel(GamePlay playerMetrics) {
+ System.out.println("Generating customized level");
+ LevelInterface level = new CustomizedLevel(320, 15,
+ new Random().nextLong(), 1, 1, playerMetrics);
+ return level;
+ }
+
+ @Override
+ public LevelInterface generateLevel(GamePlay playerMetrics, DataRecorder dataRecorder) {
+ System.out.println("Generating customized level");
+ LevelInterface level = new CustomizedLevel(320, 15,
+ new Random().nextLong(), 1, 1, playerMetrics, dataRecorder);
+ return level;
+ }
+
+ @Override
+ public LevelInterface generateLevel(String detailedInfo) {
+ throw new UnsupportedOperationException("Level generation from DataRecorder String not implemented.");
+ }
+
+ @Override
+ public int generateLevelType(GamePlay playerMetrics, DataRecorder dataRecorder) {
+ System.out.println("Generating level type based on playerMetrics, dataRecorder: TYPE_OVERGROUND");
+ return Level.TYPE_OVERGROUND;
+ }
+}
\ No newline at end of file
diff --git a/src/dk/itu/mario/level/generator/MyLevelGenerator.java b/src/dk/itu/mario/level/generator/MyLevelGenerator.java
new file mode 100644
index 0000000..bf39ce0
--- /dev/null
+++ b/src/dk/itu/mario/level/generator/MyLevelGenerator.java
@@ -0,0 +1,26 @@
+package dk.itu.mario.level.generator;
+
+import java.util.Random;
+
+import dk.itu.mario.MarioInterface.GamePlay;
+import dk.itu.mario.MarioInterface.LevelGenerator;
+import dk.itu.mario.MarioInterface.LevelInterface;
+import dk.itu.mario.level.MyLevel;
+
+public class MyLevelGenerator extends CustomizedLevelGenerator implements
+ LevelGenerator {
+
+ public LevelInterface generateLevel(GamePlay playerMetrics) {
+ System.out.println("Generating my level");
+ LevelInterface level = new MyLevel(320, 15, new Random().nextLong(), 1,
+ LevelInterface.TYPE_OVERGROUND, playerMetrics);
+ return level;
+ }
+
+ @Override
+ public LevelInterface generateLevel(String detailedInfo) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/src/dk/itu/mario/level/generator/PCGLevelGenerator.java b/src/dk/itu/mario/level/generator/PCGLevelGenerator.java
new file mode 100644
index 0000000..de0cd31
--- /dev/null
+++ b/src/dk/itu/mario/level/generator/PCGLevelGenerator.java
@@ -0,0 +1,64 @@
+package dk.itu.mario.level.generator;
+
+import java.util.Random;
+
+import dk.itu.mario.MarioInterface.GamePlay;
+import dk.itu.mario.MarioInterface.LevelGenerator;
+import dk.itu.mario.MarioInterface.LevelInterface;
+import dk.itu.mario.engine.DataRecorder;
+import dk.itu.mario.level.Level;
+import dk.itu.mario.level.LevelArchetype;
+import dk.itu.mario.level.PCGLevel;
+import dk.itu.mario.level.PlayerProfile;
+import dk.itu.mario.level.matcher.ArchetypeMatcher;
+import dk.itu.mario.level.matcher.ProfileMatcher;
+
+public class PCGLevelGenerator implements
+ LevelGenerator {
+ @Override
+ public int generateLevelDifficulty(GamePlay playerMetrics, DataRecorder dataRecorder) {
+ System.out.println("Generating level difficulty based on playerMetrics, dataRecorder: 1");
+ return 1;
+ }
+
+ @Override
+ public int generateLevelType(GamePlay playerMetrics, DataRecorder dataRecorder) {
+ System.out.println("Generating level type based on playerMetrics, dataRecorder: TYPE_OVERGROUND");
+ PlayerProfile profile = ProfileMatcher.getMatchingProfile(
+ playerMetrics, dataRecorder);
+ System.out.println("PlayerProfile: " + profile);
+
+ LevelArchetype archetype = ArchetypeMatcher.getMatchingArchetype(profile);
+ switch (archetype.getType()) {
+ case CASTLE :
+ return LevelInterface.TYPE_CASTLE;
+ case OVERGROUND :
+ return LevelInterface.TYPE_OVERGROUND;
+ case UNDERGROUND :
+ return LevelInterface.TYPE_UNDERGROUND;
+ default :
+ return LevelInterface.TYPE_OVERGROUND;
+ }
+ }
+
+ @Override
+ public LevelInterface generateLevel(GamePlay playerMetrics) {
+ System.out.println("Generating customized level");
+ LevelInterface level = new PCGLevel(320, 15,
+ new Random().nextLong(), generateLevelDifficulty(playerMetrics,DataRecorder.BLANK_RECORD), generateLevelType(playerMetrics,DataRecorder.BLANK_RECORD), playerMetrics);
+ return level;
+ }
+
+ @Override
+ public LevelInterface generateLevel(GamePlay playerMetrics, DataRecorder dataRecorder) {
+ System.out.println("Generating customized level");
+ LevelInterface level = new PCGLevel(320, 15,
+ new Random().nextLong(), generateLevelDifficulty(playerMetrics,DataRecorder.BLANK_RECORD), generateLevelType(playerMetrics,dataRecorder), playerMetrics, dataRecorder);
+ return level;
+ }
+
+ @Override
+ public LevelInterface generateLevel(String detailedInfo) {
+ throw new UnsupportedOperationException("Level generation from DataRecorder String not implemented.");
+ }
+}
diff --git a/src/dk/itu/mario/level/generator/RandomLevelGenerator.java b/src/dk/itu/mario/level/generator/RandomLevelGenerator.java
new file mode 100644
index 0000000..a420311
--- /dev/null
+++ b/src/dk/itu/mario/level/generator/RandomLevelGenerator.java
@@ -0,0 +1,26 @@
+package dk.itu.mario.level.generator;
+
+import java.util.Random;
+
+import dk.itu.mario.MarioInterface.GamePlay;
+import dk.itu.mario.MarioInterface.LevelGenerator;
+import dk.itu.mario.MarioInterface.LevelInterface;
+import dk.itu.mario.level.RandomLevel;
+
+public class RandomLevelGenerator extends CustomizedLevelGenerator implements
+ LevelGenerator {
+
+ public LevelInterface generateLevel(GamePlay playerMetrics) {
+ System.out.println("Generating random level");
+ LevelInterface level = new RandomLevel(320, 15,
+ new Random().nextLong(), 1, LevelInterface.TYPE_OVERGROUND);
+ return level;
+ }
+
+ @Override
+ public LevelInterface generateLevel(String detailedInfo) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/src/dk/itu/mario/level/grammar/AndClause.java b/src/dk/itu/mario/level/grammar/AndClause.java
new file mode 100644
index 0000000..e5903a5
--- /dev/null
+++ b/src/dk/itu/mario/level/grammar/AndClause.java
@@ -0,0 +1,63 @@
+package dk.itu.mario.level.grammar;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AndClause implements Clause {
+ List subClauses = new ArrayList();
+
+ public AndClause(List subClauses) {
+ this.subClauses.addAll(subClauses);
+ }
+
+ public AndClause(Clause... subClauses) {
+ for (Clause clause : subClauses) {
+ this.subClauses.add(clause);
+ }
+ }
+
+ @Override
+ public Clause makeRandomChoice() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getNumSubClauses() {
+ return subClauses.size();
+ }
+
+ @Override
+ public Clause getSubClause(int index) {
+ // TODO Auto-generated method stub
+ return subClauses.get(index);
+ }
+
+ @Override
+ public boolean isChoice() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+ @Override
+ public boolean isTerminal() {
+ return false;
+ }
+ @Override
+ public boolean isVariable() {
+ return false;
+ }
+ @Override
+ public String toString() {
+ if (subClauses.size() == 0) {
+ return "(Empty AND clause)";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append("(");
+ for (int i = 0; i < subClauses.size() - 1; i ++) {
+ sb.append(subClauses.get(i).toString());
+ sb.append(" + ");
+ }
+ sb.append(subClauses.get(subClauses.size()-1).toString());
+ sb.append(")");
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/src/dk/itu/mario/level/grammar/Clause.java b/src/dk/itu/mario/level/grammar/Clause.java
new file mode 100644
index 0000000..54f747d
--- /dev/null
+++ b/src/dk/itu/mario/level/grammar/Clause.java
@@ -0,0 +1,10 @@
+package dk.itu.mario.level.grammar;
+
+public interface Clause {
+ public Clause getSubClause(int index);
+ public int getNumSubClauses();
+ public boolean isChoice();
+ public boolean isTerminal();
+ public boolean isVariable();
+ public Clause makeRandomChoice();
+}
\ No newline at end of file
diff --git a/src/dk/itu/mario/level/grammar/LevelGrammar.java b/src/dk/itu/mario/level/grammar/LevelGrammar.java
new file mode 100644
index 0000000..f33ac48
--- /dev/null
+++ b/src/dk/itu/mario/level/grammar/LevelGrammar.java
@@ -0,0 +1,122 @@
+package dk.itu.mario.level.grammar;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Random;
+import java.util.Set;
+import java.util.TreeSet;
+
+import dk.itu.mario.level.LevelComponent;
+import dk.itu.mario.level.LevelComponent.TYPE;
+
+public class LevelGrammar {
+ private Variable start;
+ private Map ruleMap = new HashMap();
+ private Set variables = new TreeSet();
+
+ public void addVariable(Variable var) {
+ if (variables.contains(var)) {
+ throw new IllegalArgumentException(
+ "Grammar already contains variable: " + var);
+ }
+ variables.add(var);
+ }
+
+ public void addProductionRule(ProductionRule rule) {
+ if (ruleMap.containsKey(rule.getLHS())) {
+ throw new IllegalArgumentException(
+ "Grammar already contains rule with LHS: " + rule.getLHS());
+ }
+ ruleMap.put(rule.getLHS(), rule);
+ }
+
+ public LevelParseTree generateRandomTree(long randomSeed, int width) {
+ System.out.println("Generating random level parameters using seed: "
+ + randomSeed);
+ Variable startRuleLHS = getStart();
+
+ ParseNode rootNode = new ParseNode(startRuleLHS.getType(), 1.0);
+ generateRecursive(rootNode, getRule(startRuleLHS).getRHS(), 1.0);
+ LevelParseTree parseTree = new LevelParseTree(rootNode, 0, width);
+
+ return parseTree;
+ }
+
+ private ParseNode generateRecursive(ParseNode parseNode, Clause clause,
+ double relativeWidth) {
+ if (clause.isVariable()) {
+ if (clause.isTerminal()) {
+ parseNode.addChild(new ParseNode(((Variable) clause).getType(),
+ relativeWidth));
+ } else {
+ generateRecursive(parseNode, getRule((Variable) clause)
+ .getRHS(), relativeWidth);
+ }
+ } else {
+ if (clause.isChoice()) {
+ generateRecursive(parseNode, clause.makeRandomChoice(),
+ relativeWidth);
+ } else {
+ int numSubClauses = clause.getNumSubClauses();
+ for (int i = 0; i < numSubClauses; i++) {
+ generateRecursive(parseNode, clause.getSubClause(i),
+ relativeWidth / numSubClauses);
+ }
+ }
+ }
+ return parseNode;
+ }
+
+ public Variable getVariable(String varName) {
+ for (Variable var : variables) {
+ if (varName.equals(var.getValue())) {
+ return var;
+ }
+ }
+ return null;
+ }
+
+ public ProductionRule getRule(Variable var) {
+ return ruleMap.get(var);
+ }
+
+ public Variable getStart() {
+ return start;
+ }
+
+ public void setStart(String startVar) {
+ for (Variable var : variables) {
+ if (startVar.equals(var.getValue())) {
+ start = var;
+ return;
+ }
+ }
+ }
+
+ public void setStart(Variable start) {
+ this.start = start;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Variables:\n");
+ for (Variable var : variables) {
+ sb.append("VAR ");
+ sb.append(var.getValue());
+ sb.append(" = ");
+ sb.append(var.getType());
+ sb.append("\n");
+ }
+ sb.append("\n");
+ for (Entry entry : ruleMap.entrySet()) {
+ //could as easily have used LHS instead of getKey().getValue()
+ sb.append("RULE " + entry.getKey().getValue() + " -> " + entry.getValue().getRHS());
+ sb.append("\n");
+ }
+ sb.append("\n");
+ sb.append("START = " + start.getValue());
+ return sb.toString();
+ }
+}
diff --git a/src/dk/itu/mario/level/grammar/LevelGrammarFactory.java b/src/dk/itu/mario/level/grammar/LevelGrammarFactory.java
new file mode 100644
index 0000000..b39faa0
--- /dev/null
+++ b/src/dk/itu/mario/level/grammar/LevelGrammarFactory.java
@@ -0,0 +1,189 @@
+package dk.itu.mario.level.grammar;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import dk.itu.mario.level.LevelComponent;
+
+public class LevelGrammarFactory {
+ public static LevelGrammar createGrammar(File grammarFile) throws FileNotFoundException, IOException {
+ FileInputStream fis = new FileInputStream(grammarFile);
+ LevelGrammar levelGrammar = new LevelGrammar();
+ try {
+ InputStreamReader reader = new InputStreamReader(fis);
+ BufferedReader buf = new BufferedReader(reader);
+ String line;
+ while ((line = buf.readLine()) != null) {
+ line = line.trim(); // trim off newline/whitespace
+ if (line.length() == 0 || line.startsWith("#")) {
+ continue;
+ }
+ //System.out.println("Read: " + line);
+ //split on whitespace
+ String[] fields;
+ if (line.startsWith("VAR")) {
+ fields = line.split("\\s");
+ if ("=".equals(fields[2]) && fields.length == 4) {
+ levelGrammar.addVariable(new Variable(fields[1],LevelComponent.TYPE.valueOf(fields[3])));
+ } else {
+ throw new RuntimeException("Invalid VAR syntax: " + line);
+ }
+ } else if (line.startsWith("RULE")) {
+ fields = line.split("->");
+ String ruleName = fields[0].split("\\s")[1];
+ //System.out.println("Rule name: " + ruleName);
+ Variable lhs = levelGrammar.getVariable(ruleName);
+ if (lhs == null) {
+ throw new RuntimeException("LHS variable not found: " + ruleName);
+ }
+ Clause rhs = getClause(fields[1].trim(),levelGrammar);
+ levelGrammar.addProductionRule(new ProductionRule(lhs,rhs));
+ } else if (line.startsWith("START")) {
+ fields = line.split("\\s");
+ if ("=".equals(fields[1]) && fields.length == 3) {
+ levelGrammar.setStart(fields[2]);
+ } else {
+ throw new RuntimeException("Invalid START syntax: " + line);
+ }
+ } else {
+ throw new RuntimeException("Unable to parse grammar file " + grammarFile.getName() + " due to invalid line: " + line);
+ }
+ }
+ return levelGrammar;
+ } finally {
+ fis.close();
+ }
+ }
+
+ private static Clause getClause(String clause, LevelGrammar grammar) {
+ if (!clause.contains("|") && !clause.contains("+")) {
+ return grammar.getVariable(clause);
+ }
+ int lBraceIndex = clause.indexOf("{");
+ boolean isOrClause = false;
+ int rBraceIndex = 0;
+
+ double[] chances = {};
+
+ if (lBraceIndex != -1) {
+ isOrClause = true;
+ rBraceIndex = clause.indexOf("}");
+ //System.out.println("Read OR-clause probabilities from: " + lBraceIndex + " to " + rBraceIndex);
+ String[] doubleFields = clause.substring(lBraceIndex+1,rBraceIndex).split(",");
+ chances = new double[doubleFields.length];
+ for (int i = 0; i < doubleFields.length; i++) {
+ chances[i] = Double.valueOf(doubleFields[i]);
+ }
+ }
+
+ String remainder = clause.substring(rBraceIndex);
+ if (remainder.startsWith("}, ")) {
+ remainder = remainder.substring(3);
+ }
+ List rhsClauseStrings = new ArrayList();
+ if (remainder.contains("(")) {
+ int nextLeftIndex = -1;
+ do {
+ nextLeftIndex = remainder.indexOf("(", nextLeftIndex+1);
+ if (nextLeftIndex != -1) {
+ int nextRightIndex = remainder.indexOf(")", nextLeftIndex+1);
+ if (nextRightIndex == -1) {
+ throw new RuntimeException("Unmatched left '(' in clause");
+ }
+ rhsClauseStrings.add(remainder.substring(nextLeftIndex+1,nextRightIndex));
+ nextLeftIndex = nextRightIndex;
+ }
+ } while (nextLeftIndex != -1);
+ } else {
+ isOrClause = remainder.contains("|");
+ if (isOrClause) {
+ for (String subclause : remainder.split("\\|")) {
+ rhsClauseStrings.add(subclause.trim());
+ }
+ } else {
+ for (String subclause : remainder.split("\\+")) {
+ rhsClauseStrings.add(subclause.trim());
+ }
+ }
+ }
+
+ List rhsClauses = new ArrayList();
+
+ for (String subclause : rhsClauseStrings) {
+ rhsClauses.add(getClause(subclause,grammar));
+ }
+
+ if (isOrClause) {
+ return new OrClause(chances, rhsClauses);
+ } else {
+ return new AndClause(rhsClauses);
+ }
+ }
+ public static LevelGrammar createGrammar() {
+ LevelGrammar grammar = new LevelGrammar();
+
+ Variable v_START = new Variable("S", LevelComponent.TYPE.LEVEL);
+ Variable v_LAND_SEGMENT = new Variable("HALF_LVL", LevelComponent.TYPE.LEVEL_SEGMENT);
+ Variable v_LO_HI = new Variable("LO_HI", LevelComponent.TYPE.LO_HI);
+ Variable v_HI_LO = new Variable("HI_LO", LevelComponent.TYPE.HI_LO);
+ Variable v_LO_PATH = new Variable("LO_PATH", LevelComponent.TYPE.LO_PATH);
+ Variable v_HI_PATH = new Variable("HI_PATH", LevelComponent.TYPE.HI_PATH);
+ Variable v_lo_path = new Variable("lo_path", LevelComponent.TYPE.FLAT_LO);
+ Variable v_hi_path = new Variable("hi_path", LevelComponent.TYPE.FLAT_HI);
+
+ grammar.addVariable(v_START);
+ grammar.addVariable(v_LAND_SEGMENT);
+ grammar.addVariable(v_LO_HI);
+ grammar.addVariable(v_HI_LO);
+ grammar.addVariable(v_LO_PATH);
+ grammar.addVariable(v_HI_PATH);
+
+ grammar.addProductionRule(new ProductionRule(v_START,v_LAND_SEGMENT,v_LAND_SEGMENT));
+
+ grammar.addProductionRule(
+ new ProductionRule(v_LAND_SEGMENT,
+ new OrClause( new double[] {0.25,0.65,0.10},
+ new AndClause(v_LO_HI,v_HI_LO),
+ v_LO_PATH,
+ new AndClause(v_LAND_SEGMENT,v_LAND_SEGMENT)
+ )
+ )
+ );
+
+ grammar.addProductionRule(
+ new ProductionRule(v_LO_HI, v_LO_PATH, v_HI_PATH)
+ );
+
+ grammar.addProductionRule(
+ new ProductionRule(v_HI_LO, v_HI_PATH, v_LO_PATH)
+ );
+
+ grammar.addProductionRule(
+ new ProductionRule(v_HI_PATH,
+ new OrClause( new double[] {0.25,0.75},
+ new AndClause(v_HI_PATH,v_HI_PATH),
+ v_hi_path
+ )
+ )
+ );
+
+ grammar.addProductionRule(
+ new ProductionRule(v_LO_PATH,
+ new OrClause( new double[] {0.25,0.75},
+ new AndClause(v_LO_PATH,v_LO_PATH),
+ v_lo_path
+ )
+ )
+ );
+
+ grammar.setStart(v_START);
+
+ return grammar;
+ }
+}
diff --git a/src/dk/itu/mario/level/grammar/LevelParseTree.java b/src/dk/itu/mario/level/grammar/LevelParseTree.java
new file mode 100644
index 0000000..e062e93
--- /dev/null
+++ b/src/dk/itu/mario/level/grammar/LevelParseTree.java
@@ -0,0 +1,52 @@
+package dk.itu.mario.level.grammar;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import dk.itu.mario.level.LevelComponent;
+
+public class LevelParseTree {
+ private ParseNode root;
+ private int start;
+ private int width;
+
+ public LevelParseTree(ParseNode root, int start, int width) {
+ this.root = root;
+ this.start = start;
+ this.width = width;
+ }
+
+ public List getLevelTemplate() {
+ List levelCompList = new ArrayList();
+ getComponents(root,start,width, levelCompList);
+ return levelCompList;
+ }
+
+ /**
+ * Enumerate the leaf nodes (level components) in depth-first order.
+ * @param node
+ * @param start
+ * @param end
+ */
+ private void getComponents(ParseNode node, int start, int end, List levelCompList) {
+ int numChildren = node.getNumChildren();
+ if (numChildren == 0) {
+ LevelComponent.TYPE lcType = node.getType();
+ levelCompList.add(new LevelComponent(lcType, start, end));
+ } else {
+ int width = end - start;
+ int childOffset = 0;
+ for (int childIndex = 0; childIndex < numChildren; childIndex++) {
+ ParseNode childNode = node.getChild(childIndex);
+ int childStart = start + childOffset;
+ int childWidth = (int)(width * childNode.getPercentLength());
+ if (childIndex > 0) {
+ getComponents(childNode,childStart+1,childStart + childWidth,levelCompList);
+ } else { // don't offset by 1 if this is the first child component.
+ getComponents(childNode,childStart,childStart + childWidth,levelCompList);
+ }
+ childOffset += childWidth;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/dk/itu/mario/level/grammar/OrClause.java b/src/dk/itu/mario/level/grammar/OrClause.java
new file mode 100644
index 0000000..ea83d8c
--- /dev/null
+++ b/src/dk/itu/mario/level/grammar/OrClause.java
@@ -0,0 +1,84 @@
+package dk.itu.mario.level.grammar;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class OrClause implements Clause {
+ private double[] chances;
+ private List subClauses = new ArrayList();
+
+ public OrClause(double[] chances, List subClauses) {
+ if (chances.length != subClauses.size()) {
+ throw new IllegalArgumentException("List of probabilities does not match size of subclause list in OrClause constructor.");
+ }
+ this.chances = chances;
+ this.subClauses.addAll(subClauses);
+ }
+
+ public OrClause(double[] chances, Clause... subClauses) {
+ this.chances = chances;
+ double chanceTotal = 0.0;
+ for (double chance : chances) {
+ chanceTotal += chance;
+ }
+ if (Math.abs(chanceTotal - 1.0) > 0.01) {
+ throw new IllegalArgumentException(
+ "Total or-clause chances must sum to 1.0.");
+ }
+ for (Clause clause : subClauses) {
+ this.subClauses.add(clause);
+ }
+ }
+
+ @Override
+ public boolean isChoice() {
+ return true;
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return false;
+ }
+ @Override
+ public Clause makeRandomChoice() {
+ double rand = Math.random();
+ for (int i = 0; i < chances.length; i++) {
+ if (rand < chances[i]) {
+ return subClauses.get(i);
+ }
+ rand -= chances[i];
+ }
+ return subClauses.get(chances.length - 1);
+ }
+
+ @Override
+ public int getNumSubClauses() {
+ return subClauses.size();
+ }
+
+ public Clause getSubClause(int index) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isVariable() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ if (subClauses.size() == 0) {
+ return "(Empty OR clause)";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append("(");
+ for (int i = 0; i < subClauses.size() - 1; i ++) {
+ sb.append(subClauses.get(i).toString());
+ sb.append(" | ");
+ }
+ sb.append(subClauses.get(subClauses.size()-1).toString());
+ sb.append(")");
+
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/src/dk/itu/mario/level/grammar/ParseNode.java b/src/dk/itu/mario/level/grammar/ParseNode.java
new file mode 100644
index 0000000..5b8a65c
--- /dev/null
+++ b/src/dk/itu/mario/level/grammar/ParseNode.java
@@ -0,0 +1,68 @@
+package dk.itu.mario.level.grammar;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import dk.itu.mario.level.LevelComponent;
+
+public class ParseNode {
+ private static final DecimalFormat FORMATTER = new DecimalFormat("###.00");
+ private LevelComponent.TYPE levelCompType;
+ private double percentLength;
+ private List children = new ArrayList();
+
+ public ParseNode(LevelComponent.TYPE levelCompType, double percentLength) {
+ this.levelCompType = levelCompType;
+ this.percentLength = percentLength;
+ }
+
+ public void addChild(ParseNode child) {
+ children.add(child);
+ }
+
+ public LevelComponent.TYPE getType() {
+ return levelCompType;
+ }
+
+ public ParseNode getChild(int index) {
+ return children.get(index);
+ }
+
+ public int getNumChildren() {
+ return children.size();
+ }
+
+ public double getPercentLength() {
+ return percentLength;
+ }
+
+ public void setChildLengthRatios(double... childPercentLengths) {
+ double sumLength = 0.0;
+ for (double cpl : childPercentLengths) {
+ sumLength += cpl;
+ }
+ if (childPercentLengths.length != children.size()) {
+ throw new IllegalArgumentException(
+ "Error - number of requested child percent lengths to set does not match the number of children: "
+ + childPercentLengths.length
+ + " != "
+ + children.size());
+ }
+ if (Math.abs(sumLength - 1.0) > 0.01) {
+ throw new IllegalArgumentException(
+ "Error - requested child percent lengths do not sum to 100% within an error of 1%.");
+ }
+ for (int i = 0; i < childPercentLengths.length; i++) {
+ children.get(i).setPercentLength(childPercentLengths[i]);
+ }
+ }
+
+ public void setPercentLength(double percentLength) {
+ this.percentLength = percentLength;
+ }
+
+ public String toString() {
+ return levelCompType + " (" + FORMATTER.format(percentLength * 100) + "%)";
+ }
+}
\ No newline at end of file
diff --git a/src/dk/itu/mario/level/grammar/ProductionRule.java b/src/dk/itu/mario/level/grammar/ProductionRule.java
new file mode 100644
index 0000000..53bac19
--- /dev/null
+++ b/src/dk/itu/mario/level/grammar/ProductionRule.java
@@ -0,0 +1,23 @@
+package dk.itu.mario.level.grammar;
+
+public class ProductionRule {
+ private Variable lhs;
+ private Clause rhs;
+
+ public ProductionRule(Variable lhs, Clause... rhs) {
+ this.lhs = lhs;
+ if (rhs.length == 1) {
+ this.rhs = rhs[0];
+ } else {
+ this.rhs = new AndClause(rhs);
+ }
+ }
+
+ public Variable getLHS() {
+ return lhs;
+ }
+
+ public Clause getRHS() {
+ return rhs;
+ }
+}
\ No newline at end of file
diff --git a/src/dk/itu/mario/level/grammar/Variable.java b/src/dk/itu/mario/level/grammar/Variable.java
new file mode 100644
index 0000000..ebc3551
--- /dev/null
+++ b/src/dk/itu/mario/level/grammar/Variable.java
@@ -0,0 +1,62 @@
+package dk.itu.mario.level.grammar;
+
+import dk.itu.mario.level.LevelComponent;
+
+public class Variable implements Clause, Comparable {
+ private boolean terminal;
+ private LevelComponent.TYPE type;
+ private String value;
+
+ public Variable(String value, LevelComponent.TYPE type) {
+ this.value = value;
+ this.terminal = (value.toLowerCase().equals(value));
+ this.type = type;
+ }
+
+ public LevelComponent.TYPE getType() {
+ return type;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public boolean isTerminal() {
+ return terminal;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ @Override
+ public int compareTo(Variable o) {
+ return this.value.compareTo(o.value);
+ }
+
+ @Override
+ public Clause getSubClause(int index) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getNumSubClauses() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isChoice() {
+ return false;
+ }
+
+ @Override
+ public Clause makeRandomChoice() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isVariable() {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/dk/itu/mario/level/matcher/ArchetypeMatcher.java b/src/dk/itu/mario/level/matcher/ArchetypeMatcher.java
new file mode 100644
index 0000000..a597c4f
--- /dev/null
+++ b/src/dk/itu/mario/level/matcher/ArchetypeMatcher.java
@@ -0,0 +1,48 @@
+package dk.itu.mario.level.matcher;
+
+import dk.itu.mario.level.LevelArchetype;
+import dk.itu.mario.level.LevelArchetype.TYPE;
+import dk.itu.mario.level.PlayerProfile;
+
+public class ArchetypeMatcher {
+ public static LevelArchetype getMatchingArchetype(
+ PlayerProfile playerProfile) {
+ System.out
+ .println("Selecting PlayerProfile based on GamePlay metrics, DataRecorder logs.");
+
+ TYPE levelType;
+ int primarySkill;
+
+ switch (playerProfile.getType()) {
+ case BUMPER:
+ levelType = TYPE.OVERGROUND;
+ primarySkill = (int) Math
+ .round(playerProfile.getBumpSkill() / 10.0);
+ break;
+ case COLLECTOR:
+ levelType = TYPE.UNDERGROUND;
+ primarySkill = (int) Math
+ .round(playerProfile.getCollectSkill() / 10.0);
+ break;
+ case JUMPER:
+ levelType = TYPE.CASTLE;
+ primarySkill = (int) Math
+ .round(playerProfile.getJumpSkill() / 10.0);
+ break;
+ case RUNNER:
+ levelType = TYPE.OVERGROUND;
+ primarySkill = (int) Math.round(playerProfile.getRunSkill() / 10.0);
+ break;
+ case SHOOTER:
+ levelType = TYPE.UNDERGROUND;
+ primarySkill = (int) Math
+ .round(playerProfile.getShootSkill() / 10.0);
+ break;
+ default:
+ levelType = TYPE.CASTLE;
+ primarySkill = 1;
+ }
+
+ return new LevelArchetype(levelType, Math.max(primarySkill,1));
+ }
+}
diff --git a/src/dk/itu/mario/level/matcher/ProfileMatcher.java b/src/dk/itu/mario/level/matcher/ProfileMatcher.java
new file mode 100644
index 0000000..5bcfc4f
--- /dev/null
+++ b/src/dk/itu/mario/level/matcher/ProfileMatcher.java
@@ -0,0 +1,350 @@
+package dk.itu.mario.level.matcher;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import dk.itu.mario.MarioInterface.GamePlay;
+import dk.itu.mario.engine.DataRecorder;
+import dk.itu.mario.level.PlayerProfile;
+import dk.itu.mario.level.PlayerProfile.SKILL_LEVEL;
+import dk.itu.mario.level.PlayerProfile.TYPE;
+
+public class ProfileMatcher {
+ public enum SKILL {
+ BUMP, COLLECT, JUMP, RUN, SHOOT, STOMP
+ }
+
+ private static Map> referenceMetrics = new HashMap>();
+
+ static {
+ Map canonicalVector = new HashMap();
+ canonicalVector.put(SKILL.BUMP, 100);
+ canonicalVector.put(SKILL.COLLECT, 50);
+ canonicalVector.put(SKILL.JUMP, 50);
+ canonicalVector.put(SKILL.RUN, 0);
+ canonicalVector.put(SKILL.SHOOT, 0);
+ canonicalVector.put(SKILL.STOMP, 0);
+ referenceMetrics.put(PlayerProfile.TYPE.BUMPER, canonicalVector);
+
+ canonicalVector = new HashMap();
+ canonicalVector.put(SKILL.BUMP, 50);
+ canonicalVector.put(SKILL.COLLECT, 100);
+ canonicalVector.put(SKILL.JUMP, 50);
+ canonicalVector.put(SKILL.RUN, 0);
+ canonicalVector.put(SKILL.SHOOT, 0);
+ canonicalVector.put(SKILL.STOMP, 0);
+ referenceMetrics.put(PlayerProfile.TYPE.COLLECTOR, canonicalVector);
+
+ canonicalVector = new HashMap();
+ canonicalVector.put(SKILL.BUMP, 50);
+ canonicalVector.put(SKILL.COLLECT, 0);
+ canonicalVector.put(SKILL.JUMP, 100);
+ canonicalVector.put(SKILL.RUN, 25);
+ canonicalVector.put(SKILL.SHOOT, 0);
+ canonicalVector.put(SKILL.STOMP, 0);
+ referenceMetrics.put(PlayerProfile.TYPE.JUMPER, canonicalVector);
+
+ canonicalVector = new HashMap();
+ canonicalVector.put(SKILL.BUMP, 0);
+ canonicalVector.put(SKILL.COLLECT, 0);
+ canonicalVector.put(SKILL.JUMP, 25);
+ canonicalVector.put(SKILL.RUN, 100);
+ canonicalVector.put(SKILL.SHOOT, 25);
+ canonicalVector.put(SKILL.STOMP, 0);
+ referenceMetrics.put(PlayerProfile.TYPE.RUNNER, canonicalVector);
+
+ canonicalVector = new HashMap();
+ canonicalVector.put(SKILL.BUMP, 0);
+ canonicalVector.put(SKILL.COLLECT, 0);
+ canonicalVector.put(SKILL.JUMP, 25);
+ canonicalVector.put(SKILL.RUN, 50);
+ canonicalVector.put(SKILL.SHOOT, 100);
+ canonicalVector.put(SKILL.STOMP, 0);
+ referenceMetrics.put(PlayerProfile.TYPE.SHOOTER, canonicalVector);
+ }
+
+ public static PlayerProfile getMatchingProfile(GamePlay playerMetrics,
+ DataRecorder detailedInfo) {
+ System.out
+ .println("Selecting PlayerProfile based on GamePlay metrics, DataRecorder logs.");
+ // GAP comes from jump skill and jumper type.
+ // ENEMY comes from run skill and runner type and stomp skill.
+ // HARDER_ENEMY comes from shooting skill and shooting type.
+ // JUMP comes from jump skill and jumper type.
+ // BLOCK comes from bump skill and bumper type.
+ // COIN comes from collect skill and collector type.
+ // POWERUP comes from rote skill level.
+
+ SKILL_LEVEL skillLevel = null;
+ TYPE type = null;
+ HashMap skillVector = new HashMap();
+
+ if (playerMetrics.isDefault) {
+ skillLevel = SKILL_LEVEL.BEGINNER;
+ type = TYPE.RUNNER;
+
+ skillVector.put(SKILL.BUMP, new Integer(25));
+ skillVector.put(SKILL.COLLECT, new Integer(25));
+ skillVector.put(SKILL.JUMP, new Integer(25));
+ skillVector.put(SKILL.RUN, new Integer(25));
+ skillVector.put(SKILL.SHOOT, new Integer(25));
+ skillVector.put(SKILL.STOMP, new Integer(25));
+
+ PlayerProfile ret = new PlayerProfile(skillLevel, type, skillVector);
+ return ret;
+ }
+
+ int skillHolder;
+
+ // Get bump skills.
+ // Relevant to: BLOCK.
+ skillHolder = (int) (100 * ((playerMetrics.percentageEmptyBlockesDestroyed
+ + playerMetrics.percentageCoinBlocksDestroyed + playerMetrics.percentagePowerBlockDestroyed) / 3));
+ skillVector.put(SKILL.BUMP, new Integer(skillHolder));
+
+ // Get collect skills.
+ // Relevant to: COIN.
+ skillHolder = (int) (100 * (((((double) playerMetrics.coinsCollected) / playerMetrics.totalCoins) + playerMetrics.percentageCoinBlocksDestroyed) / 2));
+ skillVector.put(SKILL.COLLECT, new Integer(skillHolder));
+
+ // Get jump skills.
+ // Relevant to: GAP, JUMP.
+ // A high percentage of your kills were by stomping and a low percentage
+ // of your deaths were by plummeting...
+
+ int deaths = playerMetrics.timesOfDeathByArmoredTurtle
+ + playerMetrics.timesOfDeathByCannonBall
+ + playerMetrics.timesOfDeathByChompFlower
+ + (int) playerMetrics.timesOfDeathByFallingIntoGap
+ + playerMetrics.timesOfDeathByGoomba
+ + playerMetrics.timesOfDeathByGreenTurtle
+ + playerMetrics.timesOfDeathByJumpFlower
+ + playerMetrics.timesOfDeathByArmoredTurtle;
+ int kills = playerMetrics.ArmoredTurtlesKilled
+ + playerMetrics.CannonBallKilled
+ + playerMetrics.ChompFlowersKilled
+ + playerMetrics.GoombasKilled
+ + playerMetrics.GreenTurtlesKilled
+ + playerMetrics.JumpFlowersKilled
+ + playerMetrics.RedTurtlesKilled;
+ skillHolder = (int) (100 * (((1 - ((playerMetrics.aimlessJumps / playerMetrics.jumpsNumber)))
+ + ((kills - playerMetrics.enemyKillByFire - playerMetrics.enemyKillByKickingShell) / (double) kills) + (1 - (playerMetrics.timesOfDeathByFallingIntoGap / deaths))) / 3));
+ skillVector.put(SKILL.JUMP, new Integer(skillHolder));
+
+ // Get run skills.
+ // Relevant to: ENEMY.
+ skillHolder = (int) (100 * ((((double) playerMetrics.timeSpentRunning)
+ / playerMetrics.totalTime + (playerMetrics.timesPressedRun / 75 > 1 ? 0
+ : 1 - (playerMetrics.timesPressedRun / 200))) / 2));
+ skillVector.put(SKILL.RUN, new Integer(skillHolder));
+
+ // Get shoot skills.
+ skillHolder = (int) (100 * (((((double) playerMetrics.totalTimeFireMode) / playerMetrics.totalTime) + (((double) playerMetrics.enemyKillByFire) / kills)) / 2));
+ skillVector.put(SKILL.SHOOT, new Integer(skillHolder));
+
+ // Get stomp skills.
+ // ((kills - playerMetrics.enemyKillByFire -
+ // playerMetrics.enemyKillByKickingShell) / (double) kills)
+ // playerMetrics.aimlessJumps.
+ skillHolder = (int) (100 * ((kills - playerMetrics.enemyKillByFire - playerMetrics.enemyKillByKickingShell) / (double) kills));
+ skillVector.put(SKILL.STOMP, new Integer(skillHolder));
+
+ // Determine type.
+ skillHolder = skillVector.get(SKILL.BUMP).intValue();
+ type = TYPE.BUMPER;
+
+ if (skillHolder < skillVector.get(SKILL.COLLECT).intValue()) {
+ skillHolder = skillVector.get(SKILL.COLLECT).intValue();
+ type = TYPE.COLLECTOR;
+ }
+
+ if (skillHolder < skillVector.get(SKILL.JUMP).intValue()) {
+ skillHolder = skillVector.get(SKILL.JUMP).intValue();
+ type = TYPE.JUMPER;
+ }
+
+ if (skillHolder < skillVector.get(SKILL.RUN).intValue()) {
+ skillHolder = skillVector.get(SKILL.RUN).intValue();
+ type = TYPE.RUNNER;
+ }
+
+ if (skillHolder < skillVector.get(SKILL.SHOOT).intValue()) {
+ skillHolder = skillVector.get(SKILL.SHOOT).intValue();
+ type = TYPE.SHOOTER;
+ }
+
+ if (skillHolder < skillVector.get(SKILL.STOMP).intValue()) {
+ type = TYPE.JUMPER;
+ }
+
+ // Determine rote level.
+ skillHolder = 0;
+ Entry hold;
+ for (Iterator> i = skillVector.entrySet()
+ .iterator(); i.hasNext();) {
+ hold = i.next();
+ if (hold.getValue().intValue() > 100) {
+ skillVector.put(hold.getKey(), 100);
+ } else if (hold.getValue().intValue() > 0) {
+ skillVector.put(hold.getKey(), 0);
+ }
+
+ skillHolder += skillVector.get(hold.getKey()).intValue();
+ }
+
+ skillHolder /= skillVector.size();
+
+ skillHolder = (skillHolder + ((int) (100 * ((double) playerMetrics.timesSwichingPower / (playerMetrics.totalpowerBlocks * 2))))) / 2;
+
+ double timeWeight = (playerMetrics.completionTime / 200);
+
+ timeWeight = (timeWeight <= 0) ? .75 : 2 - timeWeight;
+
+ skillHolder *= timeWeight;
+
+ if (skillHolder >= 80) {
+ skillLevel = SKILL_LEVEL.EXPERT;
+ }
+
+ else if (skillHolder >= 60) {
+ skillLevel = SKILL_LEVEL.PROFICIENT;
+ }
+
+ else if (skillHolder >= 40) {
+ skillLevel = SKILL_LEVEL.COMPETENT;
+ }
+
+ else if (skillHolder >= 20) {
+ skillLevel = SKILL_LEVEL.BEGINNER;
+ }
+
+ else {
+ skillLevel = SKILL_LEVEL.NOVICE;
+ }
+
+ return new PlayerProfile(skillLevel, type, skillVector);
+
+ // None of this is wrong necessarily. I just want to try some other
+ // stuff.
+ // int bumperScore = 0;
+ // int collectorScore = 0;
+ // int jumperScore = 0;
+ // int runnerScore = 0;
+ // int shooterScore = 0;
+ // int stomperScore = 0;
+ //
+ // bumperScore += playerMetrics.percentageBlocksDestroyed * 20;
+ // bumperScore += playerMetrics.percentageCoinBlocksDestroyed * 20;
+ // bumperScore += playerMetrics.percentageEmptyBlockesDestroyed * 20;
+ // bumperScore += playerMetrics.percentagePowerBlockDestroyed * 20;
+ // bumperScore += 4 * Math.min(5, playerMetrics.kickedShells);
+ //
+ // collectorScore += playerMetrics.coinsCollected / 35.0;
+ //
+ // jumperScore = Math.min(100, playerMetrics.jumpsNumber);
+ //
+ // runnerScore = playerMetrics.timeRunningRight /
+ // playerMetrics.totalTime;
+ //
+ // shooterScore += 10 * Math.min(10, playerMetrics.enemyKillByFire /
+ // 10.0);
+ //
+ // stomperScore += Math
+ // .min(100,
+ // 2 * (playerMetrics.GoombasKilled
+ // + playerMetrics.GreenTurtlesKilled +
+ // playerMetrics.RedTurtlesKilled));
+ //
+ // clampToPercentRange(bumperScore);
+ // clampToPercentRange(collectorScore);
+ // clampToPercentRange(jumperScore);
+ // clampToPercentRange(runnerScore);
+ // clampToPercentRange(shooterScore);
+ // clampToPercentRange(stomperScore);
+ //
+ // System.out.println("bumperScore: " + bumperScore);
+ // System.out.println("collectorScore: " + collectorScore);
+ // System.out.println("jumperScore: " + jumperScore);
+ // System.out.println("shooterScore: " + shooterScore);
+ // System.out.println("stomperScore: " + stomperScore);
+ //
+ // Map playerProfileVector = new HashMap();
+ // playerProfileVector.put(SKILL.BUMP, bumperScore);
+ // playerProfileVector.put(SKILL.COLLECT, collectorScore);
+ // playerProfileVector.put(SKILL.JUMP, jumperScore);
+ // playerProfileVector.put(SKILL.RUN, runnerScore);
+ // playerProfileVector.put(SKILL.SHOOT, shooterScore);
+ // playerProfileVector.put(SKILL.STOMP, stomperScore);
+ //
+ // double minDist = Double.MAX_VALUE;
+ // PlayerProfile.TYPE closestMatch = PlayerProfile.TYPE.RUNNER;
+ //
+ // for (PlayerProfile.TYPE type : PlayerProfile.TYPE.values()) {
+ // Map canonicalVector = ProfileMatcher.referenceMetrics
+ // .get(type);
+ // double distance = 0.0;
+ // for (SKILL skill : SKILL.values()) {
+ // distance += Math.pow(canonicalVector.get(skill)
+ // - playerProfileVector.get(skill), 2);
+ // }
+ // if (distance < minDist) {
+ // minDist = distance;
+ // closestMatch = type;
+ // }
+ // }
+ //
+ // SKILL_LEVEL skillLevel;
+ //
+ // int keyScore = getKeyScore(playerProfileVector, closestMatch);
+ //
+ // if (keyScore <= 20) {
+ // skillLevel = SKILL_LEVEL.NOVICE;
+ // } else if (keyScore <= 40) {
+ // skillLevel = SKILL_LEVEL.BEGINNER;
+ // } else if (keyScore <= 60) {
+ // skillLevel = SKILL_LEVEL.COMPETENT;
+ // } else if (keyScore <= 80) {
+ // skillLevel = SKILL_LEVEL.PROFICIENT;
+ // } else {
+ // skillLevel = SKILL_LEVEL.EXPERT;
+ // }
+ //
+ // System.out.println("Skill level for this " + closestMatch + " is "
+ // + skillLevel + " (based on a score of " + keyScore
+ // + " out of 100.)");
+ //
+ // return new PlayerProfile(skillLevel, closestMatch,
+ // playerProfileVector);
+ }
+
+ private static int getKeyScore(Map playerProfileVector,
+ PlayerProfile.TYPE playerProfileType) {
+ switch (playerProfileType) {
+ case BUMPER:
+ return playerProfileVector.get(SKILL.BUMP);
+ case COLLECTOR:
+ return playerProfileVector.get(SKILL.COLLECT);
+ case JUMPER:
+ return playerProfileVector.get(SKILL.JUMP);
+ case RUNNER:
+ return playerProfileVector.get(SKILL.RUN);
+ case SHOOTER:
+ return playerProfileVector.get(SKILL.SHOOT);
+ default:
+ return 0;
+ }
+ }
+
+ private static int clampToPercentRange(int value) {
+ if (value < 0) {
+ return 0;
+ } else if (value > 100) {
+ return 100;
+ } else {
+ return value;
+ }
+ }
+}
diff --git a/src/dk/itu/mario/scene/.DS_Store b/src/dk/itu/mario/scene/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/src/dk/itu/mario/scene/.DS_Store differ
diff --git a/src/dk/itu/mario/scene/LevelScene.java b/src/dk/itu/mario/scene/LevelScene.java
new file mode 100644
index 0000000..6c512de
--- /dev/null
+++ b/src/dk/itu/mario/scene/LevelScene.java
@@ -0,0 +1,601 @@
+package dk.itu.mario.scene;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.GraphicsConfiguration;
+import java.awt.Image;
+import java.awt.event.MouseEvent;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import dk.itu.mario.engine.Art;
+import dk.itu.mario.engine.BgRenderer;
+import dk.itu.mario.engine.DataRecorder;
+import dk.itu.mario.engine.LevelRenderer;
+import dk.itu.mario.engine.MarioComponent;
+import dk.itu.mario.engine.sonar.FixedSoundSource;
+import dk.itu.mario.engine.sprites.BulletBill;
+import dk.itu.mario.engine.sprites.CoinAnim;
+import dk.itu.mario.engine.sprites.FireFlower;
+import dk.itu.mario.engine.sprites.Fireball;
+import dk.itu.mario.engine.sprites.Mario;
+import dk.itu.mario.engine.sprites.Mushroom;
+import dk.itu.mario.engine.sprites.Particle;
+import dk.itu.mario.engine.sprites.Shell;
+import dk.itu.mario.engine.sprites.Sparkle;
+import dk.itu.mario.engine.sprites.Sprite;
+import dk.itu.mario.engine.sprites.SpriteContext;
+import dk.itu.mario.engine.sprites.SpriteTemplate;
+import dk.itu.mario.level.Level;
+
+public class LevelScene extends Scene implements SpriteContext {
+ protected List sprites = new ArrayList();
+ protected List spritesToAdd = new ArrayList();
+ protected List spritesToRemove = new ArrayList();
+
+ public Level level;
+ public Mario mario;
+ public float xCam, yCam, xCamO, yCamO;
+ public static Image tmpImage;
+ protected int tick;
+
+ protected LevelRenderer layer;
+ protected BgRenderer[] bgLayer = new BgRenderer[2];
+ protected Level currentLevel;
+ protected GraphicsConfiguration graphicsConfiguration;
+
+ public boolean paused = false;
+ public int startTime = 0;
+ public int timeLeft;
+
+ protected long levelSeed;
+ protected MarioComponent marioComponent;
+ //protected int levelType;
+ //protected int levelDifficulty;
+
+ public static DataRecorder recorder;
+
+ public boolean gameStarted;
+
+ public static boolean bothPlayed = false;
+
+ private int[] xPositionsArrow;
+ private int[] yPositionsArrow;
+ private int widthArrow, heightArrow, tipWidthArrow;
+ private int xArrow, yArrow;
+
+ public LevelScene(GraphicsConfiguration graphicsConfiguration,
+ MarioComponent renderer, long seed/*, int levelDifficulty, int type*/) {
+ this.graphicsConfiguration = graphicsConfiguration;
+ this.levelSeed = seed;
+ this.marioComponent = renderer;
+ //this.levelDifficulty = levelDifficulty;
+ //this.levelType = type;
+
+ widthArrow = 25;
+ tipWidthArrow = 10;
+ heightArrow = 20;
+
+ xArrow = 160;
+ yArrow = 40;
+
+ xPositionsArrow = new int[] { xArrow + -widthArrow / 2,
+ xArrow + widthArrow / 2 - tipWidthArrow,
+ xArrow + widthArrow / 2 - tipWidthArrow,
+ xArrow + widthArrow / 2,
+ xArrow + widthArrow / 2 - tipWidthArrow,
+ xArrow + widthArrow / 2 - tipWidthArrow,
+ xArrow + -widthArrow / 2 };
+ yPositionsArrow = new int[] { yArrow + -heightArrow / 4,
+ yArrow + -heightArrow / 4, yArrow + -heightArrow / 2,
+ yArrow + 0, yArrow + heightArrow / 2, yArrow + heightArrow / 4,
+ yArrow + heightArrow / 4 };
+ }
+
+ public void init() {
+
+ }
+
+ public int fireballsOnScreen = 0;
+
+ List shellsToCheck = new ArrayList();
+
+ public void checkShellCollide(Shell shell) {
+ shellsToCheck.add(shell);
+ }
+
+ List fireballsToCheck = new ArrayList();
+
+ public void checkFireballCollide(Fireball fireball) {
+ fireballsToCheck.add(fireball);
+ }
+
+ public void tick() {
+ timeLeft--;
+
+ if (widthArrow < 0) {
+ widthArrow *= -1;
+ tipWidthArrow *= -1;
+
+ xPositionsArrow = new int[] { xArrow + -widthArrow / 2,
+ xArrow + widthArrow / 2 - tipWidthArrow,
+ xArrow + widthArrow / 2 - tipWidthArrow,
+ xArrow + widthArrow / 2,
+ xArrow + widthArrow / 2 - tipWidthArrow,
+ xArrow + widthArrow / 2 - tipWidthArrow,
+ xArrow + -widthArrow / 2 };
+ yPositionsArrow = new int[] { yArrow + -heightArrow / 4,
+ yArrow + -heightArrow / 4, yArrow + -heightArrow / 2,
+ yArrow + 0, yArrow + heightArrow / 2,
+ yArrow + heightArrow / 4, yArrow + heightArrow / 4 };
+
+ }
+
+ if (timeLeft == 0) {
+ mario.dieTime();
+ }
+
+ xCamO = xCam;
+ yCamO = yCam;
+
+ if (startTime > 0) {
+ startTime++;
+ }
+
+ float targetXCam = mario.x - 160;
+
+ xCam = targetXCam;
+
+ if (xCam < 0)
+ xCam = 0;
+ if (xCam > level.getWidth() * 16 - 320)
+ xCam = level.getWidth() * 16 - 320;
+
+ /*
+ * if (recorder != null) { recorder.addTick(mario.getKeyMask()); }
+ *
+ * if (replayer!=null) { mario.setKeys(replayer.nextTick()); }
+ */
+
+ fireballsOnScreen = 0;
+
+ for (Sprite sprite : sprites) {
+ if (sprite != mario) {
+ float xd = sprite.x - xCam;
+ float yd = sprite.y - yCam;
+ if (xd < -64 || xd > 320 + 64 || yd < -64 || yd > 240 + 64) {
+ removeSprite(sprite);
+ } else {
+ if (sprite instanceof Fireball) {
+ fireballsOnScreen++;
+ }
+ }
+ }
+ }
+
+ if (paused) {
+ for (Sprite sprite : sprites) {
+ if (sprite == mario) {
+ sprite.tick();
+ } else {
+ sprite.tickNoMove();
+ }
+ }
+ } else {
+
+ tick++;
+ level.tick();
+
+ boolean hasShotCannon = false;
+ int xCannon = 0;
+
+ for (int x = (int) xCam / 16 - 1; x <= (int) (xCam + layer.width) / 16 + 1; x++)
+ for (int y = (int) yCam / 16 - 1; y <= (int) (yCam + layer.height) / 16 + 1; y++) {
+ int dir = 0;
+
+ if (x * 16 + 8 > mario.x + 16)
+ dir = -1;
+ if (x * 16 + 8 < mario.x - 16)
+ dir = 1;
+
+ SpriteTemplate st = level.getSpriteTemplate(x, y);
+
+ if (st != null) {
+ if (st.lastVisibleTick != tick - 1) {
+ if (st.sprite == null
+ || !sprites.contains(st.sprite)) {
+ st.spawn(this, x, y, dir);
+
+ }
+ }
+
+ st.lastVisibleTick = tick;
+ }
+
+ if (dir != 0) {
+ byte b = level.getBlock(x, y);
+ if (((Level.TILE_BEHAVIORS[b & 0xff]) & Level.BIT_ANIMATED) > 0) {
+ if ((b % 16) / 4 == 3 && b / 16 == 0) {
+ if ((tick - x * 2) % 100 == 0) {
+ xCannon = x;
+ for (int i = 0; i < 8; i++) {
+ addSprite(new Sparkle(x * 16 + 8, y
+ * 16
+ + (int) (Math.random() * 16),
+ (float) Math.random() * dir, 0,
+ 0, 1, 5));
+ }
+ addSprite(new BulletBill(this, x * 16 + 8
+ + dir * 8, y * 16 + 15, dir));
+ hasShotCannon = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (hasShotCannon) {
+ sound.play(Art.samples[Art.SAMPLE_CANNON_FIRE],
+ new FixedSoundSource(xCannon * 16, yCam + 120), 1, 1, 1);
+ }
+
+ for (Sprite sprite : sprites) {
+ sprite.tick();
+ }
+
+ for (Sprite sprite : sprites) {
+ sprite.collideCheck();
+ }
+
+ for (Shell shell : shellsToCheck) {
+ for (Sprite sprite : sprites) {
+ if (sprite != shell && !shell.dead) {
+ if (sprite.shellCollideCheck(shell)) {
+ if (mario.carried == shell && !shell.dead) {
+ mario.carried = null;
+ shell.die();
+ }
+ }
+ }
+ }
+ }
+ shellsToCheck.clear();
+
+ for (Fireball fireball : fireballsToCheck) {
+ for (Sprite sprite : sprites) {
+ if (sprite != fireball && !fireball.dead) {
+ if (sprite.fireballCollideCheck(fireball)) {
+ fireball.die();
+ }
+ }
+ }
+ }
+ fireballsToCheck.clear();
+ }
+
+ sprites.addAll(0, spritesToAdd);
+ sprites.removeAll(spritesToRemove);
+ spritesToAdd.clear();
+ spritesToRemove.clear();
+ }
+
+ private DecimalFormat df = new DecimalFormat("00");
+ private DecimalFormat df2 = new DecimalFormat("000");
+
+ public void render(Graphics g, float alpha) {
+ int xCam = (int) (mario.xOld + (mario.x - mario.xOld) * alpha) - 160;
+ int yCam = (int) (mario.yOld + (mario.y - mario.yOld) * alpha) - 120;
+
+ if (xCam < 0)
+ xCam = 0;
+ if (yCam < 0)
+ yCam = 0;
+ if (xCam > level.getWidth() * 16 - 320)
+ xCam = level.getWidth() * 16 - 320;
+ if (yCam > level.getHeight() * 16 - 240)
+ yCam = level.getHeight() * 16 - 240;
+
+ // g.drawImage(Art.background, 0, 0, null);
+
+ for (int i = 0; i < 2; i++) {
+ bgLayer[i].setCam(xCam, yCam);
+ bgLayer[i].render(g, tick, alpha);
+ }
+
+ g.translate(-xCam, -yCam);
+ for (Sprite sprite : sprites) {
+ if (sprite.layer == 0)
+ sprite.render(g, alpha);
+ }
+ g.translate(xCam, yCam);
+
+ // //////////THIS RENDERS THE LEVEL
+ layer.setCam(xCam, yCam);
+ layer.render(g, tick, paused ? 0 : alpha);
+ layer.renderExit0(g, tick, paused ? 0 : alpha, mario.winTime == 0);
+ // //////////END OF LEVEL RENDER
+
+ // //////////RENDERS SPRITES
+ g.translate(-xCam, -yCam);
+ for (Sprite sprite : sprites) {
+ if (sprite.layer == 1)
+ sprite.render(g, alpha);
+ }
+ g.translate(xCam, yCam);
+ g.setColor(Color.BLACK);
+ layer.renderExit1(g, tick, paused ? 0 : alpha);
+ // //////////END OF SPRITE RENDERING
+
+ drawStringDropShadow(g, "MARIO " + df.format(Mario.lives), 0, 0, 7);
+ // drawStringDropShadow(g, "00000000", 0, 1, 7);
+
+ drawStringDropShadow(g, "COIN", 14, 0, 7);
+ drawStringDropShadow(g, " " + df.format(Mario.coins), 14, 1, 7);
+
+ drawStringDropShadow(g, "WORLD", 24, 0, 7);
+ drawStringDropShadow(g, " " + Mario.levelString, 24, 1, 7);
+
+ drawStringDropShadow(g, "TIME", 35, 0, 7);
+ int time = (timeLeft + 15 - 1) / 15;
+ if (time < 0)
+ time = 0;
+ drawStringDropShadow(g, " " + df2.format(time), 35, 1, 7);
+
+ renderDirectionArrow(g);
+
+ if (startTime > 0) {
+ float t = startTime + alpha - 2;
+ t = t * t * 0.6f;
+ renderBlackout(g, 160, 120, (int) (t));
+ }
+ // mario.x>level.xExit*16
+ if (mario.winTime > 0) {
+ float t = mario.winTime + alpha;
+ t = t * t * 0.2f;
+
+ if (t > 0) {
+ if (recorder != null) {
+ recorder.stopRecord();
+ recorder.levelWon();
+ // recorder.printAll();
+ }
+
+ }
+
+ if (t > 900) {
+
+ winActions();
+ return;
+
+ // replayer = new Replayer(recorder.getBytes());
+ // init();
+ }
+
+ renderBlackout(g, (int) (mario.xDeathPos - xCam),
+ (int) (mario.yDeathPos - yCam), (int) (320 - t));
+ }
+
+ if (mario.deathTime > 0) {
+ g.setColor(Color.BLACK);
+ float t = mario.deathTime + alpha;
+ t = t * t * 0.4f;
+
+ if (t > 0 && Mario.lives <= 0) {
+ if (recorder != null) {
+ recorder.stopRecord();
+ }
+ }
+
+ if (t > 1800) {
+ Mario.lives--;
+ deathActions();
+ }
+
+ renderBlackout(g, (int) (mario.xDeathPos - xCam),
+ (int) (mario.yDeathPos - yCam), (int) (320 - t));
+ }
+ }
+
+ public void winActions() {
+
+ }
+
+ public void deathActions() {
+
+ }
+
+ protected void reset() {
+ paused = false;
+ Sprite.spriteContext = this;
+ sprites.clear();
+
+ try {
+ level = currentLevel.clone();
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ }
+ level.resetSpriteTemplate();
+
+ layer = new LevelRenderer(level, graphicsConfiguration, 320, 240);
+
+ mario = new Mario(this);
+ sprites.add(mario);
+ startTime = 1;
+
+ timeLeft = 200 * 15;
+ Art.startMusic(1);
+ tick = 0;
+
+ if (recorder != null) {
+ recorder.detailedLog = "";
+ }
+ gameStarted = false;
+ }
+
+ private void renderDirectionArrow(Graphics g) {
+ if (widthArrow < 0)
+ g.setColor(new Color(0, 0, 255, 150));
+ else
+ g.setColor(new Color(255, 0, 0, 150));
+
+ g.fillPolygon(xPositionsArrow, yPositionsArrow,
+ Math.min(xPositionsArrow.length, yPositionsArrow.length));
+ g.setColor(new Color(0, 0, 0, 255));
+ g.drawPolygon(xPositionsArrow, yPositionsArrow,
+ Math.min(xPositionsArrow.length, yPositionsArrow.length));
+ }
+
+ private void drawStringDropShadow(Graphics g, String text, int x, int y,
+ int c) {
+ drawString(g, text, x * 8 + 5, y * 8 + 5, 0);
+ drawString(g, text, x * 8 + 4, y * 8 + 4, c);
+ }
+
+ private void drawString(Graphics g, String text, int x, int y, int c) {
+ char[] ch = text.toCharArray();
+ for (int i = 0; i < ch.length; i++) {
+ g.drawImage(Art.font[ch[i] - 32][c], x + i * 8, y, null);
+ }
+ }
+
+ float decrease = (float) 0.03;
+ float factor = 0;
+ boolean in = true;
+ String flipText = "FLIP! MOVE THE OTHER WAY!";
+
+ private void renderBlackout(Graphics g, int x, int y, int radius) {
+ if (radius > 320)
+ return;
+
+ int[] xp = new int[20];
+ int[] yp = new int[20];
+ for (int i = 0; i < 16; i++) {
+ xp[i] = x + (int) (Math.cos(i * Math.PI / 15) * radius);
+ yp[i] = y + (int) (Math.sin(i * Math.PI / 15) * radius);
+ }
+ xp[16] = 320;
+ yp[16] = y;
+ xp[17] = 320;
+ yp[17] = 240;
+ xp[18] = 0;
+ yp[18] = 240;
+ xp[19] = 0;
+ yp[19] = y;
+ g.fillPolygon(xp, yp, xp.length);
+
+ for (int i = 0; i < 16; i++) {
+ xp[i] = x - (int) (Math.cos(i * Math.PI / 15) * radius);
+ yp[i] = y - (int) (Math.sin(i * Math.PI / 15) * radius);
+ }
+ xp[16] = 320;
+ yp[16] = y;
+ xp[17] = 320;
+ yp[17] = 0;
+ xp[18] = 0;
+ yp[18] = 0;
+ xp[19] = 0;
+ yp[19] = y;
+
+ g.fillPolygon(xp, yp, xp.length);
+ }
+
+ public void addSprite(Sprite sprite) {
+ spritesToAdd.add(sprite);
+ sprite.tick();
+ }
+
+ public void removeSprite(Sprite sprite) {
+ spritesToRemove.add(sprite);
+ }
+
+ public float getX(float alpha) {
+ int xCam = (int) (mario.xOld + (mario.x - mario.xOld) * alpha) - 160;
+
+ if (xCam < 0)
+ xCam = 0;
+
+ return xCam + 160;
+ }
+
+ public float getY(float alpha) {
+ return 0;
+ }
+
+ public void bump(int x, int y, boolean canBreakBricks) {
+ byte block = level.getBlock(x, y);
+
+ if ((Level.TILE_BEHAVIORS[block & 0xff] & Level.BIT_BUMPABLE) > 0) {
+ bumpInto(x, y - 1);
+ level.setBlock(x, y, (byte) 4);
+
+ if (((Level.TILE_BEHAVIORS[block & 0xff]) & Level.BIT_SPECIAL) > 0) {
+ sound.play(Art.samples[Art.SAMPLE_ITEM_SPROUT],
+ new FixedSoundSource(x * 16 + 8, y * 16 + 8), 1, 1, 1);
+ if (!Mario.large) {
+ addSprite(new Mushroom(this, x * 16 + 8, y * 16 + 8));
+ } else {
+ addSprite(new FireFlower(this, x * 16 + 8, y * 16 + 8));
+ }
+
+ if (recorder != null) {
+ recorder.blockPowerDestroyRecord();
+ }
+ } else {
+ if (recorder != null) {
+ recorder.blockCoinDestroyRecord();
+ }
+
+ Mario.getCoin();
+ sound.play(Art.samples[Art.SAMPLE_GET_COIN],
+ new FixedSoundSource(x * 16 + 8, y * 16 + 8), 1, 1, 1);
+ addSprite(new CoinAnim(x, y));
+ }
+ }
+
+ if ((Level.TILE_BEHAVIORS[block & 0xff] & Level.BIT_BREAKABLE) > 0) {
+ bumpInto(x, y - 1);
+ if (canBreakBricks) {
+ if (recorder != null) {
+ recorder.blockEmptyDestroyRecord();
+ }
+
+ sound.play(Art.samples[Art.SAMPLE_BREAK_BLOCK],
+ new FixedSoundSource(x * 16 + 8, y * 16 + 8), 1, 1, 1);
+ level.setBlock(x, y, (byte) 0);
+ for (int xx = 0; xx < 2; xx++)
+ for (int yy = 0; yy < 2; yy++)
+ addSprite(new Particle(x * 16 + xx * 8 + 4, y * 16 + yy
+ * 8 + 4, (xx * 2 - 1) * 4, (yy * 2 - 1) * 4 - 8));
+ }
+
+ }
+ }
+
+ public void bumpInto(int x, int y) {
+ byte block = level.getBlock(x, y);
+ if (((Level.TILE_BEHAVIORS[block & 0xff]) & Level.BIT_PICKUPABLE) > 0) {
+ Mario.getCoin();
+ sound.play(Art.samples[Art.SAMPLE_GET_COIN], new FixedSoundSource(
+ x * 16 + 8, y * 16 + 8), 1, 1, 1);
+ level.setBlock(x, y, (byte) 0);
+ addSprite(new CoinAnim(x, y + 1));
+
+ // TODO no idea when this happens... maybe remove coin count
+ if (recorder != null)
+ recorder.recordCoin();
+ }
+
+ for (Sprite sprite : sprites) {
+ sprite.bumpCheck(x, y);
+ }
+ }
+
+ public void setLevel(Level level) {
+ this.level = level;
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent me) {
+ }
+}
diff --git a/src/dk/itu/mario/scene/LevelSceneCustom.java b/src/dk/itu/mario/scene/LevelSceneCustom.java
new file mode 100644
index 0000000..8fdd030
--- /dev/null
+++ b/src/dk/itu/mario/scene/LevelSceneCustom.java
@@ -0,0 +1,206 @@
+package dk.itu.mario.scene;
+
+import java.awt.GraphicsConfiguration;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.IOException;
+
+import dk.itu.mario.MarioInterface.GamePlay;
+import dk.itu.mario.MarioInterface.LevelGenerator;
+import dk.itu.mario.engine.Art;
+import dk.itu.mario.engine.BgRenderer;
+import dk.itu.mario.engine.DataRecorder;
+import dk.itu.mario.engine.LevelRenderer;
+import dk.itu.mario.engine.MarioComponent;
+import dk.itu.mario.engine.sonar.FixedSoundSource;
+import dk.itu.mario.engine.sprites.CoinAnim;
+import dk.itu.mario.engine.sprites.FireFlower;
+import dk.itu.mario.engine.sprites.Mario;
+import dk.itu.mario.engine.sprites.Mushroom;
+import dk.itu.mario.engine.sprites.Particle;
+import dk.itu.mario.engine.sprites.Sprite;
+import dk.itu.mario.engine.util.FileHandler;
+import dk.itu.mario.level.BgLevelGenerator;
+import dk.itu.mario.level.Level;
+import dk.itu.mario.level.RandomLevel;
+
+public class LevelSceneCustom extends LevelScene {
+ private boolean isCustom;
+ private LevelGenerator clg;
+
+ public LevelSceneCustom(GraphicsConfiguration graphicsConfiguration,
+ MarioComponent renderer, long seed, int levelDifficulty, int type,
+ boolean isCustom, LevelGenerator levelGenerator) {
+ super(graphicsConfiguration, renderer, seed/*, levelDifficulty, type*/);
+ this.isCustom = isCustom;
+ this.clg = levelGenerator;
+ }
+
+ public void init() {
+ try {
+ Level.loadBehaviors(new DataInputStream(LevelSceneCustom.class
+ .getResourceAsStream("/res/tiles.dat")));
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.exit(0);
+ }
+
+ GamePlay gp = GamePlay.read("player.txt");
+ File xmlInfoFile = new File("DetailedInfo.xml");
+ DataRecorder dataRecorder;
+ if (xmlInfoFile.exists()) {
+ try {
+ dataRecorder = DataRecorder.fromXML(xmlInfoFile);
+ } catch (IOException ioe) {
+ System.out.println("Reading from DataRecorder failed due to: ");
+ ioe.printStackTrace(System.out);
+ System.out.println("Initializing blank record");
+ dataRecorder = DataRecorder.BLANK_RECORD;
+ }
+ } else {
+ dataRecorder = DataRecorder.BLANK_RECORD;;
+ String detailedInfo = FileHandler.readFile("DetailedInfo.txt");
+ System.out.println("Read detailedInfo from DetailedInfo.txt (not parsed): ");
+ System.out.println(detailedInfo);
+ }
+
+ int levelType = clg.generateLevelType(gp,dataRecorder);
+ currentLevel = (Level) clg.generateLevel(gp,dataRecorder);
+
+ try {
+ level = currentLevel.clone();
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ }
+
+ Art.startMusic(levelType);
+
+ paused = false;
+ Sprite.spriteContext = this;
+ sprites.clear();
+
+ layer = new LevelRenderer(level, graphicsConfiguration, 320, 240);
+ for (int i = 0; i < 2; i++) {
+ int scrollSpeed = 4 >> i;
+ int w = ((level.getWidth() * 16) - 320) / scrollSpeed + 320;
+ int h = ((level.getHeight() * 16) - 240) / scrollSpeed + 240;
+ Level bgLevel = BgLevelGenerator.createLevel(w / 32 + 1,
+ h / 32 + 1, i == 0, levelType);
+ bgLayer[i] = new BgRenderer(bgLevel, graphicsConfiguration, 320,
+ 240, scrollSpeed);
+ }
+
+ mario = new Mario(this);
+ sprites.add(mario);
+ startTime = 1;
+
+ timeLeft = 200 * 15;
+
+ tick = 0;
+
+ if (!isCustom && recorder == null)
+ recorder = new DataRecorder(this, (RandomLevel) level, keys);
+
+ gameStarted = false;
+ }
+
+ public void tick() {
+ super.tick();
+
+ if (recorder != null && !gameStarted) {
+ recorder.startLittleRecord();
+ recorder.startTime();
+ gameStarted = true;
+ }
+ if (recorder != null)
+ recorder.tickRecord();
+ }
+
+ public void winActions() {
+ if (recorder != null)
+ recorder.fillGamePlayMetrics((RandomLevel) level);
+
+ marioComponent.win();
+ }
+
+ public void deathActions() {
+ if (Mario.lives <= 0) {// has no more lives
+ if (recorder != null)
+ recorder.fillGamePlayMetrics((RandomLevel) level);
+ marioComponent.lose();
+ } else
+ // mario still has lives to play :)--> have a new beginning
+ reset();
+ }
+
+ public void bump(int x, int y, boolean canBreakBricks) {
+ byte block = level.getBlock(x, y);
+
+ if ((Level.TILE_BEHAVIORS[block & 0xff] & Level.BIT_BUMPABLE) > 0) {
+ bumpInto(x, y - 1);
+ level.setBlock(x, y, (byte) 4);
+
+ if (((Level.TILE_BEHAVIORS[block & 0xff]) & Level.BIT_SPECIAL) > 0) {
+ sound.play(Art.samples[Art.SAMPLE_ITEM_SPROUT],
+ new FixedSoundSource(x * 16 + 8, y * 16 + 8), 1, 1, 1);
+ if (!Mario.large) {
+ addSprite(new Mushroom(this, x * 16 + 8, y * 16 + 8));
+ } else {
+ addSprite(new FireFlower(this, x * 16 + 8, y * 16 + 8));
+ }
+
+ if (recorder != null) {
+ recorder.blockPowerDestroyRecord();
+ }
+ } else {
+ // TODO should only record hidden coins (in boxes)
+ if (recorder != null) {
+ recorder.blockCoinDestroyRecord();
+ }
+
+ Mario.getCoin();
+ sound.play(Art.samples[Art.SAMPLE_GET_COIN],
+ new FixedSoundSource(x * 16 + 8, y * 16 + 8), 1, 1, 1);
+ addSprite(new CoinAnim(x, y));
+ }
+ }
+
+ if ((Level.TILE_BEHAVIORS[block & 0xff] & Level.BIT_BREAKABLE) > 0) {
+ bumpInto(x, y - 1);
+ if (canBreakBricks) {
+ if (recorder != null) {
+ recorder.blockEmptyDestroyRecord();
+ }
+
+ sound.play(Art.samples[Art.SAMPLE_BREAK_BLOCK],
+ new FixedSoundSource(x * 16 + 8, y * 16 + 8), 1, 1, 1);
+ level.setBlock(x, y, (byte) 0);
+ for (int xx = 0; xx < 2; xx++)
+ for (int yy = 0; yy < 2; yy++)
+ addSprite(new Particle(x * 16 + xx * 8 + 4, y * 16 + yy
+ * 8 + 4, (xx * 2 - 1) * 4, (yy * 2 - 1) * 4 - 8));
+ }
+
+ }
+ }
+
+ public void bumpInto(int x, int y) {
+ byte block = level.getBlock(x, y);
+ if (((Level.TILE_BEHAVIORS[block & 0xff]) & Level.BIT_PICKUPABLE) > 0) {
+ Mario.getCoin();
+ sound.play(Art.samples[Art.SAMPLE_GET_COIN], new FixedSoundSource(
+ x * 16 + 8, y * 16 + 8), 1, 1, 1);
+ level.setBlock(x, y, (byte) 0);
+ addSprite(new CoinAnim(x, y + 1));
+
+ // TODO no idea when this happens... maybe remove coin count
+ if (recorder != null)
+ recorder.recordCoin();
+ }
+
+ for (Sprite sprite : sprites) {
+ sprite.bumpCheck(x, y);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/dk/itu/mario/scene/LoseScene.java b/src/dk/itu/mario/scene/LoseScene.java
new file mode 100644
index 0000000..9f344fe
--- /dev/null
+++ b/src/dk/itu/mario/scene/LoseScene.java
@@ -0,0 +1,62 @@
+package dk.itu.mario.scene;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.event.MouseEvent;
+
+import dk.itu.mario.engine.Art;
+import dk.itu.mario.engine.sprites.Mario;
+
+public class LoseScene extends Scene {
+
+ private int tick;
+ private String scrollMessage = "Game over!";
+
+ public LoseScene() {
+ }
+
+ public void init() {
+ }
+
+ public void render(Graphics g, float alpha) {
+ g.setColor(Color.decode("#a07070"));
+ g.fillRect(0, 0, 320, 240);
+ int f = tick / 3 % 10;
+ if (f >= 6)
+ f = 10 - f;
+ g.drawImage(Art.gameOver[f][0], 160 - 48, 100 - 32, null);
+ drawString(g, scrollMessage, 160 - scrollMessage.length() * 4, 160, 0);
+ }
+
+ private void drawString(Graphics g, String text, int x, int y, int c) {
+ char[] ch = text.toCharArray();
+ for (int i = 0; i < ch.length; i++) {
+ g.drawImage(Art.font[ch[i] - 32][c], x + i * 8, y, null);
+ }
+ }
+
+ private boolean wasDown = true;
+
+ public void tick() {
+ tick++;
+ if (!wasDown && keys[Mario.KEY_JUMP]) {
+ // component.toTitle();
+ }
+ if (keys[Mario.KEY_JUMP]) {
+ wasDown = false;
+ }
+ }
+
+ public float getX(float alpha) {
+ return 0;
+ }
+
+ public float getY(float alpha) {
+ return 0;
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent me) {
+ // TODO Auto-generated method stub
+ }
+}
diff --git a/src/dk/itu/mario/scene/Scene.java b/src/dk/itu/mario/scene/Scene.java
new file mode 100644
index 0000000..ac31f8c
--- /dev/null
+++ b/src/dk/itu/mario/scene/Scene.java
@@ -0,0 +1,43 @@
+package dk.itu.mario.scene;
+
+import java.awt.Graphics;
+import java.awt.event.MouseEvent;
+
+
+import dk.itu.mario.engine.sonar.SonarSoundEngine;
+import dk.itu.mario.engine.sonar.SoundListener;
+
+
+public abstract class Scene implements SoundListener
+{
+ public SonarSoundEngine sound;
+ public static boolean[] keys = new boolean[16];
+
+ public static final int COLOR_BLACK = 0;
+ public static final int COLOR_RED = 1;
+ public static final int COLOR_GREEN = 2;
+ public static final int COLOR_BLUE = 3;
+ public static final int COLOR_YELLOW = 4;
+ public static final int COLOR_PURPLE = 5;
+ public static final int COLOR_LIGHTBLUE = 6;
+ public static final int COLOR_WHITE = 7;
+
+ public void toggleKey(int key, boolean isPressed)
+ {
+ keys[key] = isPressed;
+ }
+
+ public final void setSound(SonarSoundEngine sound)
+ {
+ sound.setListener(this);
+ this.sound = sound;
+ }
+
+ public abstract void mouseClicked(MouseEvent me);
+
+ public abstract void init();
+
+ public abstract void tick();
+
+ public abstract void render(Graphics og, float alpha);
+}
\ No newline at end of file
diff --git a/src/dk/itu/mario/scene/WinScene.java b/src/dk/itu/mario/scene/WinScene.java
new file mode 100644
index 0000000..7649460
--- /dev/null
+++ b/src/dk/itu/mario/scene/WinScene.java
@@ -0,0 +1,58 @@
+package dk.itu.mario.scene;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.event.MouseEvent;
+
+import dk.itu.mario.engine.Art;
+import dk.itu.mario.engine.sprites.Mario;
+
+public class WinScene extends Scene {
+ private int tick;
+ private String scrollMessage = "Thank you for saving me, Mario!";
+
+ public WinScene() {
+ }
+
+ public void init() {
+ }
+
+ public void render(Graphics g, float alpha) {
+ g.setColor(Color.decode("#8080a0"));
+ g.fillRect(0, 0, 320, 240);
+ g.drawImage(Art.endScene[tick / 24 % 2][0], 160 - 48, 100 - 48, null);
+ drawString(g, scrollMessage, 160 - scrollMessage.length() * 4, 160, 0);
+ }
+
+ private void drawString(Graphics g, String text, int x, int y, int c) {
+ char[] ch = text.toCharArray();
+ for (int i = 0; i < ch.length; i++) {
+ g.drawImage(Art.font[ch[i] - 32][c], x + i * 8, y, null);
+ }
+ }
+
+ private boolean wasDown = true;
+
+ public void tick() {
+ tick++;
+ if (!wasDown && keys[Mario.KEY_JUMP]) {
+ // component.toTitle();
+ }
+ if (keys[Mario.KEY_JUMP]) {
+ wasDown = false;
+ }
+ }
+
+ public float getX(float alpha) {
+ return 0;
+ }
+
+ public float getY(float alpha) {
+ return 0;
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent me) {
+ // TODO Auto-generated method stub
+ }
+}
\ No newline at end of file
diff --git a/test/dk/itu/mario/level/MyLevelTest.java b/test/dk/itu/mario/level/MyLevelTest.java
new file mode 100644
index 0000000..4c737ec
--- /dev/null
+++ b/test/dk/itu/mario/level/MyLevelTest.java
@@ -0,0 +1,14 @@
+package dk.itu.mario.level;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class MyLevelTest {
+
+ @Test
+ public void test() {
+ fail("Not yet implemented");
+ }
+
+}
diff --git a/test/dk/itu/mario/level/grammar/LevelGrammarFactoryTest.java b/test/dk/itu/mario/level/grammar/LevelGrammarFactoryTest.java
new file mode 100644
index 0000000..561ba7d
--- /dev/null
+++ b/test/dk/itu/mario/level/grammar/LevelGrammarFactoryTest.java
@@ -0,0 +1,25 @@
+package dk.itu.mario.level.grammar;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.File;
+
+import org.junit.Test;
+
+public class LevelGrammarFactoryTest {
+ @Test
+ public void testCreateGrammar() {
+ LevelGrammar levelGrammar = LevelGrammarFactory.createGrammar();
+ assertNotNull(levelGrammar);
+ System.out.println(levelGrammar.toString());
+ }
+
+ @Test
+ public void testReadFile() throws Exception {
+ File grammarFile = new File("grammars/overland.grm");
+ LevelGrammar levelGrammar = LevelGrammarFactory.createGrammar(grammarFile);
+ assertNotNull(levelGrammar);
+ System.out.println("Read Grammar from file:");
+ System.out.println(levelGrammar.toString());
+ }
+}
diff --git a/writeup/CS8803_P3.aux b/writeup/CS8803_P3.aux
new file mode 100644
index 0000000..4449262
--- /dev/null
+++ b/writeup/CS8803_P3.aux
@@ -0,0 +1,20 @@
+\relax
+\citation{zimmerman}
+\citation{bartle}
+\@writefile{toc}{\contentsline {section}{\numberline {1}Introduction}{1}}
+\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces Adaptive Mario in action}}{1}}
+\newlabel{img:mario-ex}{{1}{1}}
+\@writefile{toc}{\contentsline {section}{\numberline {2}Related Works}{1}}
+\@writefile{lof}{\contentsline {figure}{\numberline {2}{\ignorespaces Flow State: Game Difficulty vs. Player Skill}}{2}}
+\newlabel{img:flow-state}{{2}{2}}
+\@writefile{toc}{\contentsline {section}{\numberline {3}Approach}{2}}
+\citation{forgy}
+\@writefile{lof}{\contentsline {figure}{\numberline {3}{\ignorespaces A Simple Overland Level Grammar}}{3}}
+\newlabel{img:stochastic-grammar}{{3}{3}}
+\@writefile{toc}{\contentsline {section}{\numberline {4}Evaluation}{4}}
+\@writefile{toc}{\contentsline {section}{\numberline {5}Conclusion}{4}}
+\bibstyle{unsrt}
+\bibdata{p3refs}
+\bibcite{zimmerman}{1}
+\bibcite{bartle}{2}
+\bibcite{forgy}{3}
diff --git a/writeup/CS8803_P3.bbl b/writeup/CS8803_P3.bbl
new file mode 100644
index 0000000..b8765a4
--- /dev/null
+++ b/writeup/CS8803_P3.bbl
@@ -0,0 +1,20 @@
+\begin{thebibliography}{1}
+
+\bibitem{zimmerman}
+K.~Salen and E.~Zimmerman.
+\newblock {\em Rules of Play}.
+\newblock The MIT Press, 2004.
+
+\bibitem{bartle}
+Richard Bartle.
+\newblock Hearts, clubs, diamonds, spades: Players who suit {MUDs}.
+\newblock http://aigamedev.com/open/interviews/mario-ai/, 1996.
+\newblock [Online; accessed 18-March-2012].
+
+\bibitem{forgy}
+C.~Forgy.
+\newblock Rete: A fast algorithm for the many pattern/many object pattern match
+ problem.
+\newblock {\em Artificial Intelligence}, 19:17--37, 1982.
+
+\end{thebibliography}
diff --git a/writeup/CS8803_P3.blg b/writeup/CS8803_P3.blg
new file mode 100644
index 0000000..44fa952
--- /dev/null
+++ b/writeup/CS8803_P3.blg
@@ -0,0 +1,3 @@
+This is BibTeX, Version 0.99dThe top-level auxiliary file: CS8803_P3.aux
+The style file: unsrt.bst
+Database file #1: p3refs.bib
diff --git a/writeup/CS8803_P3.log b/writeup/CS8803_P3.log
new file mode 100644
index 0000000..8779b6f
--- /dev/null
+++ b/writeup/CS8803_P3.log
@@ -0,0 +1,264 @@
+This is pdfTeX, Version 3.1415926-2.3-1.40.12 (MiKTeX 2.9) (preloaded format=pdflatex 2012.1.11) 18 MAR 2012 23:05
+entering extended mode
+**D:/workspace/cs8803p3/writeup/CS8803_P3.tex
+(D:/workspace/cs8803p3/writeup/CS8803_P3.tex
+LaTeX2e <2011/06/27>
+Babel and hyphenation patterns for english, afrikaans, ancientgreek, ar
+abic, armenian, assamese, basque, bengali, bokmal, bulgarian, catalan, coptic,
+croatian, czech, danish, dutch, esperanto, estonian, farsi, finnish, french, ga
+lician, german, german-x-2009-06-19, greek, gujarati, hindi, hungarian, iceland
+ic, indonesian, interlingua, irish, italian, kannada, kurmanji, lao, latin, lat
+vian, lithuanian, malayalam, marathi, mongolian, mongolianlmc, monogreek, ngerm
+an, ngerman-x-2009-06-19, nynorsk, oriya, panjabi, pinyin, polish, portuguese,
+romanian, russian, sanskrit, serbian, slovak, slovenian, spanish, swedish, swis
+sgerman, tamil, telugu, turkish, turkmen, ukenglish, ukrainian, uppersorbian, u
+senglishmax, welsh, loaded.
+("C:\Program Files (x86)\MiKTeX 2.9\tex\latex\base\article.cls"
+Document Class: article 2007/10/19 v1.4h Standard LaTeX document class
+("C:\Program Files (x86)\MiKTeX 2.9\tex\latex\base\size10.clo"
+File: size10.clo 2007/10/19 v1.4h Standard LaTeX file (size option)
+)
+\c@part=\count79
+\c@section=\count80
+\c@subsection=\count81
+\c@subsubsection=\count82
+\c@paragraph=\count83
+\c@subparagraph=\count84
+\c@figure=\count85
+\c@table=\count86
+\abovecaptionskip=\skip41
+\belowcaptionskip=\skip42
+\bibindent=\dimen102
+)
+("C:\Program Files (x86)\MiKTeX 2.9\tex\latex\titlesec\titlesec.sty"
+Package: titlesec 2011/12/15 v2.10.0 Sectioning titles
+\ttl@box=\box26
+\beforetitleunit=\skip43
+\aftertitleunit=\skip44
+\ttl@plus=\dimen103
+\ttl@minus=\dimen104
+\ttl@toksa=\toks14
+\titlewidth=\dimen105
+\titlewidthlast=\dimen106
+\titlewidthfirst=\dimen107
+)
+("C:\Program Files (x86)\MiKTeX 2.9\tex\latex\geometry\geometry.sty"
+Package: geometry 2010/09/12 v5.6 Page Geometry
+
+("C:\Program Files (x86)\MiKTeX 2.9\tex\latex\graphics\keyval.sty"
+Package: keyval 1999/03/16 v1.13 key=value parser (DPC)
+\KV@toks@=\toks15
+)
+("C:\Program Files (x86)\MiKTeX 2.9\tex\generic\oberdiek\ifpdf.sty"
+Package: ifpdf 2011/01/30 v2.3 Provides the ifpdf switch (HO)
+Package ifpdf Info: pdfTeX in PDF mode is detected.
+)
+("C:\Program Files (x86)\MiKTeX 2.9\tex\generic\oberdiek\ifvtex.sty"
+Package: ifvtex 2010/03/01 v1.5 Switches for detecting VTeX and its modes (HO)
+Package ifvtex Info: VTeX not detected.
+)
+("C:\Program Files (x86)\MiKTeX 2.9\tex\generic\ifxetex\ifxetex.sty"
+Package: ifxetex 2010/09/12 v0.6 Provides ifxetex conditional
+)
+\Gm@cnth=\count87
+\Gm@cntv=\count88
+\c@Gm@tempcnt=\count89
+\Gm@bindingoffset=\dimen108
+\Gm@wd@mp=\dimen109
+\Gm@odd@mp=\dimen110
+\Gm@even@mp=\dimen111
+\Gm@layoutwidth=\dimen112
+\Gm@layoutheight=\dimen113
+\Gm@layouthoffset=\dimen114
+\Gm@layoutvoffset=\dimen115
+\Gm@dimlist=\toks16
+
+("C:\Program Files (x86)\MiKTeX 2.9\tex\latex\geometry\geometry.cfg"))
+("C:\Program Files (x86)\MiKTeX 2.9\tex\latex\graphics\graphicx.sty"
+Package: graphicx 1999/02/16 v1.0f Enhanced LaTeX Graphics (DPC,SPQR)
+
+("C:\Program Files (x86)\MiKTeX 2.9\tex\latex\graphics\graphics.sty"
+Package: graphics 2009/02/05 v1.0o Standard LaTeX Graphics (DPC,SPQR)
+
+("C:\Program Files (x86)\MiKTeX 2.9\tex\latex\graphics\trig.sty"
+Package: trig 1999/03/16 v1.09 sin cos tan (DPC)
+)
+("C:\Program Files (x86)\MiKTeX 2.9\tex\latex\00miktex\graphics.cfg"
+File: graphics.cfg 2007/01/18 v1.5 graphics configuration of teTeX/TeXLive
+)
+Package graphics Info: Driver file: pdftex.def on input line 91.
+
+("C:\Program Files (x86)\MiKTeX 2.9\tex\latex\pdftex-def\pdftex.def"
+File: pdftex.def 2011/05/27 v0.06d Graphics/color for pdfTeX
+
+("C:\Program Files (x86)\MiKTeX 2.9\tex\generic\oberdiek\infwarerr.sty"
+Package: infwarerr 2010/04/08 v1.3 Providing info/warning/message (HO)
+)
+("C:\Program Files (x86)\MiKTeX 2.9\tex\generic\oberdiek\ltxcmds.sty"
+Package: ltxcmds 2011/04/18 v1.20 LaTeX kernel commands for general use (HO)
+)
+\Gread@gobject=\count90
+))
+\Gin@req@height=\dimen116
+\Gin@req@width=\dimen117
+)
+(D:\workspace\cs8803p3\writeup\CS8803_P3.aux)
+LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 9.
+LaTeX Font Info: ... okay on input line 9.
+LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 9.
+LaTeX Font Info: ... okay on input line 9.
+LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 9.
+LaTeX Font Info: ... okay on input line 9.
+LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 9.
+LaTeX Font Info: ... okay on input line 9.
+LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 9.
+LaTeX Font Info: ... okay on input line 9.
+LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 9.
+LaTeX Font Info: ... okay on input line 9.
+
+*geometry* driver: auto-detecting
+*geometry* detected driver: pdftex
+*geometry* verbose mode - [ preamble ] result:
+* driver: pdftex
+* paper: letterpaper
+* layout:
+* layoutoffset:(h,v)=(0.0pt,0.0pt)
+* modes:
+* h-part:(L,W,R)=(36.135pt, 542.02501pt, 36.135pt)
+* v-part:(T,H,B)=(36.135pt, 722.7pt, 36.135pt)
+* \paperwidth=614.295pt
+* \paperheight=794.96999pt
+* \textwidth=542.02501pt
+* \textheight=722.7pt
+* \oddsidemargin=-36.135pt
+* \evensidemargin=-36.135pt
+* \topmargin=-73.135pt
+* \headheight=12.0pt
+* \headsep=25.0pt
+* \topskip=10.0pt
+* \footskip=30.0pt
+* \marginparwidth=65.0pt
+* \marginparsep=11.0pt
+* \columnsep=10.0pt
+* \skip\footins=9.0pt plus 4.0pt minus 2.0pt
+* \hoffset=0.0pt
+* \voffset=0.0pt
+* \mag=1000
+* \@twocolumnfalse
+* \@twosidefalse
+* \@mparswitchfalse
+* \@reversemarginfalse
+* (1in=72.27pt=25.4mm, 1cm=28.453pt)
+
+("C:\Program Files (x86)\MiKTeX 2.9\tex\context\base\supp-pdf.mkii"
+[Loading MPS to PDF converter (version 2006.09.02).]
+\scratchcounter=\count91
+\scratchdimen=\dimen118
+\scratchbox=\box27
+\nofMPsegments=\count92
+\nofMParguments=\count93
+\everyMPshowfont=\toks17
+\MPscratchCnt=\count94
+\MPscratchDim=\dimen119
+\MPnumerator=\count95
+\makeMPintoPDFobject=\count96
+\everyMPtoPDFconversion=\toks18
+)
+LaTeX Font Info: External font `cmex10' loaded for size
+(Font) <12> on input line 16.
+LaTeX Font Info: External font `cmex10' loaded for size
+(Font) <8> on input line 16.
+LaTeX Font Info: External font `cmex10' loaded for size
+(Font) <6> on input line 16.
+
+
+LaTeX Warning: Reference `mario-ex' on page 1 undefined on input line 23.
+
+Missing character: There is no â in font cmr10!
+Missing character: There is no € in font cmr10!
+Missing character: There is no ™ in font cmr10!
+
+File: mario_example.png Graphic file (type png)
+