diff --git a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/game/managers/GameObjectManager.java b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/game/managers/GameObjectManager.java index 6b20ec5..d04ad84 100644 --- a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/game/managers/GameObjectManager.java +++ b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/game/managers/GameObjectManager.java @@ -23,6 +23,7 @@ class GameObjectManager { private final LivesLabel livesLabel; private final Background background; private final GameFrame frame; + private final GameOverlay gameOverlay; private final PlayerObject playerObject; @@ -46,6 +47,16 @@ class GameObjectManager { background = new Background(gameView); frame = new GameFrame(gameView); playerObject = new PlayerObject(gameView); + gameOverlay = new GameOverlay(gameView); + } + + /** + * Clear all game object lists. + */ + public void clearAll(){ + gameObjects.clear(); + bubbles.clear(); + harpoons.clear(); } /** @@ -62,6 +73,7 @@ class GameObjectManager { gameObjects.add(livesLabel); gameObjects.add(playerObject); gameObjects.addAll(bubbles); + gameObjects.add(gameOverlay); gameObjects.forEach(gameObject ->{ gameObject.update(); @@ -71,6 +83,7 @@ class GameObjectManager { /** * Get the player object. + * * @return the player */ public PlayerObject getPlayerObject(){ @@ -79,6 +92,7 @@ class GameObjectManager { /** * Get the bubbles. + * * @return the bubbles */ public LinkedList getBubbles() { @@ -87,14 +101,16 @@ class GameObjectManager { /** * Get the level label. + * * @return the level label */ public LevelLabel getLevelLabel(){ - return this.levelLabel; + return levelLabel; } /** * Get the lives label. + * * @return the lives label */ public LivesLabel getLivesLabel() { @@ -103,6 +119,7 @@ class GameObjectManager { /** * Get the score label. + * * @return the score label */ public ScoreLabel getScoreLabel() { @@ -111,6 +128,7 @@ class GameObjectManager { /** * Get the level progress bar. + * * @return the level progress bar */ public LevelProgressBar getLevelProgressBar(){ @@ -119,10 +137,19 @@ class GameObjectManager { /** * Get the harpoons. + * * @return the harpoons */ public LinkedList getHarpoons() { return harpoons; } + /** + * Get the game overlay. + * + * @return the game overlay + */ + public GameOverlay getGameOverlay(){ + return gameOverlay; + } } diff --git a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/game/managers/GamePlayManager.java b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/game/managers/GamePlayManager.java index b9d1c8c..a205b9d 100644 --- a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/game/managers/GamePlayManager.java +++ b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/game/managers/GamePlayManager.java @@ -11,9 +11,9 @@ import de.thdeg.greiner.superpangworld.graphics.moveable.HexagonalBubble; import de.thdeg.greiner.superpangworld.graphics.moveable.RoundBubble; import de.thdeg.greiner.superpangworld.graphics.moveable.SpecialBubble; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Random; +import java.awt.*; +import java.util.*; +import java.util.List; /** * The manager which handles the gameplay. @@ -26,11 +26,20 @@ public class GamePlayManager { private final GameObjectManager gameObjectManager; /** Generator for random values */ private final Random random; - /** Das erste Level */ + /** The current level */ private Level level; - /** Der Spieler */ - private final Player player; - + /** The player */ + private Player player; + /** The level manager */ + private LevelManager levelManager; + /** Flag, if bubbles should be spawned */ + private boolean spawnBubbles; + /** Flag, if all bubbles should be destroyed */ + private boolean destroyAllBubbles; + /** Flag, is a special bubble is currently present */ + public boolean specialBubblePresent; + /** Flag, if the bubble movement is froze */ + public boolean movementFroze; /** * Create the manager handling the gameplay. @@ -39,47 +48,130 @@ public class GamePlayManager { * @param gameObjectManager the manager handling the game objects */ public GamePlayManager(GameView gameView, GameObjectManager gameObjectManager) { + random = new Random(); this.gameView = gameView; this.gameObjectManager = gameObjectManager; - level = new Level(1,10000,10000); - player = new Player(this.level); - random = new Random(); - // Set HUD elements + boolean easyDifficulty = gameView.showSimpleStartScreen("Super Pang World","Start the game"); + + initializeGame(easyDifficulty); + initializeLevel(); + } + + /** + * Initialize the game. + * + * @param easyDifficulty the game difficulty + */ + private void initializeGame(boolean easyDifficulty){ + gameObjectManager.clearAll(); + levelManager = new LevelManager(easyDifficulty); + try { + level = levelManager.getNextLevel(); + } catch (LevelManager.NoMoreLevelsAvailableException e) { + System.out.println("No levels were generated, exiting program"); + System.exit(-1); + } + player = new Player(level); gameObjectManager.getPlayerObject().setGamePlayManager(this); + } + + /** + * Initialize the level. + */ + private void initializeLevel(){ + // Set Gameplay variables + spawnBubbles = true; + destroyAllBubbles = false; + specialBubblePresent = false; + movementFroze = false; + // Update HUD + updateHUD(); + } + + /** + * Loose a life and remove all bubbles + */ + public void looseLife(){ + player.lives--; + gameObjectManager.getLivesLabel().setLifeCount(player.lives); + gameObjectManager.getBubbles().clear(); + specialBubblePresent = false; + spawnBubbles = true; + gameObjectManager.getGameOverlay().flashScreen(Color.red,100); + } + + /** + * Switch to the next level or end the game if every level was played. + */ + private void nextLevel(){ + try { + level = levelManager.getNextLevel(); + initializeLevel(); + } catch (LevelManager.NoMoreLevelsAvailableException e) { + gameView.showEndScreen("You won!"); + boolean easyDifficulty = gameView.showSimpleStartScreen("Super Pang World","Start the game"); + initializeGame(easyDifficulty); + initializeLevel(); + } + } + + /** + * Update all HUD elements + */ + private void updateHUD(){ gameObjectManager.getLevelLabel().setLevel(level.number); gameObjectManager.getLivesLabel().setLifeCount(player.lives); gameObjectManager.getScoreLabel().setScore(player.score); + double progress = (player.score- level.neededOverallScore+ level.neededLevelScore * 1.0) / level.neededLevelScore; + gameObjectManager.getLevelProgressBar().setLevelProgress(Math.min((int)(progress*100),100)); } /** * Handle the gameplay. */ public void updateGamePlay(){ - if(gameView.timerExpired("SpawnBubble","GamePlayManager")){ - RoundBubble roundBubble = new RoundBubble(gameView, new ArrayList<>(Collections.singletonList(gameObjectManager.getPlayerObject()))); - roundBubble.setGamePlayManager(this); - gameObjectManager.getBubbles().add(roundBubble); - gameView.setTimer("SpawnBubble","GamePlayManager",30000000); + updateHUD(); + + if(player.score>=level.neededOverallScore){ + nextLevel(); + } + + if(gameView.timerExpired("SpawnBubble","GamePlayManager" ) && spawnBubbles && !movementFroze){ + addRandomBubble(); + gameView.setTimer("SpawnBubble","GamePlayManager",3000); + } + + if(movementFroze && gameView.timerExpired("MovementFroze","GamePlayManager")){ + movementFroze = false; + } + + if(destroyAllBubbles && gameView.timerExpired("DestroyAllBubbles","GamePlayManager")){ + List bubblesCopy = new ArrayList<>(gameObjectManager.getBubbles()); + for(Bubble bubble: bubblesCopy){ + destroy(bubble); + } + gameView.setTimer("DestroyAllBubbles","GamePlayManager",500); } } /** - * Shoot a harpoon starting at a given position. - * - * @param startPosition the start position - * @return if a harpoon was shot or not + * Start the timer to destroy all bubbles. */ - public boolean shootHarpoon(Position startPosition){ - if(gameView.timerExpired("ShootHarpoon","GamePlayManager") && gameObjectManager.getHarpoons().size()<2){ - ArrayList collidableGameObjects = new ArrayList<>(gameObjectManager.getBubbles()); - Harpoon harpoon = new Harpoon(gameView,collidableGameObjects); - harpoon.getPosition().setTo(startPosition.x+16, startPosition.y-15); - harpoon.setGamePlayManager(this); - gameObjectManager.getHarpoons().add(harpoon); - gameView.setTimer("ShootHarpoon","GamePlayManager",300); - return true; - } - return false; + public void destroyAllBubbles(){ + gameView.setTimer("DestroyAllBubbles","GamePlayManager",500); + spawnBubbles = false; + destroyAllBubbles = true; + movementFroze = false; + } + + /** + * Freeze the bubble movement. + * + * @param freezeTime the freeze duration + */ + public void freezeMovement(int freezeTime){ + movementFroze = true; + gameView.setTimer("MovementFroze","GamePlayManager",freezeTime); } /** @@ -97,40 +189,21 @@ public class GamePlayManager { * @param bubble the bubble to remove */ public void destroy(Bubble bubble){ - if(gameObjectManager.getBubbles().contains(bubble)) { - if (bubble.getSize() > 1.25) { - addRandomBubble(bubble.getSize() / 2, bubble.getSpeedInPixel(), bubble.getPosition(), false); - addRandomBubble(bubble.getSize() / 2, bubble.getSpeedInPixel(), bubble.getPosition(), false); - } - - gameObjectManager.getBubbles().remove(bubble); - player.score += bubble.getBurstScore(); - updateHud(); - if(player.score>=level.neededOverallScore){ - switchLevel(); - } - } + bubble.destroy(); + gameObjectManager.getBubbles().remove(bubble); + player.score += bubble.getBurstScore(); } /** - * Update all HUD elements + * Destroy a bubble colliding with a harpoon. + * + * @param harpoon the harpoon + * @param bubble the bubble */ - private void updateHud(){ - gameObjectManager.getScoreLabel().setScore(player.score); - double progress = (player.score- level.neededOverallScore+ level.neededLevelScore * 1.0) / level.neededLevelScore; - gameObjectManager.getLevelProgressBar().setLevelProgress(Math.min((int)(progress*100),100)); - gameObjectManager.getLevelLabel().setLevel(level.number); - } - - /** - * Switch the current level. - */ - private void switchLevel(){ - if(level.number==99){ - System.out.println("You won!"); - }else{ - level = new Level(level.number+1,10000,(level.number+1)*10000); - updateHud(); + public void destroy(Harpoon harpoon, Bubble bubble){ + if(gameObjectManager.getHarpoons().contains(harpoon) && gameObjectManager.getBubbles().contains(bubble)){ + destroy(harpoon); + destroy(bubble); } } @@ -139,21 +212,15 @@ public class GamePlayManager { * - 60% normal bubble * - 30% hexagonal bubble * - 10% special bubble - * - * TODO: Integrate all different bubbles, not only the normal red bubble - * - * @param size the size of the bubble - * @param speedInPixel the speed of the bubble - * @param position the position of the bubble - * @param spawning the flag if the bubble gets created in spawning mode */ - private void addRandomBubble(double size, double speedInPixel, Position position, boolean spawning){ - int randomNumber = random.nextInt(59); + private void addRandomBubble(){ + int randomNumber = specialBubblePresent ? random.nextInt(90) : random.nextInt(100); ArrayList collidableGameObjects = new ArrayList<>(); collidableGameObjects.add(gameObjectManager.getPlayerObject()); collidableGameObjects.addAll(gameObjectManager.getHarpoons()); + if(randomNumber<60){ - RoundBubble roundBubble = new RoundBubble(gameView, collidableGameObjects,size,speedInPixel,position,spawning); + RoundBubble roundBubble = new RoundBubble(gameView, collidableGameObjects); roundBubble.setGamePlayManager(this); gameObjectManager.getBubbles().add(roundBubble); }else if(randomNumber<90){ @@ -164,15 +231,42 @@ public class GamePlayManager { SpecialBubble specialBubble = new SpecialBubble(gameView, collidableGameObjects); specialBubble.setGamePlayManager(this); gameObjectManager.getBubbles().add(specialBubble); + specialBubblePresent = true; } } /** - * Loose a life and remove all bubbles + * Add bubbles to the game. + * + * @param bubbles the bubbles */ - public void looseLife(){ - player.lives--; - gameObjectManager.getLivesLabel().setLifeCount(player.lives); - gameObjectManager.getBubbles().clear(); + public void addBubbles(Bubble... bubbles){ + ArrayList collidableGameObjects = new ArrayList<>(); + collidableGameObjects.add(gameObjectManager.getPlayerObject()); + collidableGameObjects.addAll(gameObjectManager.getHarpoons()); + for(Bubble bubble: bubbles){ + bubble.setGamePlayManager(this); + bubble.addCollidableGameObject(collidableGameObjects); + gameObjectManager.getBubbles().add(bubble); + } + } + + /** + * Shoot a harpoon starting at a given position. + * + * @param startPosition the start position + * @return if a harpoon was shot or not + */ + public boolean shootHarpoon(Position startPosition){ + if(gameView.timerExpired("ShootHarpoon","GamePlayManager") && gameObjectManager.getHarpoons().size()<2){ + Harpoon harpoon = new Harpoon(gameView,new ArrayList<>()); + harpoon.getPosition().setTo(startPosition.x+16, startPosition.y-15); + harpoon.setGamePlayManager(this); + gameObjectManager.getHarpoons().add(harpoon); + gameObjectManager.getBubbles().forEach(b -> b.addCollidableGameObject(Collections.singletonList(harpoon))); + gameView.setTimer("ShootHarpoon","GamePlayManager",300); + return true; + } + return false; } } diff --git a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/game/managers/LevelManager.java b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/game/managers/LevelManager.java new file mode 100644 index 0000000..c791b79 --- /dev/null +++ b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/game/managers/LevelManager.java @@ -0,0 +1,58 @@ +package de.thdeg.greiner.superpangworld.game.managers; + +import de.thdeg.greiner.superpangworld.graphics.helper.Level; + +import java.util.ArrayDeque; +import java.util.Queue; + +/** + * The level manager for creating and providing levels. + */ +public class LevelManager { + + /** The queue to store the generated levels */ + private final Queue levels; + + /** + * Create a level manager. + * + * @param easyDifficulty the difficulty + */ + public LevelManager(boolean easyDifficulty){ + levels = new ArrayDeque<>(); + generateLevels(easyDifficulty); + } + + /** + * Get the next level. If there a no remaining levels an exception is thrown. + * + * @return the next level + * @throws NoMoreLevelsAvailableException if there is no level remaining + */ + public Level getNextLevel() throws NoMoreLevelsAvailableException { + if(levels.isEmpty()){ + throw new NoMoreLevelsAvailableException(); + }else{ + return levels.poll(); + } + } + + /** + * Generate the levels according to the difficulty. + * + * @param easyDifficulty the difficulty + */ + private void generateLevels(boolean easyDifficulty){ + int overallScoreNeeded = 0; + for(int i=0;i<3;i++){ + int scoreNeeded = easyDifficulty ? 1000 + (100*i) : 10000 + (1000*i); + overallScoreNeeded += scoreNeeded; + levels.add(new Level(i+1, scoreNeeded,overallScoreNeeded)); + } + } + + /** + * The exception in case no next level is remaining. + */ + public static class NoMoreLevelsAvailableException extends Exception{} +} diff --git a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/base/Bubble.java b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/base/Bubble.java index 0f95753..f774360 100644 --- a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/base/Bubble.java +++ b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/base/Bubble.java @@ -4,6 +4,7 @@ import de.thdeg.greiner.superpangworld.gameview.GameView; import de.thdeg.greiner.superpangworld.graphics.helper.HelperValues; import de.thdeg.greiner.superpangworld.graphics.moveable.Harpoon; import de.thdeg.greiner.superpangworld.graphics.moveable.PlayerObject; +import de.thdeg.greiner.superpangworld.graphics.moveable.SpecialBubble; import java.awt.geom.Point2D; import java.util.*; @@ -13,21 +14,20 @@ import java.util.*; */ public abstract class Bubble extends CollidingGameObject implements MovingGameObject{ - private static final Point2D VELOCITY_RIGHT = new Point2D.Double(3,1); - private static final Point2D VELOCITY_LEFT = new Point2D.Double(-3,1); - - /** Random generator */ - private final Random random; + /** The velocity of a normal right movement */ + public static final Point2D VELOCITY_RIGHT = new Point2D.Double(3,1); + /** The velocity of a normal left movement */ + public static final Point2D VELOCITY_LEFT = new Point2D.Double(-3,1); + /** The random generator */ + protected final Random random; /** Flag, if the bubble is in the spawning phase */ private boolean spawning; /** Spawning speed */ private int spawnSpeed; /** Unique id for the spawn event timer */ - private final long spawnID; - /** Temporary movement action */ - private Position nextPosition; - - private Point2D velocity; + protected final long spawnID; + /** The current bubble velocity */ + protected Point2D velocity; /** * Create a bubble with default values. @@ -50,7 +50,7 @@ public abstract class Bubble extends CollidingGameObject implements MovingGameOb spawnSpeed = 500; gameView.setTimer("spawn"+spawnID,"bubble"+spawnID,spawnSpeed); - velocity = new Point2D.Double(-3.5,1.4); + velocity = random.nextBoolean() ? VELOCITY_LEFT : VELOCITY_RIGHT; } @@ -63,25 +63,28 @@ public abstract class Bubble extends CollidingGameObject implements MovingGameOb * @param speedInPixel the speed in pixel per tick * @param position the position * @param spawning flag, if the bubble is in the spawning phase - * @param moveLeftToRight flag, if the bubble is moving left to right + * @param velocity the initial velocity of the bubble */ - public Bubble(GameView gameView, ArrayList objectsToCollideWith, double size, double speedInPixel, Position position, boolean spawning, boolean moveLeftToRight){ + public Bubble(GameView gameView, ArrayList objectsToCollideWith, double size, double speedInPixel, Position position, boolean spawning, Point2D velocity){ super(gameView,objectsToCollideWith); + random = new Random(); rotation = 90; this.size = size; - width = (int) (12 * size); - height = (int) (12 * size); this.speedInPixel = speedInPixel; this.position.setTo(position.x,position.y); - spawnID = UUID.randomUUID().getLeastSignificantBits(); - random = new Random(); this.spawning = spawning; + this.velocity = velocity; + spawnID = UUID.randomUUID().getLeastSignificantBits(); + + width = (int) (12 * size); + height = (int) (12 * size); + if(spawning){ spawnSpeed = 500; gameView.setTimer("spawn"+spawnID,"bubble"+spawnID,spawnSpeed); } - velocity = new Point2D.Double(3.5,1.4); + } /** @@ -107,16 +110,18 @@ public abstract class Bubble extends CollidingGameObject implements MovingGameOb // Wall bounce if((newLocation.getX() >= GameView.WIDTH - width - HelperValues.FRAME_BORDER_WIDTH) || newLocation.getX() <= HelperValues.FRAME_BORDER_WIDTH){ velocity = new Point2D.Double(velocity.getX() * -1.0, velocity.getY()); + if(this instanceof SpecialBubble){ + SpecialBubble specialBubble = (SpecialBubble) this; + specialBubble.freezeState = !specialBubble.freezeState; + } } - - // Bottom bounce + // Floor bounce if(newLocation.getY() >= HelperValues.FRAME_WINDOW_HEIGHT + HelperValues.FRAME_BORDER_WIDTH - width){ velocity = new Point2D.Double(velocity.getX(), velocity.getY() * -0.95); newLocation = new Point2D.Double(newLocation.getX(), HelperValues.FRAME_WINDOW_HEIGHT + HelperValues.FRAME_BORDER_WIDTH - width); } position.setTo(newLocation.getX(), newLocation.getY()); - } } @@ -126,7 +131,7 @@ public abstract class Bubble extends CollidingGameObject implements MovingGameOb * @return the score to add */ public int getBurstScore(){ - return (int)(size*100); + return (int)(size*1000); } /** @@ -138,31 +143,17 @@ public abstract class Bubble extends CollidingGameObject implements MovingGameOb return this.size; } - /** - * Get the speed in pixel per tick. - * - * @return the speed in pixel per tick - */ - public double getSpeedInPixel(){ - return speedInPixel; - } - - /** - * Get the flag if the bubble is in its spawning phase. - * - * @return true if in spawning phase, else false - */ - public boolean isSpawning(){ - return spawning; - } - @Override public void reactToCollision(CollidableGameObject otherObject) { - if(otherObject instanceof PlayerObject){ + if (otherObject instanceof PlayerObject && !gamePlayManager.movementFroze) { gamePlayManager.looseLife(); - }else if(otherObject instanceof Harpoon && !spawning){ - gamePlayManager.destroy((Harpoon) otherObject); - gamePlayManager.destroy(this); + } else if (otherObject instanceof Harpoon && !spawning) { + gamePlayManager.destroy((Harpoon) otherObject, this); } } + + /** + * Destroys the bubble. + */ + public abstract void destroy(); } diff --git a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/base/CollidingGameObject.java b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/base/CollidingGameObject.java index 1a89f11..cef8478 100644 --- a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/base/CollidingGameObject.java +++ b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/base/CollidingGameObject.java @@ -3,6 +3,7 @@ package de.thdeg.greiner.superpangworld.graphics.base; import de.thdeg.greiner.superpangworld.gameview.GameView; import java.util.ArrayList; +import java.util.List; /** * An object that can actively collide with other objects, e.g. a shot. @@ -44,4 +45,8 @@ public abstract class CollidingGameObject extends CollidableGameObject { protected final boolean collidesWith(CollidableGameObject other) { return this.hitBox.intersects(other.hitBox); } + + public void addCollidableGameObject(List objects){ + objectsToCollideWith.addAll(objects); + } } diff --git a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/base/GameObject.java b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/base/GameObject.java index abb1f5a..88514c1 100644 --- a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/base/GameObject.java +++ b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/base/GameObject.java @@ -27,7 +27,6 @@ public abstract class GameObject implements Cloneable{ /** The responsible GamePlayManager */ protected GamePlayManager gamePlayManager; - /** * Create a game object with default values. * @@ -52,8 +51,10 @@ public abstract class GameObject implements Cloneable{ * Update the status and the position is applicable. */ public void update(){ - if(this instanceof MovingGameObject){ - ((MovingGameObject)this).updatePosition(); + if(this instanceof MovingGameObject ){ + if(!(Bubble.class.isAssignableFrom(this.getClass()) && gamePlayManager.movementFroze)){ + ((MovingGameObject)this).updatePosition(); + } } updateStatus(); } diff --git a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/helper/HelperValues.java b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/helper/HelperValues.java index 31110b2..d25c2ab 100644 --- a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/helper/HelperValues.java +++ b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/helper/HelperValues.java @@ -2,11 +2,16 @@ package de.thdeg.greiner.superpangworld.graphics.helper; import java.awt.geom.Point2D; +/** + * Storage for helper values to be used throughout the program. + */ public class HelperValues { - public static final int FRAME_BORDER_WIDTH = 8; - public static final int FRAME_WINDOW_HEIGHT = 432; - + /** The width of the frame border */ + public static final double FRAME_BORDER_WIDTH = 8; + /** The height of the main game window */ + public static final double FRAME_WINDOW_HEIGHT = 432; + /** The games gravity */ public static Point2D BUBBLE_GRAVITY = new Point2D.Double(0,0.2); } diff --git a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/immovable/GameOverlay.java b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/immovable/GameOverlay.java new file mode 100644 index 0000000..48d84ae --- /dev/null +++ b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/immovable/GameOverlay.java @@ -0,0 +1,61 @@ +package de.thdeg.greiner.superpangworld.graphics.immovable; + +import de.thdeg.greiner.superpangworld.gameview.GameView; +import de.thdeg.greiner.superpangworld.graphics.base.GameObject; +import de.thdeg.greiner.superpangworld.graphics.helper.HelperValues; + +import java.awt.*; + +/** + * The game overlay for display text or color on the screen. + */ +public class GameOverlay extends GameObject { + + /** The message to display */ + private String message; + /** The color to display */ + private Color color; + + /** + * Create the game overlay. + * + * @param gameView the game view to display the elements on + */ + public GameOverlay(GameView gameView){ + super(gameView); + } + + /** + * Show a message on the screen. + * + * @param message the message to display + */ + public void showMessage(String message){ + gameView.setTimer("DisplayMessage","Overlay",3000); + this.message = message; + } + + /** + * Display a color of a set amount of time on the main game screen. + * + * @param color the color to display + * @param duration the duration to display + */ + public void flashScreen(Color color, int duration){ + gameView.setTimer("FlashScreen","Overlay",duration); + this.color = color; + } + + @Override + public void addToCanvas() { + if(!gameView.timerExpired("DisplayMessage","Overlay")){ + gameView.addTextToCanvas(message,GameView.WIDTH/2.0, GameView.HEIGHT/2.0,30, Color.BLACK,0); + } + if(!gameView.timerExpired("FlashScreen","Overlay")){ + gameView.addRectangleToCanvas(HelperValues.FRAME_BORDER_WIDTH,HelperValues.FRAME_BORDER_WIDTH,GameView.WIDTH - (2*HelperValues.FRAME_BORDER_WIDTH),HelperValues.FRAME_WINDOW_HEIGHT+1,1,true,color); + } + } + + @Override + protected void updateStatus() {} +} diff --git a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/Harpoon.java b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/Harpoon.java index a8e35fc..600f2cc 100644 --- a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/Harpoon.java +++ b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/Harpoon.java @@ -5,13 +5,13 @@ import de.thdeg.greiner.superpangworld.graphics.base.*; import java.awt.*; import java.util.ArrayList; -import java.util.UUID; /** * A harpoon which can be fired upwards by the player. */ public class Harpoon extends CollidingGameObject implements MovingGameObject { + /** The pixel string representing a harpoon */ private final static String HARPOON = " B \n"+ " BBB \n"+ @@ -27,7 +27,7 @@ public class Harpoon extends CollidingGameObject implements MovingGameObject { */ public Harpoon(GameView gameView, ArrayList objectsToCollideWith){ super(gameView, objectsToCollideWith); - speedInPixel = 1; + speedInPixel = 2; size = 3; width = (int) (7 * size); height = (int) (5 * size); @@ -46,13 +46,7 @@ public class Harpoon extends CollidingGameObject implements MovingGameObject { @Override public void reactToCollision(CollidableGameObject otherObject) { - if(Bubble.class.isAssignableFrom(otherObject.getClass())){ - Bubble bubble = (Bubble) otherObject; - if(!bubble.isSpawning()){ - gamePlayManager.destroy((Bubble)otherObject); - gamePlayManager.destroy(this); - } - } + } /** diff --git a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/HexagonalBubble.java b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/HexagonalBubble.java index 50257b1..b8975d7 100644 --- a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/HexagonalBubble.java +++ b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/HexagonalBubble.java @@ -3,7 +3,10 @@ package de.thdeg.greiner.superpangworld.graphics.moveable; import de.thdeg.greiner.superpangworld.gameview.GameView; import de.thdeg.greiner.superpangworld.graphics.base.Bubble; import de.thdeg.greiner.superpangworld.graphics.base.CollidableGameObject; +import de.thdeg.greiner.superpangworld.graphics.base.Position; +import java.awt.*; +import java.awt.geom.Point2D; import java.util.ArrayList; /** @@ -20,25 +23,36 @@ public class HexagonalBubble extends Bubble { public HexagonalBubble(GameView gameView, ArrayList objectsToCollideWith) { super(gameView,objectsToCollideWith); - size=0.6; + size=1; speedInPixel = 2; width = (int) (128 * size); height = (int) (128 * size); - this.hitBox.width = width-20; - this.hitBox.height = height-20; + this.hitBox.width = width; + this.hitBox.height = height; + } + + public HexagonalBubble(GameView gameView, ArrayList objectsToCollideWith, double size, double speedInPixel, Position position, boolean spawning, Point2D velocity){ + super(gameView, objectsToCollideWith, size, speedInPixel, position, spawning, velocity); + + width = (int) (128 * size); + height = (int) (128 * size); + + this.hitBox.width = width; + this.hitBox.height = height; } @Override protected void updateHitBoxPosition() { - hitBox.x = (int) (position.x+10); - hitBox.y = (int) (position.y+10); + hitBox.x = (int) position.x; + hitBox.y = (int) position.y; } @Override public void addToCanvas() { gameView.addImageToCanvas("hexagon.png",position.x,position.y,size,rotation); + gameView.addRectangleToCanvas(hitBox.x,hitBox.y,hitBox.width,hitBox.height,1,false, Color.GREEN); } @Override @@ -51,4 +65,20 @@ public class HexagonalBubble extends Bubble { super.updatePosition(); rotation = (rotation + 1) % 360; } + + @Override + public void destroy() { + if (size > 0.125) { + Position positionLeft = new Position(position.x - (width/2.0/2.0), position.y); + Position positionRight = new Position(position.x + width - (width/2.0/2.0), position.y); + + Point2D.Double velocityLeft = new Point2D.Double(Math.min(this.velocity.getX(),this.velocity.getX() * -1.0),Math.min(this.velocity.getY(),this.velocity.getY() * -1.0)); + Point2D.Double velocityRight = new Point2D.Double(Math.max(this.velocity.getX(),this.velocity.getX() * -1.0),Math.min(this.velocity.getY(),this.velocity.getY() * -1.0)); + + Bubble bubbleLeft = new HexagonalBubble(gameView, new ArrayList<>(), size/2, speedInPixel, positionLeft, false, velocityLeft); + Bubble bubbleRight = new HexagonalBubble(gameView, new ArrayList<>(), size/2, speedInPixel, positionRight, false, velocityRight); + + gamePlayManager.addBubbles(bubbleLeft, bubbleRight); + } + } } diff --git a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/PlayerObject.java b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/PlayerObject.java index e8e5a86..9d5573a 100644 --- a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/PlayerObject.java +++ b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/PlayerObject.java @@ -3,7 +3,6 @@ package de.thdeg.greiner.superpangworld.graphics.moveable; import de.thdeg.greiner.superpangworld.gameview.GameView; import de.thdeg.greiner.superpangworld.graphics.base.CollidableGameObject; -import java.awt.*; import java.util.Objects; /** @@ -13,10 +12,11 @@ public class PlayerObject extends CollidableGameObject implements Cloneable { /** Flag, if the player is shooting */ private boolean shooting; - + /** Flag, if the player is moving right */ private boolean movingRight; + /** Flag, if the player is moving left */ private boolean movingLeft; - + /** Flag, to rotate between standing and walking when moving around */ private boolean movingStanding; /** diff --git a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/RoundBubble.java b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/RoundBubble.java index c636cd1..98f1e85 100644 --- a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/RoundBubble.java +++ b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/RoundBubble.java @@ -5,6 +5,7 @@ import de.thdeg.greiner.superpangworld.graphics.base.Bubble; import de.thdeg.greiner.superpangworld.graphics.base.CollidableGameObject; import de.thdeg.greiner.superpangworld.graphics.base.Position; +import java.awt.geom.Point2D; import java.util.ArrayList; @@ -28,6 +29,26 @@ public class RoundBubble extends Bubble { " RRRWWR \n"+ " RRRR \n"; + /** The pixel art for the bubble */ + private final static String WHITE_BUBBLE = + " WWWW \n"+ + " WWWWWW \n"+ + " WWWWWWWW \n"+ + " WWWWWWWWWW \n"+ + "WWWWWWWWWWWW\n"+ + "WWWWWWWWWWWW\n"+ + "WWWWWWWWWWWW\n"+ + "WWWWWWWWWWWW\n"+ + " WWWWWWWWWW \n"+ + " WWWWWWWW \n"+ + " WWWWWW \n"+ + " WWWW \n"; + + /** Flag for switching between red and white if the ball should blink */ + private boolean redBubble; + /** Flag, if the bubble freezes the time when destroyed */ + protected boolean timeFreeze; + /** * Create a round bubble with default values. * @@ -38,6 +59,7 @@ public class RoundBubble extends Bubble { super(gameView,objectsToCollideWith); this.hitBox.width = width-20; this.hitBox.height = height-20; + redBubble = true; } /** @@ -49,11 +71,37 @@ public class RoundBubble extends Bubble { * @param speedInPixel the speed in Pixel per tick * @param position the Position * @param spawning flag, if the bubble is spawning or not + * @param velocity the initial velocity of the bubble */ - public RoundBubble(GameView gameView, ArrayList objectsToCollideWith, double size, double speedInPixel, Position position, boolean spawning){ - super(gameView, objectsToCollideWith, size, speedInPixel, position, spawning); + public RoundBubble(GameView gameView, ArrayList objectsToCollideWith, double size, double speedInPixel, Position position, boolean spawning, Point2D velocity){ + super(gameView, objectsToCollideWith, size, speedInPixel, position, spawning, velocity); this.hitBox.width = (int) (width*0.8); this.hitBox.height = (int) (height*0.8); + redBubble = true; + if(size < 2.5){ + if(random.nextInt(100)<10){ + timeFreeze = true; + } + } + } + + @Override + public void destroy() { + if (size > 1.25) { + Position positionLeft = new Position(position.x - (width/2.0/2.0), position.y); + Position positionRight = new Position(position.x + width - (width/2.0/2.0), position.y); + + Point2D.Double velocityLeft = new Point2D.Double(Math.min(this.velocity.getX(),this.velocity.getX() * -1.0),Math.min(this.velocity.getY(),this.velocity.getY() * -1.0)); + Point2D.Double velocityRight = new Point2D.Double(Math.max(this.velocity.getX(),this.velocity.getX() * -1.0),Math.min(this.velocity.getY(),this.velocity.getY() * -1.0)); + + Bubble bubbleLeft = new RoundBubble(gameView, new ArrayList<>(), size/2, speedInPixel, positionLeft, false, velocityLeft); + Bubble bubbleRight = new RoundBubble(gameView, new ArrayList<>(), size/2, speedInPixel, positionRight, false, velocityRight); + + gamePlayManager.addBubbles(bubbleLeft, bubbleRight); + }else if(timeFreeze){ + gamePlayManager.freezeMovement(1000); + System.out.println("FREEZING"); + } } @Override @@ -67,7 +115,19 @@ public class RoundBubble extends Bubble { */ @Override public void addToCanvas(){ - gameView.addBlockImageToCanvas(RED_BUBBLE, getPosition().x, getPosition().y,size, rotation); + if(gameView.timerExpired("BubbleBlink"+spawnID,"Bubble"+spawnID)){ + redBubble = !redBubble; + gameView.setTimer("BubbleBlink"+spawnID,"Bubble"+spawnID,100); + } + if(!timeFreeze){ + gameView.addBlockImageToCanvas(RED_BUBBLE, getPosition().x, getPosition().y,size, rotation); + }else{ + if(redBubble){ + gameView.addBlockImageToCanvas(RED_BUBBLE, getPosition().x, getPosition().y,size, rotation); + }else{ + gameView.addBlockImageToCanvas(WHITE_BUBBLE, getPosition().x, getPosition().y,size, rotation); + } + } } @Override diff --git a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/SpecialBubble.java b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/SpecialBubble.java index 1ac303d..8ea1453 100644 --- a/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/SpecialBubble.java +++ b/SuperPangWorld/src/de/thdeg/greiner/superpangworld/graphics/moveable/SpecialBubble.java @@ -3,6 +3,7 @@ package de.thdeg.greiner.superpangworld.graphics.moveable; import de.thdeg.greiner.superpangworld.gameview.GameView; import de.thdeg.greiner.superpangworld.graphics.base.Bubble; import de.thdeg.greiner.superpangworld.graphics.base.CollidableGameObject; +import de.thdeg.greiner.superpangworld.graphics.base.Position; import java.util.ArrayList; @@ -26,6 +27,51 @@ public class SpecialBubble extends Bubble { " GGGWWG \n"+ " GGGG \n"; + /** The pixel art for the bubble */ + private final static String STAR = + " OO \n"+ + " OYYO \n"+ + " OYYO \n"+ + " OOYYOO \n"+ + "OOOOOYYYYOOOOO\n"+ + "OYYYYYYYYYYYYO\n"+ + " OOYYYYYYYOO \n"+ + " OOYYYYYYOO \n"+ + " OYYYYYYO \n"+ + " OYYYYYYYYO \n"+ + " OYYYOOYYYO \n"+ + " OYYYO OYYYO \n"+ + " OYOO OOYO \n"+ + " OO OO \n"; + + /** The pixel art for the bubble */ + private final static String CLOCK = + " YY YY \n"+ + "YYYY OOO YYYY\n"+ + "YYYOOWWWOOYYY\n"+ + " YOBWWWWWWOY \n"+ + " OWBWWWBWO \n"+ + " OWWWBWBWWWO \n"+ + " OWWWWBWWWWO \n"+ + " OWWWWWWWWWO \n"+ + " OWWWWWWWO \n"+ + " OWWWWWWWO \n"+ + " OOWWWOO \n"+ + " OOOOO \n"+ + " O O \n"+ + " O O \n"; + + /** Flag, if the bubble is in freeze state or in burst state */ + public boolean freezeState; + /** The height of the star pixel art */ + private final static int STAR_HEIGHT = 14; + /** The width of the star pixel art */ + private final static int STAR_WIDTH = 14; + /** The height of the clock pixel art */ + private final static int CLOCK_HEIGHT = 14; + /** The width of the clock pixel art */ + private final static int CLOCK_WIDTH = 13; + /** * Create a special bubble with default values. * @@ -34,11 +80,21 @@ public class SpecialBubble extends Bubble { */ public SpecialBubble(GameView gameView, ArrayList objectsToCollideWith) { super(gameView,objectsToCollideWith); - + this.freezeState = true; this.hitBox.width = width-20; this.hitBox.height = height-20; } + @Override + public void destroy() { + gamePlayManager.specialBubblePresent = false; + if(freezeState){ + gamePlayManager.freezeMovement(3000); + }else{ + gamePlayManager.destroyAllBubbles(); + } + } + @Override protected void updateHitBoxPosition() { hitBox.x = (int) (position.x+10); @@ -48,6 +104,15 @@ public class SpecialBubble extends Bubble { @Override public void addToCanvas() { gameView.addBlockImageToCanvas(GREEN_BUBBLE,position.x,position.y,size, rotation); + + Position iconPosition = new Position(position.x + (width/2.0),position.y + (width/2.0)); + int IconBlockSize = 3; + if(freezeState){ + gameView.addBlockImageToCanvas(CLOCK,iconPosition.x - (CLOCK_WIDTH*IconBlockSize/2.0),iconPosition.y - (CLOCK_HEIGHT*IconBlockSize/2.0),IconBlockSize, 0); + }else{ + gameView.addBlockImageToCanvas(STAR,iconPosition.x - (STAR_WIDTH*IconBlockSize/2.0),iconPosition.y - (STAR_HEIGHT*IconBlockSize/2.0),IconBlockSize, 0); + } + } @Override diff --git a/out/artifacts/Programmieren_2_jar/Programmieren 2.jar b/out/artifacts/Programmieren_2_jar/Programmieren 2.jar index 23239a8..af34727 100644 Binary files a/out/artifacts/Programmieren_2_jar/Programmieren 2.jar and b/out/artifacts/Programmieren_2_jar/Programmieren 2.jar differ diff --git a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/game/managers/GameObjectManager.class b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/game/managers/GameObjectManager.class index d606496..1ef8a2f 100644 Binary files a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/game/managers/GameObjectManager.class and b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/game/managers/GameObjectManager.class differ diff --git a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/game/managers/GamePlayManager.class b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/game/managers/GamePlayManager.class index a846c8d..5c5b401 100644 Binary files a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/game/managers/GamePlayManager.class and b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/game/managers/GamePlayManager.class differ diff --git a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/game/managers/LevelManager$NoMoreLevelsAvailableException.class b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/game/managers/LevelManager$NoMoreLevelsAvailableException.class new file mode 100644 index 0000000..087f157 Binary files /dev/null and b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/game/managers/LevelManager$NoMoreLevelsAvailableException.class differ diff --git a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/game/managers/LevelManager.class b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/game/managers/LevelManager.class new file mode 100644 index 0000000..ecfc27c Binary files /dev/null and b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/game/managers/LevelManager.class differ diff --git a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/base/Bubble.class b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/base/Bubble.class index 61e6fc1..9169f7c 100644 Binary files a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/base/Bubble.class and b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/base/Bubble.class differ diff --git a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/base/CollidingGameObject.class b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/base/CollidingGameObject.class index ed78d6a..9f9131c 100644 Binary files a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/base/CollidingGameObject.class and b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/base/CollidingGameObject.class differ diff --git a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/base/GameObject.class b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/base/GameObject.class index 1548b34..103b97b 100644 Binary files a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/base/GameObject.class and b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/base/GameObject.class differ diff --git a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/helper/HelperValues.class b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/helper/HelperValues.class index cbbe268..299fc2d 100644 Binary files a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/helper/HelperValues.class and b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/helper/HelperValues.class differ diff --git a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/immovable/GameOverlay.class b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/immovable/GameOverlay.class new file mode 100644 index 0000000..03610ac Binary files /dev/null and b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/immovable/GameOverlay.class differ diff --git a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/Harpoon.class b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/Harpoon.class index ecee511..55140f3 100644 Binary files a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/Harpoon.class and b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/Harpoon.class differ diff --git a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/HexagonalBubble.class b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/HexagonalBubble.class index 21cbd21..009ee62 100644 Binary files a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/HexagonalBubble.class and b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/HexagonalBubble.class differ diff --git a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/PlayerObject.class b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/PlayerObject.class index 64833a1..bed9869 100644 Binary files a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/PlayerObject.class and b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/PlayerObject.class differ diff --git a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/RoundBubble.class b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/RoundBubble.class index 08ccdf4..68de193 100644 Binary files a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/RoundBubble.class and b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/RoundBubble.class differ diff --git a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/SpecialBubble.class b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/SpecialBubble.class index 4fed2ce..477249c 100644 Binary files a/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/SpecialBubble.class and b/out/production/SuperPangWorld/de/thdeg/greiner/superpangworld/graphics/moveable/SpecialBubble.class differ