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) { } }