diff --git a/res/levels/second.level b/res/levels/second.level new file mode 100644 index 0000000..993e90c --- /dev/null +++ b/res/levels/second.level @@ -0,0 +1,22 @@ +name:Level Zwai! :D +backgroundMusic:music/ScooterFriendsTurbo8Bit.wav +background:EarthBackground +nextLevel: +- +[0-1] +spawn:EnemyBoss,1,1,50 +[1-2] +spawn:StoneOne,2,1,0 +spawn:StoneOne,2,1,80 +spawn:StoneThree,2,1,90 +spawn:StoneThree,2,1,100 +[2-4] +spawn:EnemyOne,1,5,20 +spawn:StoneOne,4,5,50 +[4-10] +spawn:EnemyTwo,1,10,60 +[10-25] +spawn:EnemyThree,2,4,33 +spawn:EnemyTwo,5,6,10 +[25-30] +spawn:EnemyBoss,1,1,50 diff --git a/res/levels/test.level b/res/levels/test.level index c22ddb7..53bec45 100644 --- a/res/levels/test.level +++ b/res/levels/test.level @@ -1,6 +1,7 @@ name:Testlevel \o/ backgroundMusic:music/ScooterFriendsTurbo8Bit.wav background:CloudBackground +nextLevel:levels/second.level - [0-1] spawn:EnemyBoss,1,1,50 diff --git a/src/de/teamteamteam/spacescooter/brain/GameConfig.java b/src/de/teamteamteam/spacescooter/brain/GameConfig.java index 6b2e983..64abe31 100644 --- a/src/de/teamteamteam/spacescooter/brain/GameConfig.java +++ b/src/de/teamteamteam/spacescooter/brain/GameConfig.java @@ -78,6 +78,11 @@ public final class GameConfig { */ public static final int initialPlayerShotDamage = 10; + /** + * The first level the game will start with. + */ + public static final String firstLevel = "levels/test.level"; + /** * Private constructor, this class will never be instantiated. */ diff --git a/src/de/teamteamteam/spacescooter/brain/PlayerSession.java b/src/de/teamteamteam/spacescooter/brain/PlayerSession.java index 1a704d6..458d0b2 100644 --- a/src/de/teamteamteam/spacescooter/brain/PlayerSession.java +++ b/src/de/teamteamteam/spacescooter/brain/PlayerSession.java @@ -57,6 +57,11 @@ public class PlayerSession { */ private static int secondaryWeapon; + /** + * The next level to load for the player. + */ + private static String nextLevel; + /** * Private constructor, this class will never be instantiated. @@ -237,6 +242,19 @@ public class PlayerSession { PlayerSession.secondaryWeapon = secondaryWeapon; } + /** + * Get the next Level the player will play. + */ + public static String getNextLevel() { + return PlayerSession.nextLevel; + } + + /** + * Set the next Level the player will play. + */ + public static void setNextLevel(String nextLevel) { + PlayerSession.nextLevel = nextLevel; + } /** * This will reset all data from the players session. @@ -245,6 +263,7 @@ public class PlayerSession { * (So the next player can start a fresh session.) */ public static void reset() { + PlayerSession.nextLevel = GameConfig.firstLevel; PlayerSession.score = 0; PlayerSession.secondaryWeapon = 1; PlayerSession.credits = 0; @@ -255,4 +274,5 @@ public class PlayerSession { PlayerSession.shipShieldUpgadesBought = 0; PlayerSession.shipShotUpgadesBought = 0; } + } diff --git a/src/de/teamteamteam/spacescooter/entity/Entity.java b/src/de/teamteamteam/spacescooter/entity/Entity.java index 1073d79..40b49ff 100644 --- a/src/de/teamteamteam/spacescooter/entity/Entity.java +++ b/src/de/teamteamteam/spacescooter/entity/Entity.java @@ -200,6 +200,15 @@ public abstract class Entity implements Updateable, Paintable { g.drawImage(this.img, this.x, this.y, null); } + /** + * Make sure an Entity removes itself when off-screen. + */ + public void update() { + if(this.getX() + this.getImageWidth() < 0) { + this.remove(); + } + } + /** * Removes entity from the game by telling the current Screen * to remove it from its list. diff --git a/src/de/teamteamteam/spacescooter/entity/ShootingEntity.java b/src/de/teamteamteam/spacescooter/entity/ShootingEntity.java index 808049c..ae17cbd 100644 --- a/src/de/teamteamteam/spacescooter/entity/ShootingEntity.java +++ b/src/de/teamteamteam/spacescooter/entity/ShootingEntity.java @@ -87,6 +87,7 @@ public abstract class ShootingEntity extends LivingEntity { * Update logic making sure that the currentShootDelay is updated. */ public void update() { + super.update(); if(this.currentShootDelay > 0) this.currentShootDelay--; if(this.currentRocketDelay > 0) this.currentRocketDelay--; if(this.currentBeamDelay > 0) this.currentBeamDelay--; diff --git a/src/de/teamteamteam/spacescooter/entity/enemy/Enemy.java b/src/de/teamteamteam/spacescooter/entity/enemy/Enemy.java index 39c5dc2..5012986 100644 --- a/src/de/teamteamteam/spacescooter/entity/enemy/Enemy.java +++ b/src/de/teamteamteam/spacescooter/entity/enemy/Enemy.java @@ -40,13 +40,6 @@ public abstract class Enemy extends ShootingEntity { */ public void update() { super.update(); - - // enemy has fully left the screen ..to the left! - if(this.getX() + this.getImageWidth() < 0){ - this.remove(); - return; - } - if(willShoot == true){ this.shoot(); } diff --git a/src/de/teamteamteam/spacescooter/entity/item/Item.java b/src/de/teamteamteam/spacescooter/entity/item/Item.java index 88020c2..0789175 100644 --- a/src/de/teamteamteam/spacescooter/entity/item/Item.java +++ b/src/de/teamteamteam/spacescooter/entity/item/Item.java @@ -38,9 +38,6 @@ public abstract class Item extends CollidableEntity { */ public void update(){ this.transpose(-1, 0); - if(this.getX() < this.getImageWidth()) { - this.remove(); - } } /** diff --git a/src/de/teamteamteam/spacescooter/entity/obstacle/MovingObstacle.java b/src/de/teamteamteam/spacescooter/entity/obstacle/MovingObstacle.java index 6864d06..ecf7f3f 100644 --- a/src/de/teamteamteam/spacescooter/entity/obstacle/MovingObstacle.java +++ b/src/de/teamteamteam/spacescooter/entity/obstacle/MovingObstacle.java @@ -27,6 +27,7 @@ public abstract class MovingObstacle extends Obstacle { * Make the Obstacle move at its defined X- and Y-Delta. */ public void update() { + super.update(); this.transpose(this.xDelta, this.yDelta); } diff --git a/src/de/teamteamteam/spacescooter/entity/obstacle/Obstacle.java b/src/de/teamteamteam/spacescooter/entity/obstacle/Obstacle.java index ee1db86..988deb7 100644 --- a/src/de/teamteamteam/spacescooter/entity/obstacle/Obstacle.java +++ b/src/de/teamteamteam/spacescooter/entity/obstacle/Obstacle.java @@ -20,5 +20,13 @@ public abstract class Obstacle extends CollidableEntity { public void collideWith(Collidable entity) { } + + /** + * Patch through the update() call. + */ + @Override + public void update() { + super.update(); + } } diff --git a/src/de/teamteamteam/spacescooter/level/Level.java b/src/de/teamteamteam/spacescooter/level/Level.java index 9c67e9b..e1d05a9 100644 --- a/src/de/teamteamteam/spacescooter/level/Level.java +++ b/src/de/teamteamteam/spacescooter/level/Level.java @@ -4,16 +4,21 @@ import de.teamteamteam.spacescooter.background.CloudBackground; import de.teamteamteam.spacescooter.background.EarthBackground; import de.teamteamteam.spacescooter.background.StarBackground; import de.teamteamteam.spacescooter.brain.GameConfig; +import de.teamteamteam.spacescooter.brain.PlayerSession; +import de.teamteamteam.spacescooter.datastructure.ConcurrentIterator; import de.teamteamteam.spacescooter.entity.Entity; import de.teamteamteam.spacescooter.entity.Player; +import de.teamteamteam.spacescooter.entity.enemy.Enemy; import de.teamteamteam.spacescooter.entity.enemy.EnemyBoss; import de.teamteamteam.spacescooter.entity.enemy.EnemyOne; import de.teamteamteam.spacescooter.entity.enemy.EnemyThree; import de.teamteamteam.spacescooter.entity.enemy.EnemyTwo; +import de.teamteamteam.spacescooter.entity.obstacle.Obstacle; import de.teamteamteam.spacescooter.entity.obstacle.StoneOne; import de.teamteamteam.spacescooter.entity.obstacle.StoneThree; import de.teamteamteam.spacescooter.entity.obstacle.StoneTwo; import de.teamteamteam.spacescooter.screen.GameScreen; +import de.teamteamteam.spacescooter.screen.Screen; import de.teamteamteam.spacescooter.sound.SoundSystem; import de.teamteamteam.spacescooter.utility.Loader; @@ -54,17 +59,29 @@ public final class Level { */ private int gameOverDelay; + /** + * Tells how the game over has to be interpreted. + * True - player won, False - player lost. + */ + private boolean playerWon; + + /** + * EntityIterator to evaluate the state of all the existing Entities. + */ + private ConcurrentIterator entityIterator; + /** * Constructor creating a LevelConfig based on a given config file. */ public Level(String levelConfig) { this.levelClock = 0; this.isGameOver = false; + this.playerWon = false; this.gameOverDelay = 3; this.config = Loader.getLevelConfigByFilename(levelConfig); + this.entityIterator = Screen.currentScreen.createEntityIterator(); } - /** * Initialize the level based on the LevelConfig attributes. */ @@ -134,10 +151,29 @@ public final class Level { /** * Evaluates things like whether the Player is alive or * - if there is a bossfight - if the boss is dead. + * Also checks whether the player has survived everything (won) */ private void checkGameOverCondition() { if(!GameScreen.getPlayer().isAlive()) { this.isGameOver = true; + this.playerWon = false; + } + int enemyCounter = 0; + int obstacleCounter = 0; + this.entityIterator.reset(); + while(this.entityIterator.hasNext()) { + Entity e = this.entityIterator.next(); + if(e instanceof Enemy) enemyCounter++; + if(e instanceof Obstacle) obstacleCounter++; + } + + //use the currentIntervalIndex to determine whether there are things scheduled to spawn. + int currentIntervalIndex = this.config.getIntervalIndexByCurrentTime(this.levelClock); + if(enemyCounter == 0 && obstacleCounter == 0 && GameScreen.getPlayer().isAlive() && currentIntervalIndex == -1) { + this.isGameOver = true; + this.playerWon = true; + //Update the next Level + PlayerSession.setNextLevel(this.config.nextLevel); } } @@ -149,6 +185,12 @@ public final class Level { return (this.gameOverDelay == 0); } + /** + * Tell whether the player won the game. + */ + public boolean playerHasWon() { + return this.playerWon; + } /** * Clean up before the Level is torn down. diff --git a/src/de/teamteamteam/spacescooter/level/LevelConfig.java b/src/de/teamteamteam/spacescooter/level/LevelConfig.java index 9a53c88..72181a8 100644 --- a/src/de/teamteamteam/spacescooter/level/LevelConfig.java +++ b/src/de/teamteamteam/spacescooter/level/LevelConfig.java @@ -28,6 +28,11 @@ public class LevelConfig { */ public String backgroundMusic; + /** + * The name of the level that will come after this one. + */ + public String nextLevel; + /** * Intervals have a start and an end. * They are put within this list in a sorted manner, ascending in values. @@ -62,6 +67,8 @@ public class LevelConfig { sb.append(this.background); sb.append(" backgroundMusic="); sb.append(this.backgroundMusic); + sb.append(" nextLevelName="); + sb.append(this.nextLevel); sb.append("\\\n\tRules:\n"); for(int[] rule : this.spawnRuleList) { sb.append("\t"); diff --git a/src/de/teamteamteam/spacescooter/level/LevelConfigParser.java b/src/de/teamteamteam/spacescooter/level/LevelConfigParser.java index 8041ac9..84727a0 100644 --- a/src/de/teamteamteam/spacescooter/level/LevelConfigParser.java +++ b/src/de/teamteamteam/spacescooter/level/LevelConfigParser.java @@ -70,6 +70,8 @@ public class LevelConfigParser { this.levelConfig.background = linePieces[1]; } else if (linePieces[0].equals("backgroundMusic")) { this.levelConfig.backgroundMusic = linePieces[1]; + } else if (linePieces[0].equals("nextLevel")) { + this.levelConfig.nextLevel = linePieces[1]; } else { throw new LevelConfigException("[LevelConfigParser] Unknown attribute in line: '" + line + "'"); } @@ -104,6 +106,12 @@ public class LevelConfigParser { throw new LevelConfigException("[LevelConfigParser] Where am i?!"); } } + + //Set the nextLevel to null explicitly, so it is easy to detect. + if(this.levelConfig.nextLevel.equals("")) { + this.levelConfig.nextLevel = null; + } + return this.levelConfig; } diff --git a/src/de/teamteamteam/spacescooter/screen/GameOverScreen.java b/src/de/teamteamteam/spacescooter/screen/GameOverScreen.java index 6386598..f015177 100644 --- a/src/de/teamteamteam/spacescooter/screen/GameOverScreen.java +++ b/src/de/teamteamteam/spacescooter/screen/GameOverScreen.java @@ -94,7 +94,7 @@ public class GameOverScreen extends Screen { } else if(this.animationStatus == 2) { switch (this.menuPoint) { case 0: - this.parent.setOverlay(new GameScreen(this.parent, "levels/test.level")); + this.parent.setOverlay(new GameScreen(this.parent)); break; case 1: this.parent.setOverlay(new MainMenuScreen(this.parent)); diff --git a/src/de/teamteamteam/spacescooter/screen/GameScreen.java b/src/de/teamteamteam/spacescooter/screen/GameScreen.java index 18ce787..9584ac6 100644 --- a/src/de/teamteamteam/spacescooter/screen/GameScreen.java +++ b/src/de/teamteamteam/spacescooter/screen/GameScreen.java @@ -3,6 +3,7 @@ package de.teamteamteam.spacescooter.screen; import java.awt.Graphics2D; import java.awt.event.KeyEvent; +import de.teamteamteam.spacescooter.brain.PlayerSession; import de.teamteamteam.spacescooter.control.Keyboard; import de.teamteamteam.spacescooter.entity.Player; import de.teamteamteam.spacescooter.gui.HealthBar; @@ -37,9 +38,9 @@ public class GameScreen extends Screen { * GameScreen Constructor. * Takes the level as its second parameter. */ - public GameScreen(Screen parent, String levelConfigName) { + public GameScreen(Screen parent) { super(parent); - this.level = new Level(levelConfigName); + this.level = new Level(PlayerSession.getNextLevel()); this.level.doBuildUp(); //Have the level build up the whole setting. this.gameClockTrigger = 0; @@ -89,9 +90,18 @@ public class GameScreen extends Screen { this.setOverlay(new GamePausedScreen(this), false); } - //Go to GameOverScreen if the game is actually over. + //React if the game is actually over. if (this.level.isGameOver()) { - this.parent.setOverlay(new GameOverScreen(this.parent)); + if(this.level.playerHasWon()) { + if(PlayerSession.getNextLevel() == null) { + System.out.println("You beat the game! TODO: Transition to HighscoreScreen now!"); + } + //Go to the I don't know yet Screen if the game is over and the player WON. + this.parent.setOverlay(new GameWonScreen(this.parent)); + } else { + //Go to GameOverScreen if the game is over - the player died and lost the game. + this.parent.setOverlay(new GameOverScreen(this.parent)); + } } } diff --git a/src/de/teamteamteam/spacescooter/screen/GameWonScreen.java b/src/de/teamteamteam/spacescooter/screen/GameWonScreen.java new file mode 100644 index 0000000..4fc00b2 --- /dev/null +++ b/src/de/teamteamteam/spacescooter/screen/GameWonScreen.java @@ -0,0 +1,100 @@ +package de.teamteamteam.spacescooter.screen; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.event.KeyEvent; +import java.awt.image.BufferedImage; + +import de.teamteamteam.spacescooter.brain.GameConfig; +import de.teamteamteam.spacescooter.control.Keyboard; +import de.teamteamteam.spacescooter.entity.Player; +import de.teamteamteam.spacescooter.gui.Button; +import de.teamteamteam.spacescooter.utility.Loader; + +/** + * This Screen is shown after the player has beaten a level, so he can enter the shop + * and do some various things before entering the next level. + */ +public class GameWonScreen extends Screen { + + private BufferedImage img; + private Player player; + private float playerMoveSpeed = 0; + private int colorValue = 0; + private boolean colorValueIncrease = true; + private int menuPoint = 0; + private int animationStatus = 0; //0 = Noch nicht gestartet, 1 = Animation läuft, 2 = Animation beendet + + public GameWonScreen(Screen parent) { + super(parent); + this.img = Loader.getBufferedImageByFilename("images/pausebackground.png"); + new Button(GameConfig.windowWidth/2-125, 300); + new Button(GameConfig.windowWidth/2-125, 400); + player = new Player(GameConfig.windowWidth/2-170, 309); + player.setCanMove(false); + player.setCanShoot(false); + } + + @Override + protected void paint(Graphics2D g) { + g.drawImage(this.img, 0, 0, null); + this.entityPaintIterator.reset(); + while (this.entityPaintIterator.hasNext()) { + this.entityPaintIterator.next().paint(g); + } + g.setFont(new Font("Monospace", 0, 100)); + g.setColor(new Color(75 + colorValue, 175 + colorValue, 175 + colorValue)); + g.drawString("You win!", GameConfig.windowWidth/2-290, 200); + g.setFont(new Font("Monospace", 0, 20)); + g.setColor(new Color(0, 0, 0)); + g.drawString("Weiter zum nächsten Abenteuer! :D", GameConfig.windowWidth/2-60, 332); + g.drawString("Hauptmen\u00fc", GameConfig.windowWidth/2-60, 432); + } + + @Override + protected void update() { + this.entityUpdateIterator.reset(); + while (this.entityUpdateIterator.hasNext()) { + this.entityUpdateIterator.next().update(); + } + + if(this.colorValueIncrease) { + this.colorValue += 2; + if(this.colorValue > 70) this.colorValueIncrease = false; + } else { + this.colorValue -= 2; + if(this.colorValue < -70) this.colorValueIncrease = true; + } + + if(Keyboard.isKeyDown(KeyEvent.VK_DOWN) && this.animationStatus == 0){ + this.menuPoint = 1; + player.setPosition(player.getX(), 409); + } + if(Keyboard.isKeyDown(KeyEvent.VK_UP) && this.animationStatus == 0){ + this.menuPoint = 0; + player.setPosition(player.getX(), 309); + } + + // make a selection + if(Keyboard.isKeyDown(KeyEvent.VK_ENTER) || Keyboard.isKeyDown(KeyEvent.VK_SPACE)) { + this.animationStatus = 1; + } + if(this.animationStatus == 1) { + if(player.getX() <= GameConfig.windowWidth) { + player.setPosition(player.getX() + (int) playerMoveSpeed, player.getY()); + playerMoveSpeed += 0.1; + } else this.animationStatus = 2; + } else if(this.animationStatus == 2) { + switch (this.menuPoint) { + case 0: + this.parent.setOverlay(new GameScreen(this.parent)); + break; + case 1: + this.parent.setOverlay(new MainMenuScreen(this.parent)); + break; + } + } + } + +} diff --git a/src/de/teamteamteam/spacescooter/screen/MainMenuScreen.java b/src/de/teamteamteam/spacescooter/screen/MainMenuScreen.java index ce23576..31c5394 100644 --- a/src/de/teamteamteam/spacescooter/screen/MainMenuScreen.java +++ b/src/de/teamteamteam/spacescooter/screen/MainMenuScreen.java @@ -99,7 +99,7 @@ public class MainMenuScreen extends Screen { } else if(animationStatus == 2) { switch (menuPoint) { case 0: - this.parent.setOverlay(new GameScreen(this.parent, "levels/test.level")); + this.parent.setOverlay(new GameScreen(this.parent)); break; case 1: this.parent.setOverlay(new ShopScreen(this.parent));