diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..2dccf70
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,65 @@
+
+ 4.0.0
+ de.techfak
+ Part14
+ jar
+ 1.0
+ Part14
+
+
+ 17
+ 17
+ 21.0.2
+
+
+
+
+
+
+ org.openjfx
+ javafx-controls
+ ${javafx.version}
+
+
+ org.openjfx
+ javafx-fxml
+ ${javafx.version}
+
+
+ org.openjfx
+ javafx-base
+ ${javafx.version}
+
+
+ org.openjfx
+ javafx-graphics
+ ${javafx.version}
+
+
+ org.openjfx
+ javafx-media
+ ${javafx.version}
+
+
+
+
+
+ ${basedir}/src
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 17
+ 17
+
+
+
+
+
+
+
diff --git a/src/asteroids/AsteroidsApplication.java b/src/asteroids/AsteroidsApplication.java
new file mode 100644
index 0000000..a3a6950
--- /dev/null
+++ b/src/asteroids/AsteroidsApplication.java
@@ -0,0 +1,369 @@
+package asteroids;
+
+import javafx.animation.AnimationTimer;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.input.KeyCode;
+import javafx.scene.layout.Pane;
+import javafx.scene.shape.Polygon;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+import javafx.scene.shape.Shape;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class AsteroidsApplication extends Application {
+ public static final int WIDTH = 800;
+ public static final int HEIGHT = 600;
+ private GameController controller;
+
+ @Override
+ public void start(Stage stage) {
+ GameView view = new GameView();
+ GameModel model = new GameModel(); // Initialize GameModel with no controller
+ controller = new GameController(view, model); // Pass the model to the controller
+
+ model.setController(controller); // Set the controller in the model after initialization
+ controller.startGameLoop(); // Start the game loop after everything is initialized
+
+ stage.setTitle("Asteroids!");
+ stage.setScene(view.getScene());
+ stage.show();
+ }
+
+ public static void main(String[] args) {
+ launch(AsteroidsApplication.class);
+ }
+}
+
+class GameModel {
+ private final List asteroids = new ArrayList<>();
+ private final List projectiles = new ArrayList<>();
+ private final Ship ship;
+ private final AtomicInteger points = new AtomicInteger();
+ private boolean isGameOver = false; // Game Over state
+ private GameController controller; // Reference to the controller
+
+ public GameModel() {
+ ship = new Ship(AsteroidsApplication.WIDTH / 2, AsteroidsApplication.HEIGHT / 2);
+ for (int i = 0; i < 15; i++) {
+ asteroids.add(new Asteroid(new Random().nextInt(AsteroidsApplication.WIDTH / 3), new Random().nextInt(AsteroidsApplication.HEIGHT)));
+ }
+ }
+
+ // Setter for controller
+ public void setController(GameController controller) {
+ this.controller = controller;
+ }
+
+ public Ship getShip() { return ship; }
+ public List getAsteroids() { return asteroids; }
+ public List getProjectiles() { return projectiles; }
+ public AtomicInteger getPoints() { return points; }
+ public boolean isGameOver() { return isGameOver; }
+
+ public void update() {
+ if (isGameOver) return; // Stop updates when game over
+
+ ship.move();
+ asteroids.forEach(Asteroid::move);
+ projectiles.forEach(Projectile::move);
+
+ checkCollisions();
+ }
+
+ private void checkCollisions() {
+ List toRemoveProjectiles = new ArrayList<>();
+ List toRemoveAsteroids = new ArrayList<>();
+
+ long currentTime = System.currentTimeMillis();
+
+ for (Projectile projectile : projectiles) {
+ if (currentTime - projectile.getSpawnTime() > 200 && projectile.collide(ship)) {
+ gameOver();
+ return;
+ }
+
+ for (Asteroid asteroid : asteroids) {
+ if (projectile.collide(asteroid)) {
+ toRemoveProjectiles.add(projectile);
+ toRemoveAsteroids.add(asteroid);
+ points.addAndGet(1000);
+ }
+ }
+ }
+
+ for (Asteroid asteroid : asteroids) {
+ if (asteroid.collide(ship)) {
+ gameOver();
+ return;
+ }
+ }
+
+ // Update the view in the controller
+ toRemoveAsteroids.forEach(asteroid -> asteroids.remove(asteroid));
+ toRemoveProjectiles.forEach(projectile -> projectiles.remove(projectile));
+
+ if (!toRemoveAsteroids.isEmpty() || !toRemoveProjectiles.isEmpty()) {
+ controller.updateViewAfterCollisions(toRemoveAsteroids, toRemoveProjectiles);
+ }
+ }
+
+ private void gameOver() {
+ isGameOver = true; // End the game
+ }
+}
+
+
+class GameView {
+ private final Pane pane;
+ private final Text scoreText;
+ private final Scene scene;
+ private final Text gameOverText;
+
+ public GameView() {
+ pane = new Pane();
+ pane.setPrefSize(AsteroidsApplication.WIDTH, AsteroidsApplication.HEIGHT);
+
+ scoreText = new Text(10, 20, "Points: 0");
+ pane.getChildren().add(scoreText);
+
+ gameOverText = new Text(AsteroidsApplication.WIDTH / 2 - 50, AsteroidsApplication.HEIGHT / 2, "Game Over!");
+ gameOverText.setVisible(false);
+ pane.getChildren().add(gameOverText);
+
+ scene = new Scene(pane);
+ }
+
+ public Scene getScene() { return scene; }
+ public Pane getPane() { return pane; }
+ public Text getScoreText() { return scoreText; }
+
+ public void addGameObject(Character obj) {
+ pane.getChildren().add(obj.getCharacter());
+ }
+
+ public void removeGameObject(Character obj) {
+ pane.getChildren().remove(obj.getCharacter());
+ }
+
+ public void setGameOverVisible(boolean visible) {
+ gameOverText.setVisible(visible);
+ }
+}
+
+class GameController {
+ private final GameView view;
+ private final GameModel model;
+ private final Map pressedKeys = new HashMap<>();
+ private AnimationTimer gameLoop;
+ private long lastShotTime = 0;
+
+ public GameController(GameView view, GameModel model) {
+ this.view = view;
+ this.model = model;
+
+ view.addGameObject(model.getShip());
+ model.getAsteroids().forEach(view::addGameObject);
+
+ view.getScene().setOnKeyPressed(event -> pressedKeys.put(event.getCode(), true));
+ view.getScene().setOnKeyReleased(event -> pressedKeys.put(event.getCode(), false));
+ }
+
+ public void startGameLoop() {
+ gameLoop = new AnimationTimer() {
+ @Override
+ public void handle(long now) {
+ if (model.isGameOver()) {
+ gameLoop.stop();
+ view.setGameOverVisible(true);
+ return;
+ }
+
+ handleInputs();
+ spawnAsteroids();
+ model.update();
+ updateView();
+ }
+ };
+ gameLoop.start();
+ }
+
+ private void handleInputs() {
+ if (model.isGameOver()) return;
+
+ if (pressedKeys.getOrDefault(KeyCode.LEFT, false)) model.getShip().turnLeft();
+ if (pressedKeys.getOrDefault(KeyCode.RIGHT, false)) model.getShip().turnRight();
+ if (pressedKeys.getOrDefault(KeyCode.UP, false)) model.getShip().accelerate();
+
+ long currentTime = System.currentTimeMillis();
+ if (pressedKeys.getOrDefault(KeyCode.SPACE, false) && model.getProjectiles().size() < 3
+ && currentTime - lastShotTime > 200) {
+ lastShotTime = currentTime;
+ double shipAngle = Math.toRadians(model.getShip().getCharacter().getRotate());
+ double spawnX = model.getShip().getX() + Math.cos(shipAngle) * 15;
+ double spawnY = model.getShip().getY() + Math.sin(shipAngle) * 15;
+
+ Projectile projectile = new Projectile(spawnX, spawnY, System.currentTimeMillis());
+ projectile.getCharacter().setRotate(model.getShip().getCharacter().getRotate());
+ model.getProjectiles().add(projectile);
+ projectile.accelerate();
+
+ view.addGameObject(projectile);
+ }
+ }
+
+ private void spawnAsteroids() {
+ if (Math.random() < 0.005) {
+ Asteroid asteroid = new Asteroid(Math.random() * AsteroidsApplication.WIDTH, 0);
+ if (!asteroid.collide(model.getShip())) {
+ model.getAsteroids().add(asteroid);
+ view.addGameObject(asteroid);
+ }
+ }
+ }
+
+ private void updateView() {
+ view.getScoreText().setText("Points: " + model.getPoints().get());
+
+ List toRemove = new ArrayList<>();
+ for (Projectile projectile : model.getProjectiles()) {
+ if (projectile.getX() < 0 || projectile.getX() > AsteroidsApplication.WIDTH ||
+ projectile.getY() < 0 || projectile.getY() > AsteroidsApplication.HEIGHT) {
+ toRemove.add(projectile);
+ }
+ }
+
+ toRemove.forEach(projectile -> {
+ model.getProjectiles().remove(projectile);
+ view.removeGameObject(projectile);
+ });
+ }
+
+ public void updateViewAfterCollisions(List removedAsteroids, List removedProjectiles) {
+ // Handle view updates here
+ removedAsteroids.forEach(asteroid -> view.removeGameObject(asteroid));
+ removedProjectiles.forEach(projectile -> view.removeGameObject(projectile));
+ }
+}
+
+abstract class Character {
+ protected final Polygon character;
+ protected double x, y, velocityX, velocityY;
+
+ public Character(Polygon shape, double x, double y) {
+ this.character = shape;
+ this.x = x;
+ this.y = y;
+ this.velocityX = 0;
+ this.velocityY = 0;
+ this.character.setTranslateX(x);
+ this.character.setTranslateY(y);
+ }
+
+ public void move() {
+ x += velocityX;
+ y += velocityY;
+
+ // Wrap around screen horizontally
+ if (x < 0) x += AsteroidsApplication.WIDTH;
+ if (x > AsteroidsApplication.WIDTH) x -= AsteroidsApplication.WIDTH;
+
+ // Wrap around screen vertically
+ if (y < 0) y += AsteroidsApplication.HEIGHT;
+ if (y > AsteroidsApplication.HEIGHT) y -= AsteroidsApplication.HEIGHT;
+
+ character.setTranslateX(x);
+ character.setTranslateY(y);
+ }
+
+ public boolean collide(Character other) {
+ Shape collisionArea = Shape.intersect(this.character, other.getCharacter());
+ return !collisionArea.getBoundsInLocal().isEmpty();
+ }
+
+ public Polygon getCharacter() {
+ return character;
+ }
+
+ public double getX() {
+ return x;
+ }
+
+ public double getY() {
+ return y;
+ }
+ public void setX(double x) {
+ this.x = x;
+ character.setTranslateX(x);
+ }
+
+ public void setY(double y) {
+ this.y = y;
+ character.setTranslateY(y);
+ }
+}
+
+
+class Ship extends Character {
+ public Ship(double x, double y) {
+ super(new Polygon(-10, -10, 20, 0, -10, 10), x, y);
+ }
+
+ public void turnLeft() {
+ character.setRotate(character.getRotate() - 5);
+ }
+
+ public void turnRight() {
+ character.setRotate(character.getRotate() + 5);
+ }
+
+ public void accelerate() {
+ double angle = Math.toRadians(character.getRotate());
+ velocityX += Math.cos(angle) * 0.1;
+ velocityY += Math.sin(angle) * 0.1;
+ }
+}
+
+class Asteroid extends Character {
+ public Asteroid(double x, double y) {
+ super(new PolygonFactory().createPolygon(), x, y);
+ Random rnd = new Random();
+ velocityX = rnd.nextDouble() - 0.5;
+ velocityY = rnd.nextDouble() - 0.5;
+ }
+}
+
+
+class Projectile extends Character {
+ private final long spawnTime; // Track when the projectile was created
+
+ public Projectile(double x, double y, long spawnTime) {
+ super(new Polygon(2, -2, 2, 2, -2, 2, -2, -2), x, y);
+ this.spawnTime = spawnTime; // Store the spawn time
+ }
+
+ public void accelerate() {
+ double angle = Math.toRadians(character.getRotate());
+ velocityX = Math.cos(angle) * 3;
+ velocityY = Math.sin(angle) * 3;
+ }
+
+ public long getSpawnTime() {
+ return spawnTime;
+ }
+}
+
+class PolygonFactory {
+ public Polygon createPolygon() {
+ Random rnd = new Random();
+ double size = 10 + rnd.nextInt(10);
+ Polygon polygon = new Polygon();
+ for (int i = 0; i < 5; i++) {
+ double angle = 2 * Math.PI * i / 5;
+ polygon.getPoints().addAll(size * Math.cos(angle), size * Math.sin(angle));
+ }
+ return polygon;
+ }
+}
\ No newline at end of file