diff --git a/OGP1718-Worms/.project b/OGP1718-Worms/.project
new file mode 100644
index 0000000..7c6ada6
--- /dev/null
+++ b/OGP1718-Worms/.project
@@ -0,0 +1,29 @@
+
+
+ 1718-Worms
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
+
+ resources/images
+ 2
+ PROJECT_LOC/images
+
+
+ resources/levels
+ 2
+ PROJECT_LOC/levels
+
+
+
diff --git a/OGP1718-Worms/images/burger.png b/OGP1718-Worms/images/burger.png
new file mode 100644
index 0000000..95854eb
Binary files /dev/null and b/OGP1718-Worms/images/burger.png differ
diff --git a/OGP1718-Worms/levels/Blocks.lvl b/OGP1718-Worms/levels/Blocks.lvl
new file mode 100644
index 0000000..7413ebf
--- /dev/null
+++ b/OGP1718-Worms/levels/Blocks.lvl
@@ -0,0 +1,3 @@
+# A map with blocks
+map:Blocks.png
+height:20
\ No newline at end of file
diff --git a/OGP1718-Worms/levels/Blocks.png b/OGP1718-Worms/levels/Blocks.png
new file mode 100644
index 0000000..7d603ad
Binary files /dev/null and b/OGP1718-Worms/levels/Blocks.png differ
diff --git a/OGP1718-Worms/levels/Simple.lvl b/OGP1718-Worms/levels/Simple.lvl
new file mode 100644
index 0000000..baeb7e0
--- /dev/null
+++ b/OGP1718-Worms/levels/Simple.lvl
@@ -0,0 +1,3 @@
+# A simple map
+map:Simple.png
+height:20
diff --git a/OGP1718-Worms/levels/Simple.png b/OGP1718-Worms/levels/Simple.png
new file mode 100644
index 0000000..ba94492
Binary files /dev/null and b/OGP1718-Worms/levels/Simple.png differ
diff --git a/OGP1718-Worms/levels/Skulls-lowres.lvl b/OGP1718-Worms/levels/Skulls-lowres.lvl
new file mode 100644
index 0000000..458e86c
--- /dev/null
+++ b/OGP1718-Worms/levels/Skulls-lowres.lvl
@@ -0,0 +1,4 @@
+# A smaller version of the skulls map,
+# which may improve performance
+map:Skulls-lowres.png
+height:20
diff --git a/OGP1718-Worms/levels/Skulls-lowres.png b/OGP1718-Worms/levels/Skulls-lowres.png
new file mode 100644
index 0000000..50c8916
Binary files /dev/null and b/OGP1718-Worms/levels/Skulls-lowres.png differ
diff --git a/OGP1718-Worms/levels/Skulls.lvl b/OGP1718-Worms/levels/Skulls.lvl
new file mode 100644
index 0000000..2c15e2a
--- /dev/null
+++ b/OGP1718-Worms/levels/Skulls.lvl
@@ -0,0 +1,3 @@
+# A large and complex map
+map:Skulls.png
+height:20
diff --git a/OGP1718-Worms/levels/Skulls.png b/OGP1718-Worms/levels/Skulls.png
new file mode 100644
index 0000000..3907c49
Binary files /dev/null and b/OGP1718-Worms/levels/Skulls.png differ
diff --git a/OGP1718-Worms/levels/levels.txt b/OGP1718-Worms/levels/levels.txt
new file mode 100644
index 0000000..3dfd07b
--- /dev/null
+++ b/OGP1718-Worms/levels/levels.txt
@@ -0,0 +1,4 @@
+Blocks.lvl
+Simple.lvl
+Skulls-lowres.lvl
+Skulls.lvl
\ No newline at end of file
diff --git a/OGP1718-Worms/src-provided/worms/facade/IFacade.java b/OGP1718-Worms/src-provided/worms/facade/IFacade.java
index 2a81d1a..abcce2c 100644
--- a/OGP1718-Worms/src-provided/worms/facade/IFacade.java
+++ b/OGP1718-Worms/src-provided/worms/facade/IFacade.java
@@ -1,7 +1,10 @@
package worms.facade;
-import worms.model.Worm;
+import java.math.BigInteger;
+import java.util.*;
+import worms.model.*;
import worms.util.ModelException;
+import worms.util.MustNotImplementException;
/**
* Implement this interface to connect your code to the graphical user interface
@@ -18,6 +21,12 @@ import worms.util.ModelException;
*
* a class Worm
in the package worms.model
for
* representing a worm
+ * a class World
in the package worms.model
for
+ * representing a world
+ * a class Food
in the package worms.model
for
+ * representing a food ration
+ * a class Team
in the package worms.model
for
+ * representing a team (only for teams of 2 students)
* a class Facade
in the package worms.facade
that
* implements this interface (IFacade
).
*
@@ -31,8 +40,8 @@ import worms.util.ModelException;
*
* Each method defined in the interface IFacade
must be
* implemented by the class Facade
. For example, the implementation
- * of getX
should call a method of the given worm
to
- * retrieve its x-coordinate.
+ * of getMass
should call a method of the given worm
to
+ * retrieve its mass.
*
* Your Facade
class should offer a default constructor.
*
@@ -63,7 +72,60 @@ import worms.util.ModelException;
*/
public interface IFacade {
+
+ /************
+ * WORLD
+ ************/
+
/**
+ * Create a new world with given width and given height.
+ * The passable map is a rectangular matrix indicating which parts of the terrain
+ * are passable and which parts are impassable.
+ * This matrix is derived from the transparency of the pixels in the image file
+ * of the terrain. passableMap[r][c] is true if the location at row r and column c
+ * is passable, and false if that location is impassable.
+ * The elements in the first row (row 0) represent the pixels at the top of the
+ * terrain (i.e., largest y-coordinates).
+ * The elements in the last row (row passableMap.length-1) represent pixels at the
+ * bottom of the terrain (smallest y-coordinates).
+ * The elements in the first column (column 0) represent the pixels at the left
+ * of the terrain (i.e., smallest x-coordinates).
+ * The elements in the last column (column passableMap[0].length-1) represent the
+ * pixels at the right of the terrain (i.e., largest x-coordinates).
+ */
+ public World createWorld(double width, double height,
+ boolean[][] passableMap) throws ModelException;
+
+ /**
+ * Terminate the given world.
+ */
+ void terminate(World world) throws ModelException;
+
+ /**
+ * Check whether the given worls is terminated.
+ */
+ boolean isTerminated(World world) throws ModelException;
+
+ /**
+ * Return the width of the given world.
+ */
+ public double getWorldWidth(World world) throws ModelException;
+
+ /**
+ * Return the height of the given world.
+ */
+ public double getWorldHeight(World world) throws ModelException;
+
+ /**
+ * Check whether the given world is passable at the given location.
+ * - The location is an array containing the x-coordinate of the location to
+ * check followed by the y-coordinate of that location.
+ * - Locations outside the boundaries of the world are always passable.
+ */
+ boolean isPassable(World world, double[] location) throws ModelException;
+
+ /**
+<<<<<<< HEAD
* Create and return a new worm that is positioned at the given location, looks
* in the given direction, has the given radius and the given name.
*
@@ -77,92 +139,401 @@ public interface IFacade {
* The radius of the new worm (in meter)
* @param name
* The name of the new worm
+=======
+ * Check whether the circular area with given center and given radius
+ * is passable in the given world.
+ * - The circular area must not lie completely within the given world.
+>>>>>>> f45fa3fa96c4ea42dd782f398cf71895f06caae9
*/
- Worm createWorm(double[] location, double direction, double radius, String name) throws ModelException;
+ boolean isPassable(World world, double[] center, double radius);
/**
- * Moves the given worm by the given number of steps.
+ * Check whether the circular area with given center and given radius
+ * is adjacent to impassable terrain in the given world.
+ * - The circular area must not lie completely within the given world.
*/
- void move(Worm worm, int nbSteps) throws ModelException;
+ boolean isAdjacent(World world, double[] center, double radius);
+
+ /**
+ * Check whether the given world contains the given worm.
+ */
+ boolean hasAsWorm(World world, Worm worm) throws ModelException;
/**
- * Turns the given worm by the given angle.
+ * Add the given worm to the given world.
*/
- void turn(Worm worm, double angle) throws ModelException;
+ void addWorm(World world, Worm worm) throws ModelException;
+
+ /**
+ * Remove the given worm from the given world.
+ */
+ void removeWorm(World world, Worm worm) throws ModelException;
/**
- * Makes the given worm jump.
+ * Return a list filled with all the worms in the given world.
*/
- void jump(Worm worm) throws ModelException;
+ List getAllWorms(World world) throws ModelException;
+
+ /**
+ * Check whether the given world contains the given food.
+ */
+ boolean hasAsFood(World world, Food food) throws ModelException;
/**
- * Returns the total amount of time (in seconds) that a jump of the given worm
- * would take.
+ * Add the given portion of food to the given world.
*/
- double getJumpTime(Worm worm) throws ModelException;
+ void addFood(World world, Food food) throws ModelException;
+
+ /**
+ * Remove the given portion of food from the given world.
+ */
+ void removeFood(World world, Food food) throws ModelException;
+
+ /**
+ * Return a collection filled with all the worms and all the
+ * portions of food in the given world.
+ */
+ Collection getAllItems(World world) throws ModelException;
/**
- * Returns the location on the jump trajectory of the given worm after a time t.
- *
- * @return An array with two elements, with the first element being the
- * x-coordinate and the second element the y-coordinate.
+ * Return a set of all the team in the given world.
*/
- double[] getJumpStep(Worm worm, double t) throws ModelException;
+ Set getAllTeams(World world) throws ModelException;
+
+ /**
+ * Check whether the given world has an active game.
+ */
+ boolean hasActiveGame(World world) throws ModelException;
/**
- * Returns the x-coordinate of the current location of the given worm.
+ * Return the active worm in the given world.
+ * - The active worm is the worm whose turn it is to perform
+ * player-controlled actions.
*/
- double getX(Worm worm) throws ModelException;
+ Worm getActiveWorm(World world) throws ModelException;
+
+ /**
+ * Start a new game in the given world.
+ */
+ void startGame(World world) throws ModelException;
+
+ /**
+ * Finish the current game, if any, in the given world.
+ */
+ void finishGame(World world) throws ModelException;
/**
- * Returns the y-coordinate of the current location of the given worm.
+ * Activate the next worm in the given world.
*/
- double getY(Worm worm) throws ModelException;
+ void activateNextWorm(World world) throws ModelException;
/**
- * Returns the current orientation of the given worm (in radians).
+ * Return the name of a single worm if that worm is the winner, or the name
+ * of a team if that team is the winner and the team still has several members.
+ */
+ String getWinner(World world);
+
+
+ /************
+ * WORM
+ ************/
+
+ /**
+ * Create and return a new worm that is positioned at the given location in
+ * the given world, that looks in the given direction, that has the given radius
+ * and the given name, and that is a member of the given team.
+ * - If the given world is not effective, the new worm is simply positioned
+ * at the given location.
+ * - If the given team is not effective, the new worm is not part of any team.
+ * The location is an array containing the x-coordinate of the location of
+ * the new worm followed by the y-coordinate of that location.
+ */
+ Worm createWorm(World world, double[] location, double direction, double radius,
+ String name, Team team) throws ModelException;
+
+ /**
+ * Terminate the given worm.
+ */
+ void terminate(Worm worm) throws ModelException;
+
+ /**
+ * Check whether the given worm is terminated.
+ */
+ boolean isTerminated(Worm worm) throws ModelException;
+
+ /**
+ * Return the current location of the given worm.
+ * - The resulting array contains the the x-coordinate of the given worm
+ * followed by its y-coordinate.
+ */
+ double[] getLocation(Worm worm) throws ModelException;
+
+ /**
+ * Return the current orientation of the given worm (in radians).
*/
double getOrientation(Worm worm) throws ModelException;
/**
- * Returns the radius of the given worm.
+ * Return the radius of the given worm.
*/
double getRadius(Worm worm) throws ModelException;
/**
- * Sets the radius of the given worm to the given value.
+ * Set the radius of the given worm to the given value.
*/
void setRadius(Worm worm, double newRadius) throws ModelException;
/**
- * Returns the current number of action points of the given worm.
+ * Return the mass of the given worm.
*/
- long getNbActionPoints(Worm worm) throws ModelException;
+ double getMass(Worm worm) throws ModelException;
/**
- * Decreases the current number of action points of the given worm with the
- * given delta.
- */
- void decreaseNbActionPoints(Worm worm, long delta) throws ModelException;
-
- /**
- * Returns the maximum number of action points of the given worm.
+ * Return the maximum number of action points of the given worm.
*/
long getMaxNbActionPoints(Worm worm) throws ModelException;
/**
- * Returns the name the given worm.
+ * Return the current number of action points of the given worm.
+ */
+ long getNbActionPoints(Worm worm) throws ModelException;
+
+ /**
+ * Decrease the current number of action points of the given worm
+ * with the given delta.
+ * - The given delta may be negative.
+ */
+ void decreaseNbActionPoints(Worm worm, long delta) throws ModelException;
+
+ /**
+ * Return the current number of hit points of the given worm.
+ */
+ BigInteger getNbHitPoints(Worm worm) throws ModelException;
+
+ /**
+ * Increment the current number of hit points of the given worm
+ * with the given delta.
+ * - The given delta may be negative.
+ */
+ void incrementNbHitPoints(Worm worm, long delta) throws ModelException;
+
+ /**
+ * Return the name the given worm.
*/
String getName(Worm worm) throws ModelException;
/**
- * Renames the given worm.
+ * Rename the given worm.
*/
void rename(Worm worm, String newName) throws ModelException;
+
+ /**
+ * Return the world to which this worm belongs
+ */
+ World getWorld(Worm worm) throws ModelException;
+
/**
- * Returns the mass of the given worm.
+ * Turn the given worm by the given angle.
*/
- double getMass(Worm worm) throws ModelException;
+ void turn(Worm worm, double angle);
+ /**
+ * Return the location the farthest away from its current location to which the given
+ * worm can move in the world in which that worm is positioned, if any, following
+ * the given direction and not exceeding the given maximum distance.
+ * - The maximum distance must be finite and may not be negative.
+ * - The given direction must be in the range [0.0 .. PI[.
+ * - On its road to the resulting location, the given worm will always be
+ * positioned on passable terrain.
+ * - The resulting position may be outside the boundaries of the world, if any, in
+ * which the given worm is located.
+ */
+ double[] getFurthestLocationInDirection(Worm worm, double direction, double maxDistance) throws ModelException;
+
+ /**
+ * Move the given worm according to the rules in the assignment.
+ */
+ void move(Worm worm) throws ModelException;
+
+ /**
+ * Returns whether the given worm can fall.
+ * - Students working alone on the project must not override this method.
+ */
+ default public boolean canFall(Worm worm) throws MustNotImplementException {
+ throw new MustNotImplementException();
+ }
+
+ /**
+ * Makes the given worm fall down until it rests on impassable terrain again,
+ * or until it leaves the world in which it is in.
+ * - Students working alone on the project must not override this method.
+ */
+ default void fall(Worm worm) throws ModelException, MustNotImplementException {
+ throw new MustNotImplementException();
+ }
+ /**
+ * Return the time needed by the given worm to jump to the nearest position
+ * adjacent to impassable terrain.
+ * - deltaT determines the resolution to be used in successive steps of the jump.
+ */
+ double getJumpTime(Worm worm, double deltaT) throws ModelException;
+
+ /**
+ * Returns the location on the jump trajectory of the given worm
+ * after a time t.
+ * - The resulting location is an array with two elements,
+ * with the first element being the x-coordinate and the
+ * second element the y-coordinate.
+ */
+ double[] getJumpStep(Worm worm, double t) throws ModelException;
+
+ /**
+ * Make the given worm jump using the given time step.
+ * - The given time step determines a time interval during which
+ * you may assume that the worm will not move through a piece
+ * of impassable terrain.
+ */
+ void jump(Worm worm, double timeStep) throws ModelException;
+
+
+
+ /************
+ * FOOD
+ ************/
+
+ /**
+ * Create and return a new portion of food that is positioned at the given
+ * location in the given world.
+ * = If the given world is not effective, the new food is simply positioned
+ * at the given location.
+ */
+ Food createFood(World world, double[] location) throws ModelException;
+
+ /**
+ * Terminate the given portion of food.
+ */
+ void terminate(Food food) throws ModelException;
+
+ /**
+ * Check whether the given portion of food is terminated.
+ */
+ boolean isTerminated(Food food) throws ModelException;
+
+ /**
+ * Return the current location of the given portion of food.
+ * - The resulting array contains the the x-coordinate of the given worm
+ * followed by its y-coordinate.
+ */
+ double[] getLocation(Food food) throws ModelException;
+
+ /**
+ * Return the radius of the given portion of food.
+ */
+ double getRadius(Food food) throws ModelException;
+
+ /**
+ * Return the mass of the given portion of food.
+ */
+ double getMass(Food food) throws ModelException;
+
+ /**
+ * Return the world to which this portion of food belongs.
+ */
+ World getWorld(Food food) throws ModelException;
+
+
+
+ /********
+ * TEAM
+ ********/
+
+ /**
+ * Create a new team for the given world with given name and with no members yet.
+ * - Students working alone on the project must not override this method.
+ */
+ default Team createTeam(World world, String name)
+ throws ModelException, MustNotImplementException {
+ throw new MustNotImplementException();
+ }
+
+ /**
+ * Terminate the given team.
+ */
+ default void terminate(Team team) throws ModelException, MustNotImplementException {
+ throw new MustNotImplementException();
+ }
+
+ /**
+ * Check whether the given portion of food is terminated.
+ */
+ default boolean isTerminated(Team team) throws ModelException, MustNotImplementException {
+ throw new MustNotImplementException();
+ }
+
+ /**
+ * Return the name of the given team.
+ * - Students working alone on the project must not override this method.
+ */
+ default String getName(Team team) throws ModelException, MustNotImplementException {
+ throw new MustNotImplementException();
+ }
+
+
+ /**
+ * Return the team to which this worm belongs.
+ * - Students working alone on the project must not override this method.
+ */
+ default Team getTeam(Worm worm) throws ModelException {
+ throw new MustNotImplementException();
+ }
+
+ /**
+ * Return the number of worms in the given team.
+ * - Students working alone on the project must not override this method.
+ */
+ default int getNbWormsOfTeam(Team team)
+ throws ModelException, MustNotImplementException {
+ throw new MustNotImplementException();
+ }
+
+ /**
+ * Return a list of all the worms in the given team, sorted alphabetically.
+ * This method must run in linear time.
+ * - Students working alone on the project must not override this method.
+ */
+ default List getAllWormsOfTeam(Team team)
+ throws ModelException, MustNotImplementException {
+ throw new MustNotImplementException();
+ };
+
+ /**
+ * Add the given worms to the given team.
+ * - Students working alone on the project must not override this method.
+ */
+ default void addWormsToTeam(Team team, Worm... worms)
+ throws ModelException, MustNotImplementException {
+ throw new MustNotImplementException();
+ }
+
+ /**
+ * Remove the given worms from the given team.
+ * - Students working alone on the project must not override this method.
+ */
+ default void removeWormsFromTeam(Team team, Worm... worms)
+ throws ModelException, MustNotImplementException {
+ throw new MustNotImplementException();
+ }
+
+ /**
+ * Merge the given teams.
+ * - All the worms of the supplying team are transferred to the receiving team.
+ * - Students working alone on the project must not override this method.
+ */
+ default void mergeTeams(Team recevingTeam, Team supplyingTeam)
+ throws ModelException, MustNotImplementException {
+ throw new MustNotImplementException();
+ }
+
+
}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/GUIUtils.java b/OGP1718-Worms/src-provided/worms/internal/gui/GUIUtils.java
index cdab28d..af7e59f 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/GUIUtils.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/GUIUtils.java
@@ -12,6 +12,11 @@ import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
+import java.util.Collections;
+import java.util.Random;
+
+import worms.facade.IFacade;
+import worms.model.World;
public class GUIUtils {
@@ -89,4 +94,47 @@ public class GUIUtils {
}
return url;
}
+
+ // ideally, this constant would only exist once in the code, but we cannot refer to the constant from worms.model
+ private static final double ADJACENCY_RADIUS_FRACTION = 0.1;
+
+ public static double[] findFreeAdjacentSpot(IFacade facade, World world, double radius, Random random) {
+ double worldWidth = facade.getWorldWidth(world);
+ double worldHeight = facade.getWorldHeight(world);
+
+ // start at random location
+ double x = random.nextDouble() * worldWidth;
+ double y = random.nextDouble() * worldHeight;
+ int n = 0;
+ // move towards center
+ double angle = Math.atan((worldHeight / 2 - y) / (worldWidth / 2 - x));
+ while (!isValidLocation(facade, world, x, y, radius)) {
+ // at some point, give up and start somewhere else
+ if (!liesInWorld(worldWidth, worldHeight, x, y) || n % 1000 == 0) {
+ x = random.nextDouble() * worldWidth;
+ y = random.nextDouble() * worldHeight;
+ angle = Math.atan((worldHeight / 2 - y) / (worldWidth / 2 - x));
+ n = 0;
+ }
+ double d = ADJACENCY_RADIUS_FRACTION / 2 * radius;
+ x += d * Math.cos(angle);
+ y += d * Math.sin(angle);
+ n += 1;
+ }
+ return new double[] { x, y };
+ }
+
+ private static boolean isValidLocation(IFacade facade, World world, double x, double y, double radius) {
+ double[] center = { x, y };
+ return facade.isPassable(world, center, radius) &&
+ facade.isAdjacent(world, center , radius);
+ }
+
+ private static boolean liesInWorld(double width, double height, double x, double y) {
+ return 0 <= x && x <= width && 0 <= y && y <= height;
+ }
+
+ public static String numberToName(int n) {
+ return String.join("", Collections.nCopies(n / 26, "Z")) + (char)('A' + (n % 26));
+ }
}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/GameState.java b/OGP1718-Worms/src-provided/worms/internal/gui/GameState.java
index c212cbb..8d28058 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/GameState.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/GameState.java
@@ -1,11 +1,6 @@
package worms.internal.gui;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
@@ -13,67 +8,47 @@ import java.util.concurrent.TimeUnit;
import worms.facade.IFacade;
import worms.internal.gui.game.commands.Command;
+import worms.model.World;
import worms.model.Worm;
public class GameState {
private final Random random;
private final IFacade facade;
- private final Collection worms = new ArrayList();
private final BlockingQueue timeDelta = new LinkedBlockingQueue(
1);
- private final int width;
- private final int height;
+ private World world;
- public GameState(IFacade facade, long randomSeed, int width, int height) {
+ private final Level level;
+
+ public GameState(IFacade facade, long randomSeed, Level level) {
this.random = new Random(randomSeed);
this.facade = facade;
- this.width = width;
- this.height = height;
+ this.level = level;
+ }
+
+ public synchronized void createWorld() {
+ level.load();
+ world = facade.createWorld(level.getWorldWidth(),
+ level.getWorldHeight(), level.getPassableMap());
}
- private List wormNames = Arrays.asList("Shari", "Shannon",
- "Willard", "Jodi", "Santos", "Ross", "Cora", "Jacob", "Homer",
- "Kara");
- private int nameIndex = 0;
- private Iterator selection;
- private Worm selectedWorm;
-
-
public IFacade getFacade() {
return facade;
}
- private void createRandomWorms() {
- double worldWidth = width * GUIConstants.DISPLAY_SCALE;
- double worldHeight = height * GUIConstants.DISPLAY_SCALE;
-
- for (int i = 0; i < wormNames.size(); i++) {
- String name = wormNames.get(nameIndex++);
- double radius = 0.25 + random.nextDouble() / 4;
-
- double x = -worldWidth / 2 + radius + random.nextDouble()
- * (worldWidth - 2 * radius);
- double y = -worldHeight / 2 + radius + random.nextDouble()
- * (worldHeight - 2 * radius);
- double direction = random.nextDouble() * 2 * Math.PI;
- Worm worm = facade.createWorm(new double[] {x, y}, direction, radius, name);
- if (worm != null) {
- worms.add(worm);
- } else {
- throw new NullPointerException("Created worm must not be null");
- }
- }
+ public Collection getWorms() {
+ return getFacade().getAllWorms(getWorld());
}
-
+
public void evolve(double dt) {
timeDelta.clear(); // nobody was waiting for the previous tick, so
// clear it
timeDelta.offer(dt);
}
-
+
public boolean executeImmediately(Command cmd) {
cmd.startExecution();
while (!cmd.isTerminated()) {
@@ -93,32 +68,12 @@ public class GameState {
return cmd.isExecutionCompleted();
}
- public void startGame() {
- createRandomWorms();
- selectNextWorm();
+ public Level getLevel() {
+ return level;
}
- public Worm getSelectedWorm() {
- return selectedWorm;
- }
-
- public void selectNextWorm() {
- if (selection == null || !selection.hasNext()) {
- selection = worms.iterator();
- }
- if (selection.hasNext()) {
- selectWorm(selection.next());
- } else {
- selectWorm(null);
- }
- }
-
- public void selectWorm(Worm worm) {
- selectedWorm = worm;
- }
-
- public Collection getWorms() {
- return Collections.unmodifiableCollection(worms);
+ public synchronized World getWorld() {
+ return world;
}
public Random getRandom() {
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/Level.java b/OGP1718-Worms/src-provided/worms/internal/gui/Level.java
new file mode 100644
index 0000000..ebbe5b8
--- /dev/null
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/Level.java
@@ -0,0 +1,228 @@
+package worms.internal.gui;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferByte;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+public class Level {
+
+ private static final String LEVELS_DIRECTORY = "levels";
+
+ private static class LoadException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public LoadException(String message, Exception cause) {
+ super(message, cause);
+ }
+
+ }
+
+ private static class LevelFile implements Comparable {
+ private final URL url;
+ private final String name;
+
+ public LevelFile(String name, URL url) {
+ this.name = name;
+ this.url = url;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public URL getURL() {
+ return url;
+ }
+
+ public InputStream getInputStream() {
+ try {
+ return getURL().openStream();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public int compareTo(LevelFile o) {
+ return getName().compareTo(o.getName());
+ }
+ }
+
+ public static Level[] getAvailableLevels() {
+ LevelFile[] files = getLevelFiles();
+ Level[] levels = new Level[files.length];
+ for (int i = 0; i < files.length; i++) {
+ levels[i] = new Level(files[i]);
+ }
+ return levels;
+ }
+
+ private static LevelFile[] getLevelFiles() {
+ InputStream levelsListFile;
+ try {
+ levelsListFile = GUIUtils.openResource(LEVELS_DIRECTORY
+ + "/levels.txt");
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ return new LevelFile[0];
+ }
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ levelsListFile));
+ List levelURLs = new ArrayList();
+
+ try {
+ String line = reader.readLine();
+ while (line != null) {
+ line = line.trim();
+ if (!line.isEmpty() && line.toLowerCase().endsWith(".lvl")) {
+ URL url = GUIUtils.toURL(LEVELS_DIRECTORY + "/" + line);
+ levelURLs.add(new LevelFile(line, url));
+ }
+ line = reader.readLine();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ return new LevelFile[0];
+ } finally {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ // don't care
+ }
+ }
+ LevelFile[] levelFiles = levelURLs.toArray(new LevelFile[levelURLs
+ .size()]);
+ Arrays.sort(levelFiles);
+ return levelFiles;
+ }
+
+ private final LevelFile file;
+ private BufferedImage mapImage;
+
+ private double scale;
+
+ public Level(LevelFile file) {
+ this.file = file;
+ }
+
+ public String getName() {
+ return file.getName().substring(0, file.getName().length() - 4);
+ }
+
+ public void load() {
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ file.getInputStream()));
+ readFile(reader);
+ reader.close();
+ } catch (Exception e) {
+ throw new LoadException("Could not load world from file "
+ + file.getName(), e);
+ }
+ }
+
+ protected void readFile(BufferedReader reader) throws IOException {
+ this.mapImage = ImageIO.read(GUIUtils.openResource(LEVELS_DIRECTORY
+ + "/" + readAsKeyVal(reader, "map")));
+ try {
+ double height = Double.parseDouble(readAsKeyVal(reader, "height"));
+ this.scale = height / mapImage.getHeight();
+ } catch (IllegalArgumentException e) {
+ double width = Double.parseDouble(readAsKeyVal(reader, "width"));
+ this.scale = width / mapImage.getWidth();
+ }
+ }
+
+ protected String readAsKeyVal(BufferedReader reader, String expectedKey)
+ throws IOException {
+ String line = reader.readLine();
+ while (line.isEmpty() || line.indexOf("#") == 0) {
+ line = reader.readLine();
+ }
+ if (line.indexOf("#") > 0) {
+ line = line.substring(0, line.indexOf("#")).trim();
+ }
+ int split = line.indexOf(":");
+ String key = line.substring(0, split);
+ String value = line.substring(split + 1);
+ if (!expectedKey.equals(key)) {
+ throw new IllegalArgumentException("Expected key " + expectedKey
+ + ", got " + key);
+ }
+ return value;
+ }
+
+ public BufferedImage getMapImage() {
+ return mapImage;
+ }
+
+ public int getMapHeight() {
+ return mapImage.getHeight();
+ }
+
+ public int getMapWidth() {
+ return mapImage.getWidth();
+ }
+
+ /**
+ * Scale of the world (in worm-meter per map pixel)
+ *
+ * @return
+ */
+ public double getScale() {
+ return scale;
+ }
+
+ public double getWorldWidth() {
+ return scale * mapImage.getWidth();
+ }
+
+ public double getWorldHeight() {
+ return scale * mapImage.getHeight();
+ }
+
+ public boolean[][] getPassableMap() {
+ final boolean[][] result = new boolean[getMapHeight()][getMapWidth()];
+ final byte[] bytes = ((DataBufferByte) mapImage.getRaster()
+ .getDataBuffer()).getData();
+ final int w = getMapWidth();
+ final int h = getMapHeight();
+ for (int row = 0; row < h; row++) {
+ final int offset = w * row;
+ for (int col = 0; col < w; col++) {
+ final byte alpha = bytes[4 * (offset + col)];
+ // alpha < 128 ((alpha & 0xf) == 0) => passable
+ // alpha >= 128 ((alpha & 0xf) != 0) => impassable
+ if (((int) alpha & 0xf0) == 0) {
+ result[row][col] = true;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * map width / map height
+ */
+ public double getMapAspectRatio() {
+ return (double) getMapWidth() / getMapHeight();
+ }
+
+ /**
+ * world width / world height
+ */
+ public double getWorldAspectRatio() {
+ return getWorldWidth() / getWorldHeight();
+ }
+}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/DefaultActionHandler.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/DefaultActionHandler.java
index c14aa86..59193ce 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/game/DefaultActionHandler.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/DefaultActionHandler.java
@@ -5,11 +5,14 @@ import java.util.concurrent.Executors;
import worms.facade.IFacade;
import worms.internal.gui.GameState;
+import worms.internal.gui.game.commands.AddNewFood;
+import worms.internal.gui.game.commands.AddNewTeam;
+import worms.internal.gui.game.commands.AddNewWorm;
import worms.internal.gui.game.commands.Command;
import worms.internal.gui.game.commands.Jump;
import worms.internal.gui.game.commands.Move;
import worms.internal.gui.game.commands.Rename;
-import worms.internal.gui.game.commands.Resize;
+import worms.internal.gui.game.commands.SelectNextWorm;
import worms.internal.gui.game.commands.StartGame;
import worms.internal.gui.game.commands.Turn;
import worms.internal.gui.messages.MessageType;
@@ -84,11 +87,24 @@ class DefaultActionHandler implements IActionHandler {
executeCommand(new Rename(getFacade(), worm, newName, getScreen()));
}
+ public void selectNextWorm() {
+ executeCommand(new SelectNextWorm(getFacade(), getScreen()));
+ }
+
public void startGame() {
executeCommand(new StartGame(getFacade(), getScreen()));
}
- public void resizeWorm(Worm worm, int sign) {
- executeCommand(new Resize(getFacade(), worm, 1 + sign * 0.2, getScreen()));
+ public void addNewWorm() {
+ executeCommand(new AddNewWorm(getFacade(), getScreen()));
}
+
+ public void addEmptyTeam(String name) {
+ executeCommand(new AddNewTeam(getFacade(), name, getScreen()));
+ }
+
+ public void addNewFood() {
+ executeCommand(new AddNewFood(getFacade(), getScreen()));
+ }
+
}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/PlayGameScreen.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/PlayGameScreen.java
index d7f60bd..74ca37a 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/game/PlayGameScreen.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/PlayGameScreen.java
@@ -1,23 +1,33 @@
package worms.internal.gui.game;
import java.awt.Graphics2D;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
import worms.facade.IFacade;
import worms.internal.gui.GUIConstants;
import worms.internal.gui.GUIUtils;
import worms.internal.gui.GameState;
import worms.internal.gui.InputMode;
+import worms.internal.gui.Level;
import worms.internal.gui.Screen;
import worms.internal.gui.WormsGUI;
import worms.internal.gui.game.modes.DefaultInputMode;
import worms.internal.gui.game.modes.EnteringNameMode;
+import worms.internal.gui.game.modes.GameOverMode;
+import worms.internal.gui.game.modes.SetupInputMode;
+import worms.internal.gui.game.sprites.FoodSprite;
import worms.internal.gui.game.sprites.WormSprite;
+import worms.internal.gui.messages.MessageType;
+import worms.model.Food;
+import worms.model.Team;
+import worms.model.World;
import worms.model.Worm;
public class PlayGameScreen extends Screen {
@@ -47,13 +57,12 @@ public class PlayGameScreen extends Screen {
@Override
protected InputMode createDefaultInputMode() {
- return new DefaultInputMode(this, null);
+ return new SetupInputMode(this, null);
}
@Override
public void screenStarted() {
runGameLoop();
- userActionHandler.startGame();
}
final AtomicLong lastUpdateTimestamp = new AtomicLong();
@@ -69,6 +78,7 @@ public class PlayGameScreen extends Screen {
repaint();
}
};
+ private Worm currentWorm;
private void runGameLoop() {
Timer timer = new Timer();
@@ -77,27 +87,44 @@ public class PlayGameScreen extends Screen {
public void uncaughtException(Thread t, Throwable e) {
gameLoop.cancel();
e.printStackTrace();
- getGUI().showError(
- e.getClass().getName() + ": " + e.getMessage());
+ getGUI().showError(e.getClass().getName() + ": " + e.getMessage());
}
});
lastUpdateTimestamp.set(System.currentTimeMillis());
timer.scheduleAtFixedRate(gameLoop, 0, 1000 / GUIConstants.FRAMERATE);
}
+ public void gameFinished() {
+ addMessage("Game over! The winner is " + getFacade().getWinner(getWorld())
+ + "\n\nPress 'R' to start another game, or 'ESC' to quit.", MessageType.NORMAL);
+ gameLoop.cancel();
+ switchInputMode(new GameOverMode(this, getCurrentInputMode()));
+ }
+
public synchronized void update() {
+ removeInactiveSprites();
addNewSprites();
for (Sprite> sprite : sprites) {
sprite.update();
}
+ currentWorm = getFacade().getActiveWorm(getWorld());
}
-
+
+ protected void removeInactiveSprites() {
+ for (Sprite> sprite : new ArrayList>(sprites)) {
+ if (!sprite.isObjectAlive()) {
+ removeSprite(sprite);
+ }
+ }
+ }
+
protected void addNewSprites() {
addNewWormSprites();
+ addNewFoodSprites();
}
private void addNewWormSprites() {
- Collection worms = getGameState().getWorms();
+ Collection worms = getFacade().getAllWorms(getWorld());
if (worms != null) {
for (Worm worm : worms) {
WormSprite sprite = getWormSprite(worm);
@@ -108,6 +135,28 @@ public class PlayGameScreen extends Screen {
}
}
+ private void addNewFoodSprites() {
+ Collection foods = getAll(Food.class);
+ if (foods != null) {
+ for (Food food : foods) {
+ FoodSprite sprite = getSpriteOfTypeFor(FoodSprite.class, food);
+ if (sprite == null) {
+ createFoodSprite(food);
+ }
+ }
+ }
+ }
+
+ private Collection getAll(Class type) {
+ return getFacade().getAllItems(getWorld()).stream().filter(type::isInstance).map(type::cast)
+ .collect(Collectors.toSet());
+ }
+
+ private void createFoodSprite(Food food) {
+ FoodSprite sprite = new FoodSprite(this, food);
+ addSprite(sprite);
+ }
+
private void createWormSprite(Worm worm) {
WormSprite sprite = new WormSprite(this, worm);
addSprite(sprite);
@@ -186,7 +235,7 @@ public class PlayGameScreen extends Screen {
}
public synchronized Worm getSelectedWorm() {
- return getGameState().getSelectedWorm();
+ return currentWorm;
}
@Override
@@ -194,8 +243,7 @@ public class PlayGameScreen extends Screen {
painter.paint(g);
}
- public static PlayGameScreen create(WormsGUI gui, GameState gameState,
- boolean debugMode) {
+ public static PlayGameScreen create(WormsGUI gui, GameState gameState, boolean debugMode) {
if (!debugMode) {
return new PlayGameScreen(gui, gameState);
} else {
@@ -208,6 +256,14 @@ public class PlayGameScreen extends Screen {
}
}
+ protected Level getLevel() {
+ return getGameState().getLevel();
+ }
+
+ public World getWorld() {
+ return getGameState().getWorld();
+ }
+
public void addSprite(Sprite> sprite) {
sprites.add(sprite);
}
@@ -216,11 +272,40 @@ public class PlayGameScreen extends Screen {
sprites.remove(sprite);
}
+ /**
+ * Aspect ratio of the screen
+ */
+ private double getScreenAspectRatio() {
+ return (double) getScreenWidth() / getScreenHeight();
+ }
+
+ /**
+ * Width of the world when displayed (in pixels)
+ */
+ private double getWorldDisplayWidth() {
+ if (getLevel().getMapAspectRatio() >= getScreenAspectRatio()) {
+ return getScreenWidth();
+ } else {
+ return getScreenHeight() * getLevel().getMapAspectRatio();
+ }
+ }
+
+ /**
+ * Height of the world when displayed (in pixels)
+ */
+ private double getWorldDisplayHeight() {
+ if (getLevel().getMapAspectRatio() <= getScreenAspectRatio()) {
+ return getScreenHeight();
+ } else {
+ return getScreenWidth() / getLevel().getMapAspectRatio();
+ }
+ }
+
/**
* Scale of the displayed world (in worm-meter per pixel)
*/
private double getDisplayScale() {
- return GUIConstants.DISPLAY_SCALE;
+ return getLevel().getWorldWidth() / getWorldDisplayWidth();
}
/**
@@ -241,36 +326,39 @@ public class PlayGameScreen extends Screen {
* World x coordinate to screen x coordinate
*/
public double getScreenX(double x) {
- return getScreenWidth()/2.0 + worldToScreenDistance(x);
+ double offset = (getScreenWidth() - getWorldDisplayWidth()) / 2.0;
+ return offset + worldToScreenDistance(x);
}
/**
* Screen x coordinate to world x coordinate
*/
public double getLogicalX(double screenX) {
- return screenToWorldDistance(screenX - getScreenWidth()/2.0);
+ double offset = (getScreenWidth() - getWorldDisplayWidth()) / 2.0;
+ return screenToWorldDistance(screenX - offset);
}
/**
* World y coordinate to screen y coordinate
*/
public double getScreenY(double y) {
- return getScreenHeight()/2.0 - worldToScreenDistance(y);
+ double offset = (getScreenHeight() - getWorldDisplayHeight()) / 2.0;
+ return offset + getWorldDisplayHeight() - worldToScreenDistance(y);
}
/**
* Screen y coordinate to world y coordinate
*/
public double getLogicalY(double screenY) {
- return screenToWorldDistance(getScreenHeight()/2.0 - screenY);
+ double offset = (getScreenHeight() - getWorldDisplayHeight()) / 2.0;
+ return screenToWorldDistance(-screenY + offset + getWorldDisplayHeight());
}
public void paintTextEntry(Graphics2D g, String message, String enteredName) {
painter.paintTextEntry(g, message, enteredName);
}
- public void drawTurnAngleIndicator(Graphics2D g, WormSprite wormSprite,
- double currentAngle) {
+ public void drawTurnAngleIndicator(Graphics2D g, WormSprite wormSprite, double currentAngle) {
painter.drawTurnAngleIndicator(g, wormSprite, currentAngle);
}
@@ -285,13 +373,41 @@ public class PlayGameScreen extends Screen {
return (InputMode) super.getCurrentInputMode();
}
+ public void addEmptyTeam() {
+ switchInputMode(
+ new EnteringNameMode("Enter team name: ", this, getCurrentInputMode(), newName -> userActionHandler.addEmptyTeam(newName)));
+ }
+
+ private Team lastTeam;
+
+ public void setLastCreatedTeam(Team team) {
+ this.lastTeam = team;
+ }
+
+ public Team getLastCreatedTeam() {
+ return lastTeam;
+ }
+
+ public void addPlayerControlledWorm() {
+ userActionHandler.addNewWorm();
+ }
+
+ public void addFood() {
+ userActionHandler.addNewFood();
+ }
+
+ public void startGame() {
+ lastTeam = null;
+ userActionHandler.startGame();
+ }
+
public void gameStarted() {
switchInputMode(new DefaultInputMode(this, getCurrentInputMode()));
}
public void renameWorm() {
- switchInputMode(new EnteringNameMode("Enter new name for worm: ", this,
- getCurrentInputMode(), new EnteringNameMode.Callback() {
+ switchInputMode(new EnteringNameMode("Enter new name for worm: ", this, getCurrentInputMode(),
+ new EnteringNameMode.Callback() {
@Override
public void onNameEntered(String newName) {
changeName(newName);
@@ -308,11 +424,7 @@ public class PlayGameScreen extends Screen {
}
public void selectNextWorm() {
- getGameState().selectNextWorm();
- }
-
- public IActionHandler getProgramActionHandler() {
- return programActionHandler;
+ userActionHandler.selectNextWorm();
}
public void selectWorm(Worm worm) {
@@ -321,12 +433,8 @@ public class PlayGameScreen extends Screen {
}
}
- public void resizeWorm(int sign) {
- Worm worm = getSelectedWorm();
-
- if (worm != null) {
- userActionHandler.resizeWorm(worm, sign);
- }
+ public IActionHandler getProgramActionHandler() {
+ return programActionHandler;
}
}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/PlayGameScreenDebugPainter.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/PlayGameScreenDebugPainter.java
index 19e64b5..3a55641 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/game/PlayGameScreenDebugPainter.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/PlayGameScreenDebugPainter.java
@@ -1,25 +1,126 @@
package worms.internal.gui.game;
import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.RenderingHints;
import java.awt.Shape;
+import java.awt.geom.Ellipse2D.Double;
+import java.awt.image.BufferedImage;
import worms.internal.gui.GUIUtils;
+import worms.internal.gui.Level;
+import worms.internal.gui.game.sprites.FoodSprite;
import worms.internal.gui.game.sprites.WormSprite;
+import worms.model.World;
public class PlayGameScreenDebugPainter extends PlayGameScreenPainter {
private static final int LOCATION_MARKER_SIZE = 4;
+ private static final double MAX_DIVERSION = 0.7875;
+
+ private static final boolean PAINT_PASSABLE = true;
+
+ /**
+ * Step size for sampling; lower = more details (but takes longer)
+ */
+ private static final double PASSABLE_STEP_SIZE = 3; // screen pixels
+
+ /**
+ * Radius for sampling; lower = more details (but takes longer)
+ */
+ private static final double PASSABLE_TEST_RADIUS = 10 ; // screen pixels
+
+
+ private Image passableImage;
+
public PlayGameScreenDebugPainter(PlayGameScreen screen) {
super(screen);
}
+ @Override
+ public void paint(Graphics2D g) {
+ super.paint(g);
+ }
+
+ @Override
+ protected void paintLevel() {
+ super.paintLevel();
+
+ if (passableImage == null) {
+ BufferedImage image = createPassableImage();
+ this.passableImage = image;
+ }
+
+ currentGraphics.drawImage(passableImage, 0, 0, null);
+
+ drawCrossMarker(getScreenX(0), getScreenY(0), 10, Color.BLUE);
+ drawCrossMarker(getScreenX(0), getScreenY(getLevel().getWorldHeight()),
+ 10, Color.BLUE);
+ drawCrossMarker(getScreenX(getLevel().getWorldWidth()), getScreenY(0),
+ 10, Color.BLUE);
+ drawCrossMarker(getScreenX(getLevel().getWorldWidth()),
+ getScreenY(getLevel().getWorldHeight()), 10, Color.BLUE);
+ }
+
+ protected BufferedImage createPassableImage() {
+ Level level = getState().getLevel();
+ World world = getState().getWorld();
+
+ BufferedImage image = new BufferedImage(getScreen().getScreenWidth(),
+ getScreen().getScreenHeight(), BufferedImage.TYPE_4BYTE_ABGR);
+ BufferedImage adjacencyImage = new BufferedImage(getScreen()
+ .getScreenWidth(), getScreen().getScreenHeight(),
+ BufferedImage.TYPE_4BYTE_ABGR);
+ Graphics2D imGfx = image.createGraphics();
+ imGfx.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ Graphics2D imAdjacencyGfx = adjacencyImage.createGraphics();
+ imAdjacencyGfx.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+
+ double testRadius = getScreen().screenToWorldDistance(PASSABLE_TEST_RADIUS);
+ double stepSize = getScreen().screenToWorldDistance(PASSABLE_STEP_SIZE);
+ for (double x = testRadius; x <= level.getWorldWidth() - testRadius; x += stepSize) {
+ for (double y = testRadius; y <= level.getWorldHeight()
+ - testRadius; y += stepSize) {
+ double randomizedX = x + (-0.5 + Math.random()) * stepSize * 2;
+ double randomizedY = y + (-0.5 + Math.random()) * stepSize * 2;
+ Graphics2D targetGraphics = imGfx;
+ boolean isPassable = false;
+ if (!getState().getFacade().isPassable(world, new double[] {randomizedX,
+ randomizedY}, testRadius)) {
+ targetGraphics.setColor(new Color(255, 0, 0, 4));
+ } else if (getState().getFacade().isAdjacent(world,
+ new double[] { randomizedX, randomizedY} , testRadius)) {
+ targetGraphics = imAdjacencyGfx;
+ targetGraphics.setColor(new Color(0, 255, 0, 64));
+ } else {
+ isPassable = true;
+ targetGraphics.setColor(new Color(0, 0, 255, 4));
+ }
+ if (!isPassable || PAINT_PASSABLE) {
+ Double circle = GUIUtils.circleAt(getScreenX(randomizedX),
+ getScreenY(randomizedY), getScreen()
+ .worldToScreenDistance(testRadius));
+ targetGraphics.fill(circle);
+ }
+ }
+ }
+ imGfx.drawImage(adjacencyImage, 0, 0, null);
+ imAdjacencyGfx.dispose();
+ imGfx.dispose();
+ return image;
+ }
+
@Override
protected void paintWorm(WormSprite sprite) {
drawName(sprite);
drawActionBar(sprite);
+ drawHitpointsBar(sprite);
drawOutline(sprite);
drawJumpMarkers(sprite); // also draw for other worms
@@ -29,11 +130,6 @@ public class PlayGameScreenDebugPainter extends PlayGameScreenPainter {
drawLocationMarker(sprite);
}
-
- @Override
- protected void paintLevel() {
- drawCrossMarker(getScreenX(0), getScreenY(0), 10, Color.BLUE);
- }
@Override
protected void drawJumpMarkers(WormSprite sprite) {
@@ -70,6 +166,21 @@ public class PlayGameScreenDebugPainter extends PlayGameScreenPainter {
Color.YELLOW);
}
+ @Override
+ protected void paintFood(FoodSprite sprite) {
+ super.paintFood(sprite);
+ double r = sprite.getRadius();
+ double x = sprite.getCenterX();
+ double y = sprite.getCenterY();
+
+ currentGraphics.setColor(Color.CYAN);
+ Shape circle = GUIUtils.circleAt(x, y, getScreen()
+ .worldToScreenDistance(r));
+ currentGraphics.fill(circle);
+ currentGraphics.setColor(Color.DARK_GRAY);
+ currentGraphics.draw(circle);
+ }
+
protected void drawOutline(WormSprite sprite) {
double r = sprite.getRadius();
double x = sprite.getCenterX();
@@ -92,6 +203,18 @@ public class PlayGameScreenDebugPainter extends PlayGameScreenPainter {
currentGraphics.drawLine((int) x, (int) y,
(int) (x + dist * Math.cos(direction)),
(int) (y - dist * Math.sin(direction)));
+
+ // draw move tolerance
+
+ direction = direction - MAX_DIVERSION;
+ currentGraphics.drawLine((int) x, (int) y,
+ (int) (x + dist * Math.cos(direction)),
+ (int) (y - dist * Math.sin(direction)));
+
+ direction = direction + 2 * MAX_DIVERSION;
+ currentGraphics.drawLine((int) x, (int) y,
+ (int) (x + dist * Math.cos(direction)),
+ (int) (y - dist * Math.sin(direction)));
}
}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/PlayGameScreenPainter.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/PlayGameScreenPainter.java
index 5d4578a..b25190b 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/game/PlayGameScreenPainter.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/PlayGameScreenPainter.java
@@ -3,6 +3,7 @@ package worms.internal.gui.game;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
+import java.awt.Image;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
@@ -14,17 +15,27 @@ import java.util.StringTokenizer;
import worms.internal.gui.AbstractPainter;
import worms.internal.gui.GUIUtils;
import worms.internal.gui.GameState;
+import worms.internal.gui.Level;
+import worms.internal.gui.game.sprites.FoodSprite;
import worms.internal.gui.game.sprites.WormSprite;
+import worms.model.World;
+import worms.util.ModelException;
public class PlayGameScreenPainter extends AbstractPainter {
- protected static final Color SELECTION_FILL_COLOR = new Color(0xaa84b6cc, true);
- protected static final Color SELECTION_IMPASSABLE_FILL_COLOR = new Color(0xaacc8484, true);
- protected static final Color SELECTION_OUTLINE_COLOR = new Color(0xaaffffff, true);
- protected static final Color DIRECTION_MARKER_COLOR = new Color(0xcc84b6cc, true);
- protected static final Color TURN_ANGLE_MARKER_COLOR = new Color(0xcccc84b6, true);
+ protected static final Color SELECTION_FILL_COLOR = new Color(0xaa84b6cc,
+ true);
+ protected static final Color SELECTION_IMPASSABLE_FILL_COLOR = new Color(
+ 0xaacc8484, true);
+ protected static final Color SELECTION_OUTLINE_COLOR = new Color(
+ 0xaaffffff, true);
+ protected static final Color DIRECTION_MARKER_COLOR = new Color(0xcc84b6cc,
+ true);
+ protected static final Color TURN_ANGLE_MARKER_COLOR = new Color(
+ 0xcccc84b6, true);
protected static final Color INVALID_TURN_ANGLE_MARKER_COLOR = Color.RED;
- protected static final Color ACTION_POINTS_COLOR = new Color(0xcc00cc00, true);
+ protected static final Color ACTION_POINTS_COLOR = new Color(0xcc00cc00,
+ true);
protected static final double ACTION_BAR_WIDTH = 30;
protected static final double ACTION_BAR_HEIGHT = 5;
@@ -32,15 +43,16 @@ public class PlayGameScreenPainter extends AbstractPainter {
protected static final Color HIT_POINTS_COLOR = new Color(0xccff6a00, true);
protected static final Color BAR_OUTLINE_COLOR = Color.WHITE;
- protected static final Color NAME_BAR_BACKGROUND = new Color(0x40ffffff, true);
- protected static final Color WEAPON_BAR_BACKGROUND = new Color(0x806666ff, true);
+ protected static final Color NAME_BAR_BACKGROUND = new Color(0x40ffffff,
+ true);
protected static final Color NAME_BAR_TEXT = Color.WHITE;
protected static final double TEXT_BAR_H_MARGIN = 4;
protected static final double TEXT_BAR_V_MARGIN = 3;
protected static final double TEXT_BAR_V_OFFSET = 2;
- protected static final Color RENAME_BACKGROUND_COLOR = new Color(0x600e53a7, true);
+ protected static final Color RENAME_BACKGROUND_COLOR = new Color(
+ 0x600e53a7, true);
protected static final Color RENAME_TEXT_COLOR = Color.WHITE;
protected static final Color JUMP_MARKER_COLOR = Color.GRAY;
@@ -48,20 +60,41 @@ public class PlayGameScreenPainter extends AbstractPainter {
protected static final double DIRECTION_INDICATOR_SIZE = 10;
protected Graphics2D currentGraphics;
+ private Image scaledImage;
public PlayGameScreenPainter(PlayGameScreen screen) {
super(screen);
}
+ private void createBackgroundImage() {
+ if (scaledImage == null) {
+ scaledImage = GUIUtils.scaleTo(getState().getLevel().getMapImage(),
+ getScreen().getScreenWidth(),
+ getScreen().getScreenHeight(), Image.SCALE_SMOOTH);
+ }
+ }
+
protected GameState getState() {
return getScreen().getGameState();
}
+ protected World getWorld() {
+ return getState().getWorld();
+ }
+
+ protected Level getLevel() {
+ return getState().getLevel();
+ }
+
public void paint(Graphics2D g) {
this.currentGraphics = g;
-
+
paintLevel();
+ for (FoodSprite sprite : getScreen().getSpritesOfType(FoodSprite.class)) {
+ paintFood(sprite);
+ }
+
for (WormSprite sprite : getScreen().getSpritesOfType(WormSprite.class)) {
if (sprite.getWorm() == getScreen().getSelectedWorm()) {
drawSelection(sprite);
@@ -72,8 +105,16 @@ public class PlayGameScreenPainter extends AbstractPainter {
this.currentGraphics = null;
}
+ protected void paintFood(FoodSprite sprite) {
+ sprite.draw(currentGraphics);
+ }
+
protected void paintLevel() {
-
+ createBackgroundImage();
+
+ int x = (int) getScreenX(0);
+ int y = (int) getScreenY(getLevel().getWorldHeight());
+ currentGraphics.drawImage(scaledImage, x, y, null);
}
protected double getScreenX(double x) {
@@ -91,6 +132,7 @@ public class PlayGameScreenPainter extends AbstractPainter {
drawName(sprite);
drawActionBar(sprite);
+ drawHitpointsBar(sprite);
if (getScreen().getSelectedWorm() == sprite.getWorm()) {
drawDirectionIndicator(sprite);
@@ -106,16 +148,29 @@ public class PlayGameScreenPainter extends AbstractPainter {
name = "(null)";
}
- Rectangle2D bounds = currentGraphics.getFontMetrics().getStringBounds(name, currentGraphics);
+ String teamName = null;
+ try {
+ teamName = sprite.getTeamName();
+ } catch (ModelException e) {
+ // no team
+ }
+
+ if (teamName != null) {
+ name += " (" + teamName + ")";
+ }
+
+ Rectangle2D bounds = currentGraphics.getFontMetrics().getStringBounds(
+ name, currentGraphics);
final double stringWidth = bounds.getWidth();
final double stringHeight = bounds.getHeight();
final double x = sprite.getCenterX() - stringWidth / 2;
final double y = sprite.getCenterY() - voffset - TEXT_BAR_V_OFFSET;
- RoundRectangle2D nameBarFill = new RoundRectangle2D.Double(x - TEXT_BAR_H_MARGIN,
- y - stringHeight - TEXT_BAR_V_MARGIN, stringWidth + 2 * TEXT_BAR_H_MARGIN,
- stringHeight + 2 * TEXT_BAR_V_MARGIN, 5, 5);
+ RoundRectangle2D nameBarFill = new RoundRectangle2D.Double(x
+ - TEXT_BAR_H_MARGIN, y - stringHeight - TEXT_BAR_V_MARGIN,
+ stringWidth + 2 * TEXT_BAR_H_MARGIN, stringHeight + 2
+ * TEXT_BAR_V_MARGIN, 5, 5);
currentGraphics.setColor(NAME_BAR_BACKGROUND);
currentGraphics.fill(nameBarFill);
@@ -132,23 +187,53 @@ public class PlayGameScreenPainter extends AbstractPainter {
double actionPoints = sprite.getActionPoints();
double maxActionPoints = sprite.getMaxActionPoints();
- RoundRectangle2D actionBarFill = new RoundRectangle2D.Double(x - ACTION_BAR_WIDTH / 2, y + spriteHeight / 2,
- actionPoints * ACTION_BAR_WIDTH / maxActionPoints, ACTION_BAR_HEIGHT, 5, 5);
+ RoundRectangle2D actionBarFill = new RoundRectangle2D.Double(x
+ - ACTION_BAR_WIDTH / 2, y + spriteHeight / 2, actionPoints
+ * ACTION_BAR_WIDTH / maxActionPoints, ACTION_BAR_HEIGHT, 5, 5);
currentGraphics.setColor(ACTION_POINTS_COLOR);
currentGraphics.fill(actionBarFill);
- RoundRectangle2D actionBar = new RoundRectangle2D.Double(x - ACTION_BAR_WIDTH / 2, y + spriteHeight / 2,
- ACTION_BAR_WIDTH, ACTION_BAR_HEIGHT, 5, 5);
+ RoundRectangle2D actionBar = new RoundRectangle2D.Double(x
+ - ACTION_BAR_WIDTH / 2, y + spriteHeight / 2, ACTION_BAR_WIDTH,
+ ACTION_BAR_HEIGHT, 5, 5);
currentGraphics.setColor(BAR_OUTLINE_COLOR);
currentGraphics.draw(actionBar);
}
+ protected void drawHitpointsBar(WormSprite sprite) {
+ double x = sprite.getCenterX();
+ double y = sprite.getCenterY();
+ double spriteHeight = sprite.getHeight(currentGraphics);
+
+ double hitPoints = sprite.getHitPoints().doubleValue();
+ double maxHitPoints = 2000;
+ hitPoints = Math.max(hitPoints, maxHitPoints);
+
+ RoundRectangle2D hitpointsBarFill = new RoundRectangle2D.Double(x
+ - ACTION_BAR_WIDTH / 2, y + spriteHeight / 2
+ + ACTION_BAR_HEIGHT, hitPoints * ACTION_BAR_WIDTH
+ / maxHitPoints, ACTION_BAR_HEIGHT, 5, 5);
+ currentGraphics.setColor(HIT_POINTS_COLOR);
+ currentGraphics.fill(hitpointsBarFill);
+
+ RoundRectangle2D hitpointsBar = new RoundRectangle2D.Double(x
+ - ACTION_BAR_WIDTH / 2, y + spriteHeight / 2
+ + ACTION_BAR_HEIGHT, ACTION_BAR_WIDTH, ACTION_BAR_HEIGHT, 5, 5);
+ currentGraphics.setColor(BAR_OUTLINE_COLOR);
+ currentGraphics.draw(hitpointsBar);
+ }
+
protected void drawSelection(WormSprite sprite) {
double x = sprite.getCenterX();
double y = sprite.getCenterY();
- double spriteHeight = Math.max(sprite.getWidth(currentGraphics), sprite.getHeight(currentGraphics));
+ double spriteHeight = Math.max(sprite.getWidth(currentGraphics),
+ sprite.getHeight(currentGraphics));
- currentGraphics.setColor(SELECTION_FILL_COLOR);
+ if (sprite.isAtImpassableTerrain()) {
+ currentGraphics.setColor(SELECTION_IMPASSABLE_FILL_COLOR);
+ } else {
+ currentGraphics.setColor(SELECTION_FILL_COLOR);
+ }
Shape circle = GUIUtils.circleAt(x, y, spriteHeight / 2);
currentGraphics.fill(circle);
@@ -157,41 +242,47 @@ public class PlayGameScreenPainter extends AbstractPainter {
protected void drawDirectionIndicator(WormSprite sprite) {
double x = sprite.getCenterX();
double y = sprite.getCenterY();
- double distance = Math.max(sprite.getWidth(currentGraphics), sprite.getHeight(currentGraphics)) / 2;
+ double distance = Math.max(sprite.getWidth(currentGraphics),
+ sprite.getHeight(currentGraphics)) / 2;
distance += DIRECTION_INDICATOR_SIZE / 2;
double direction = GUIUtils.restrictDirection(sprite.getOrientation());
currentGraphics.setColor(DIRECTION_MARKER_COLOR);
- Shape directionIndicator = new Ellipse2D.Double(
- x + distance * Math.cos(direction) - DIRECTION_INDICATOR_SIZE / 2,
- y - distance * Math.sin(direction) - DIRECTION_INDICATOR_SIZE / 2, DIRECTION_INDICATOR_SIZE,
- DIRECTION_INDICATOR_SIZE);
+ Shape directionIndicator = new Ellipse2D.Double(x + distance
+ * Math.cos(direction) - DIRECTION_INDICATOR_SIZE / 2,
+ y - distance * Math.sin(direction) - DIRECTION_INDICATOR_SIZE
+ / 2, DIRECTION_INDICATOR_SIZE, DIRECTION_INDICATOR_SIZE);
currentGraphics.fill(directionIndicator);
}
- void drawTurnAngleIndicator(Graphics2D graphics, WormSprite sprite, double angle) {
+ void drawTurnAngleIndicator(Graphics2D graphics, WormSprite sprite,
+ double angle) {
if (sprite == null) {
return;
}
double x = sprite.getCenterX();
double y = sprite.getCenterY();
- double distance = Math.max(sprite.getWidth(graphics), sprite.getHeight(graphics)) / 2;
+ double distance = Math.max(sprite.getWidth(graphics),
+ sprite.getHeight(graphics)) / 2;
distance += DIRECTION_INDICATOR_SIZE / 2;
- double direction = GUIUtils.restrictDirection(sprite.getOrientation() + angle);
+ double direction = GUIUtils.restrictDirection(sprite.getOrientation()
+ + angle);
/*
- * can't do this when getting information from sprite if
- * (getFacade().canTurn(sprite.getWorm(), angle)) {
- * graphics.setColor(TURN_ANGLE_MARKER_COLOR); } else {
- * graphics.setColor(INVALID_TURN_ANGLE_MARKER_COLOR); }
- */
+ can't do this when getting information from sprite
+ if (getFacade().canTurn(sprite.getWorm(), angle)) {
+ graphics.setColor(TURN_ANGLE_MARKER_COLOR);
+ } else {
+ graphics.setColor(INVALID_TURN_ANGLE_MARKER_COLOR);
+ }
+ */
graphics.setColor(TURN_ANGLE_MARKER_COLOR);
- Shape directionIndicator = new Ellipse2D.Double(
- x + distance * Math.cos(direction) - DIRECTION_INDICATOR_SIZE / 2,
- y - distance * Math.sin(direction) - DIRECTION_INDICATOR_SIZE / 2, DIRECTION_INDICATOR_SIZE,
- DIRECTION_INDICATOR_SIZE);
+ Shape directionIndicator = new Ellipse2D.Double(x + distance
+ * Math.cos(direction) - DIRECTION_INDICATOR_SIZE / 2,
+ y - distance * Math.sin(direction) - DIRECTION_INDICATOR_SIZE
+ / 2, DIRECTION_INDICATOR_SIZE, DIRECTION_INDICATOR_SIZE);
graphics.fill(directionIndicator);
}
@@ -202,7 +293,8 @@ public class PlayGameScreenPainter extends AbstractPainter {
if (xy != null) {
double jumpX = getScreenX(xy[0]);
double jumpY = getScreenY(xy[1]);
- drawCrossMarker(jumpX, jumpY, JUMP_MARKER_SIZE, JUMP_MARKER_COLOR);
+ drawCrossMarker(jumpX, jumpY, JUMP_MARKER_SIZE,
+ JUMP_MARKER_COLOR);
}
}
}
@@ -210,8 +302,10 @@ public class PlayGameScreenPainter extends AbstractPainter {
protected void drawCrossMarker(double x, double y, int size, Color color) {
currentGraphics.setColor(color);
- currentGraphics.drawLine((int) (x - size), (int) y, (int) (x + size), (int) y);
- currentGraphics.drawLine((int) x, (int) (y - size), (int) x, (int) (y + size));
+ currentGraphics.drawLine((int) (x - size), (int) y, (int) (x + size),
+ (int) y);
+ currentGraphics.drawLine((int) x, (int) (y - size), (int) x,
+ (int) (y + size));
}
void paintTextEntry(Graphics2D g, String message, String enteredText) {
@@ -219,7 +313,8 @@ public class PlayGameScreenPainter extends AbstractPainter {
g.fillRect(0, 0, getScreen().getScreenWidth(), 120);
g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 20));
g.setColor(RENAME_TEXT_COLOR);
- GUIUtils.drawCenteredString(g, message + enteredText + "\u2502", getScreen().getScreenWidth(), 100);
+ GUIUtils.drawCenteredString(g, message + enteredText + "\u2502",
+ getScreen().getScreenWidth(), 100);
}
public void paintInstructions(Graphics2D g, String message) {
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/Sprite.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/Sprite.java
index d4ce2c3..5861b1a 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/game/Sprite.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/Sprite.java
@@ -20,6 +20,8 @@ public abstract class Sprite {
public abstract T getObject();
+ public abstract boolean isObjectAlive();
+
protected IFacade getFacade() {
return getScreen().getFacade();
}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/AddNewFood.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/AddNewFood.java
new file mode 100644
index 0000000..cc29b6e
--- /dev/null
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/AddNewFood.java
@@ -0,0 +1,38 @@
+package worms.internal.gui.game.commands;
+
+import java.util.Random;
+
+import worms.facade.IFacade;
+import worms.internal.gui.GUIUtils;
+import worms.internal.gui.game.PlayGameScreen;
+import worms.internal.gui.messages.MessageType;
+import worms.util.ModelException;
+
+public class AddNewFood extends InstantaneousCommand {
+
+ private static final double FOOD_RADIUS = 0.20;
+
+ public AddNewFood(IFacade facade, PlayGameScreen screen) {
+ super(facade, screen);
+ }
+
+ @Override
+ protected boolean canStart() {
+ return true;
+ }
+
+ @Override
+ protected void doStartExecution() {
+ try {
+ Random random = getScreen().getGameState().getRandom();
+
+ double[] p = GUIUtils.findFreeAdjacentSpot(getFacade(), getWorld(), FOOD_RADIUS, random);
+
+ getFacade().createFood(getWorld(), p);
+ } catch (ModelException e) {
+ e.printStackTrace();
+ getScreen().addMessage("Error while adding new food", MessageType.ERROR);
+ }
+ }
+
+}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/AddNewTeam.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/AddNewTeam.java
new file mode 100644
index 0000000..2210c2c
--- /dev/null
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/AddNewTeam.java
@@ -0,0 +1,41 @@
+package worms.internal.gui.game.commands;
+
+import worms.facade.IFacade;
+import worms.internal.gui.game.PlayGameScreen;
+import worms.internal.gui.messages.MessageType;
+import worms.model.Team;
+import worms.util.ModelException;
+
+public class AddNewTeam extends InstantaneousCommand {
+
+ private final String name;
+
+ public AddNewTeam(IFacade facade, String name, PlayGameScreen screen) {
+ super(facade, screen);
+ this.name = name;
+ }
+
+ @Override
+ protected boolean canStart() {
+ return true;
+ }
+
+ @Override
+ protected void doStartExecution() {
+ try {
+ Team team = getFacade().createTeam(getWorld(), name);
+ getScreen().addMessage("Team " + name + " created.", MessageType.NORMAL);
+ getScreen().setLastCreatedTeam(team);
+ } catch (ModelException e) {
+ e.printStackTrace();
+ getScreen().addMessage(
+ "Could not create team " + name + ": " + e.getMessage(),
+ MessageType.ERROR);
+ }
+ }
+
+ @Override
+ protected void afterExecutionCompleted() {
+ }
+
+}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/AddNewWorm.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/AddNewWorm.java
new file mode 100644
index 0000000..021a848
--- /dev/null
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/AddNewWorm.java
@@ -0,0 +1,45 @@
+package worms.internal.gui.game.commands;
+
+import java.util.Random;
+
+import worms.facade.IFacade;
+import worms.internal.gui.GUIUtils;
+import worms.internal.gui.game.PlayGameScreen;
+import worms.internal.gui.messages.MessageType;
+import worms.model.Team;
+import worms.util.ModelException;
+
+public class AddNewWorm extends InstantaneousCommand {
+
+ private static final double MIN_RADIUS = 0.25;
+ private static final double MAX_RADIUS = Math.pow(2, 1.0/3.0) * MIN_RADIUS;
+
+ public AddNewWorm(IFacade facade, PlayGameScreen screen) {
+ super(facade, screen);
+ }
+
+ @Override
+ protected boolean canStart() {
+ return true;
+ }
+
+ @Override
+ protected void doStartExecution() {
+ try {
+ Random random = getScreen().getGameState().getRandom();
+ int nbWorms = getFacade().getAllWorms(getWorld()).size();
+ String name = "Worm " + GUIUtils.numberToName(nbWorms++);
+ // ensures minimum radius and team size conditions are always fulfilled
+ double radius = MIN_RADIUS + (MAX_RADIUS - MIN_RADIUS) * random.nextDouble();
+
+ double[] p = GUIUtils.findFreeAdjacentSpot(getFacade(), getWorld(), radius, random);
+
+ double direction = random.nextDouble() * 2 * Math.PI;
+ Team team = getScreen().getLastCreatedTeam();
+ getFacade().createWorm(getWorld(), p, direction, radius, name, team);
+ } catch (ModelException e) {
+ e.printStackTrace();
+ getScreen().addMessage("Could not create worm", MessageType.ERROR);
+ }
+ }
+}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Command.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Command.java
index 4fdb594..d404f69 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Command.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Command.java
@@ -2,6 +2,7 @@ package worms.internal.gui.game.commands;
import worms.facade.IFacade;
import worms.internal.gui.game.PlayGameScreen;
+import worms.model.World;
public abstract class Command {
@@ -26,9 +27,13 @@ public abstract class Command {
return facade;
}
+ protected World getWorld() {
+ return getScreen().getWorld();
+ }
+
public final void startExecution() {
if (canStart()) {
- started = true;
+ started = true;
doStartExecution();
afterExecutionStarted();
} else {
@@ -37,8 +42,12 @@ public abstract class Command {
}
protected final void cancelExecution() {
+ cancelExecution(null);
+ }
+
+ protected final void cancelExecution(Throwable e) {
cancelled = true;
- afterExecutionCancelled();
+ afterExecutionCancelled(e);
}
protected final void completeExecution() {
@@ -52,6 +61,9 @@ public abstract class Command {
doUpdate(dt);
if (isTerminated()) {
getScreen().update();
+ if (!getFacade().hasActiveGame(getWorld())) {
+ getScreen().gameFinished();
+ }
}
}
}
@@ -71,12 +83,11 @@ public abstract class Command {
}
/**
- * Returns whether or not the execution of this command is terminated,
- * either by cancellation or by successful completion.
+ * Returns whether or not the execution of this command is terminated, either by
+ * cancellation or by successful completion.
*/
public final boolean isTerminated() {
- return isExecutionCancelled()
- || (hasBeenStarted() && isExecutionCompleted());
+ return isExecutionCancelled() || (hasBeenStarted() && isExecutionCompleted());
}
/**
@@ -111,9 +122,10 @@ public abstract class Command {
}
/**
- * Called when the execution of the command has been cancelled.
+ * Called when the execution of the command has been cancelled, with an optional
+ * throwable that indicates the cause of the cancellation.
*/
- protected void afterExecutionCancelled() {
+ protected void afterExecutionCancelled(Throwable e) {
}
/**
@@ -131,11 +143,8 @@ public abstract class Command {
@Override
public String toString() {
- return this.getClass().getSimpleName()
- + " ("
- + (hasBeenStarted() ? "elapsed: "
- + String.format("%.2f", getElapsedTime()) + "s)"
- : "queued)");
+ return this.getClass().getSimpleName() + " ("
+ + (hasBeenStarted() ? "elapsed: " + String.format("%.2f", getElapsedTime()) + "s)" : "queued)");
}
}
\ No newline at end of file
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Jump.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Jump.java
index b6ab2b9..7e6ba50 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Jump.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Jump.java
@@ -1,6 +1,7 @@
package worms.internal.gui.game.commands;
import worms.facade.IFacade;
+import worms.internal.gui.GUIConstants;
import worms.internal.gui.game.PlayGameScreen;
import worms.internal.gui.game.sprites.WormSprite;
import worms.internal.gui.messages.MessageType;
@@ -21,7 +22,6 @@ public class Jump extends Command {
return worm;
}
-
@Override
protected boolean canStart() {
return getWorm() != null;
@@ -30,21 +30,23 @@ public class Jump extends Command {
@Override
protected void doStartExecution() {
try {
- this.jumpDuration = getFacade().getJumpTime(worm);
+ this.jumpDuration = getFacade().getJumpTime(worm, GUIConstants.JUMP_TIME_STEP);
} catch (ModelException e) {
- cancelExecution();
+ e.printStackTrace();
+ cancelExecution(e);
}
}
@Override
- protected void afterExecutionCancelled() {
+ protected void afterExecutionCancelled(Throwable e) {
WormSprite sprite = getScreen().getWormSprite(getWorm());
if (sprite != null) {
sprite.setIsJumping(false);
}
- getScreen().addMessage("This worm cannot jump :(", MessageType.ERROR);
+ getScreen().addMessage("This worm cannot jump :(" + (e != null ? "\n" + e.getMessage() : ""),
+ MessageType.ERROR);
}
-
+
@Override
protected void afterExecutionCompleted() {
WormSprite sprite = getScreen().getWormSprite(getWorm());
@@ -62,19 +64,16 @@ public class Jump extends Command {
if (getElapsedTime() >= jumpDuration) {
if (!hasJumped) {
hasJumped = true;
- getFacade()
- .jump(getWorm());
- double x = getFacade().getX(getWorm());
- double y = getFacade().getY(getWorm());
- sprite.setCenterLocation(getScreen().getScreenX(x),
- getScreen().getScreenY(y));
+ getFacade().jump(getWorm(), GUIConstants.JUMP_TIME_STEP);
+ if (!getFacade().isTerminated(getWorm())) {
+ double[] xy = getFacade().getLocation(getWorm());
+ sprite.setCenterLocation(getScreen().getScreenX(xy[0]), getScreen().getScreenY(xy[1]));
+ }
completeExecution();
}
} else {
- double[] xy = getFacade().getJumpStep(getWorm(),
- getElapsedTime());
- sprite.setCenterLocation(getScreen().getScreenX(xy[0]),
- getScreen().getScreenY(xy[1]));
+ double[] xy = getFacade().getJumpStep(getWorm(), getElapsedTime());
+ sprite.setCenterLocation(getScreen().getScreenX(xy[0]), getScreen().getScreenY(xy[1]));
}
} catch (ModelException e) {
e.printStackTrace();
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Move.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Move.java
index 79d575e..1b0b347 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Move.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Move.java
@@ -7,6 +7,7 @@ import worms.internal.gui.game.sprites.WormSprite;
import worms.internal.gui.messages.MessageType;
import worms.model.Worm;
import worms.util.ModelException;
+import worms.util.MustNotImplementException;
public class Move extends Command {
@@ -16,6 +17,9 @@ public class Move extends Command {
private double finalX;
private double finalY;
+ private boolean isFalling;
+ private double fallingStartTime = -1;
+
private final Worm worm;
public Move(IFacade facade, Worm worm, PlayGameScreen screen) {
@@ -29,13 +33,21 @@ public class Move extends Command {
@Override
protected boolean canStart() {
- return getWorm() != null;
+ return getWorm() != null;
}
private double getDuration() {
return GUIConstants.MOVE_DURATION;
}
+ protected boolean canFall() {
+ try {
+ return getFacade().canFall(getWorm());
+ } catch (MustNotImplementException e) {
+ return false;
+ }
+ }
+
@Override
protected void doUpdate(double dt) {
WormSprite sprite = getScreen().getWormSprite(getWorm());
@@ -48,13 +60,81 @@ public class Move extends Command {
double y = (1.0 - t) * startY + t * finalY;
sprite.setCenterLocation(x, y);
} else {
+ fall(dt);
+ }
+ } else {
+ cancelExecution();
+ }
+ }
+
+ protected boolean isFalling() {
+ return isFalling;
+ }
+
+ protected void ensureFalling() {
+ if (fallingStartTime == -1) {
+ fallingStartTime = getElapsedTime();
+ }
+ isFalling = true;
+ }
+
+ protected void fall(double dt) {
+ if (!isFalling) {
+ startFalling();
+ } else {
+ updateFalling();
+ }
+ }
+
+ protected void updateFalling() {
+ WormSprite sprite = getScreen().getWormSprite(worm);
+ if (sprite != null) {
+ double duration = getScreen().screenToWorldDistance(Math.abs(finalY - startY)) / GUIConstants.FALL_VELOCITY;
+ double timeElapsedFalling = getElapsedTime() - fallingStartTime;
+ if (timeElapsedFalling <= duration) {
+ double t = timeElapsedFalling / duration;
+ t = t * t;
+ double x = (1.0 - t) * startX + t * finalX;
+ double y = (1.0 - t) * startY + t * finalY;
+ sprite.setCenterLocation(x, y);
+ } else {
+ sprite.setCenterLocation(finalX, finalY);
completeExecution();
}
} else {
cancelExecution();
}
}
-
+
+ protected void startFalling() {
+ this.startX = getScreen().getScreenX(getObjectX());
+ this.startY = getScreen().getScreenY(getObjectY());
+
+ if (canFall()) {
+ ensureFalling();
+ getFacade().fall(getWorm());
+ if (isObjectStillActive()) {
+ this.finalX = getScreen().getScreenX(getObjectX());
+ this.finalY = getScreen().getScreenY(getObjectY());
+ } else {
+ this.finalX = startX;
+ try {
+ this.finalY = getScreen().getScreenY(getObjectY());
+ } catch (ModelException e) {
+ this.finalY = getScreen().getScreenY(0);
+ }
+ }
+ } else {
+ completeExecution();
+ }
+ WormSprite sprite = getScreen().getWormSprite(worm);
+ if (sprite != null) {
+ sprite.setCenterLocation(startX, startY);
+ } else {
+ cancelExecution();
+ }
+ }
+
@Override
protected void afterExecutionCompleted() {
WormSprite sprite = getScreen().getWormSprite(getWorm());
@@ -64,34 +144,39 @@ public class Move extends Command {
}
@Override
- protected void afterExecutionCancelled() {
+ protected void afterExecutionCancelled(Throwable e) {
WormSprite sprite = getScreen().getWormSprite(getWorm());
if (sprite != null) {
sprite.setIsMoving(false);
}
- getScreen().addMessage("This worm cannot move like that :(",
+ getScreen().addMessage("This worm cannot move like that :(" + (e != null ? "\n" + e.getMessage() : ""),
MessageType.ERROR);
}
@Override
protected void doStartExecution() {
try {
- this.startX = getScreen().getScreenX(getObjectX());
- this.startY = getScreen().getScreenY(getObjectY());
- getFacade().move(getWorm(), 1);
- this.finalX = getScreen().getScreenX(getObjectX());
- this.finalY = getScreen().getScreenY(getObjectY());
+ double[] xy = getFacade().getLocation(getWorm());
+ this.startX = getScreen().getScreenX(xy[0]);
+ this.startY = getScreen().getScreenY(xy[1]);
+ getFacade().move(getWorm());
+ xy = getFacade().getLocation(getWorm());
+ this.finalX = getScreen().getScreenX(xy[0]);
+ this.finalY = getScreen().getScreenY(xy[1]);
} catch (ModelException e) {
- e.printStackTrace();
- cancelExecution();
+ cancelExecution(e);
}
}
+ protected boolean isObjectStillActive() {
+ return !getFacade().isTerminated(getWorm());
+ }
+
protected double getObjectX() {
- return getFacade().getX(getWorm());
+ return getFacade().getLocation(getWorm())[0];
}
protected double getObjectY() {
- return getFacade().getY(getWorm());
+ return getFacade().getLocation(getWorm())[1];
}
}
\ No newline at end of file
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Resize.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Resize.java
deleted file mode 100644
index 246f725..0000000
--- a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Resize.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package worms.internal.gui.game.commands;
-
-import worms.facade.IFacade;
-import worms.internal.gui.game.PlayGameScreen;
-import worms.internal.gui.game.sprites.WormSprite;
-import worms.internal.gui.messages.MessageType;
-import worms.model.Worm;
-import worms.util.ModelException;
-
-public class Resize extends InstantaneousCommand {
- private final Worm worm;
- private final double factor;
-
- public Resize(IFacade facade, Worm worm, double factor,
- PlayGameScreen screen) {
- super(facade, screen);
- this.worm = worm;
- this.factor = factor;
- }
-
- @Override
- protected boolean canStart() {
- return worm != null;
- }
-
- @Override
- protected void doStartExecution() {
- try {
- double newRadius = factor * getFacade().getRadius(worm);
- getFacade().setRadius(worm, newRadius);
- } catch (ModelException e) {
- // an invalid radius
- getScreen().addMessage(
- "Cannot " + (factor > 1.0 ? "grow" : "shrink")
- + " that worm anymore :(", MessageType.ERROR);
- }
- WormSprite sprite = getScreen().getWormSprite(worm);
- sprite.update();
- }
-}
\ No newline at end of file
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/SelectNextWorm.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/SelectNextWorm.java
new file mode 100644
index 0000000..419833a
--- /dev/null
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/SelectNextWorm.java
@@ -0,0 +1,34 @@
+package worms.internal.gui.game.commands;
+
+import worms.facade.IFacade;
+import worms.internal.gui.game.PlayGameScreen;
+import worms.internal.gui.messages.MessageType;
+import worms.util.ModelException;
+
+public class SelectNextWorm extends InstantaneousCommand {
+
+ public SelectNextWorm(IFacade facade, PlayGameScreen screen) {
+ super(facade, screen);
+ }
+
+ @Override
+ protected boolean canStart() {
+ return true;
+ }
+
+ @Override
+ protected void afterExecutionCancelled(Throwable e) {
+ getScreen().addMessage("Cannot activate next worm :(" + (e != null ? "\n" + e.getMessage() : ""), MessageType.ERROR);
+
+ }
+
+ @Override
+ protected void doStartExecution() {
+ try {
+ getFacade().activateNextWorm(getWorld());
+ } catch (ModelException e) {
+ cancelExecution(e);
+ }
+ }
+
+}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/StartGame.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/StartGame.java
index 121901b..7445771 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/StartGame.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/StartGame.java
@@ -6,6 +6,7 @@ import worms.facade.IFacade;
import worms.internal.gui.game.PlayGameScreen;
import worms.internal.gui.messages.MessageType;
import worms.model.Worm;
+import worms.util.ModelException;
public class StartGame extends InstantaneousCommand {
@@ -15,18 +16,30 @@ public class StartGame extends InstantaneousCommand {
@Override
protected boolean canStart() {
- Collection worms = getScreen().getGameState().getWorms();
+ Collection worms = getFacade().getAllWorms(getWorld());
return worms != null && !worms.isEmpty();
}
-
+
@Override
- protected void afterExecutionCancelled() {
- getScreen().addMessage("Cannot start the game without worms", MessageType.ERROR);
+ protected void afterExecutionCancelled(Throwable e) {
+ if (e != null) {
+ getScreen().addMessage("Cannot start the game: " + e.getMessage(), MessageType.ERROR);
+ } else {
+ getScreen().addMessage("Cannot start the game without worms", MessageType.ERROR);
+ }
}
@Override
protected void doStartExecution() {
- getScreen().gameStarted();
+ try {
+ getScreen().gameStarted();
+ getFacade().startGame(getWorld());
+ if (!getFacade().hasActiveGame(getWorld())) {
+ getScreen().gameFinished();
+ }
+ } catch (ModelException e) {
+ cancelExecution(e);
+ }
}
}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Turn.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Turn.java
index cb9dcb9..be8c5b5 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Turn.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/commands/Turn.java
@@ -23,8 +23,8 @@ public class Turn extends InstantaneousCommand {
}
@Override
- protected void afterExecutionCancelled() {
- getScreen().addMessage("This worm cannot perform that turn :(", MessageType.ERROR);
+ protected void afterExecutionCancelled(Throwable e) {
+ getScreen().addMessage("This worm cannot perform that turn :(" + (e != null ? "\n" + e.getMessage() : ""), MessageType.ERROR);
}
@Override
@@ -34,7 +34,7 @@ public class Turn extends InstantaneousCommand {
double angleToTurn = GUIUtils.restrictDirection(direction + angle) - direction;
getFacade().turn(worm, angleToTurn);
} catch (ModelException e) {
- cancelExecution();
+ cancelExecution(e);
}
}
}
\ No newline at end of file
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/modes/DefaultInputMode.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/modes/DefaultInputMode.java
index c72a4ac..63da925 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/game/modes/DefaultInputMode.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/modes/DefaultInputMode.java
@@ -51,12 +51,6 @@ public class DefaultInputMode extends InputMode {
case 'N':
getScreen().renameWorm();
break;
- case '+':
- getScreen().resizeWorm(+1);
- break;
- case '-':
- getScreen().resizeWorm(-1);
- break;
}
}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/modes/EnteringNameMode.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/modes/EnteringNameMode.java
index e7a0784..bfe40be 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/game/modes/EnteringNameMode.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/modes/EnteringNameMode.java
@@ -8,6 +8,7 @@ import worms.internal.gui.game.PlayGameScreen;
public class EnteringNameMode extends InputMode {
+ @FunctionalInterface
public static interface Callback {
public void onNameEntered(String newName);
}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/modes/GameOverMode.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/modes/GameOverMode.java
new file mode 100644
index 0000000..58d8551
--- /dev/null
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/modes/GameOverMode.java
@@ -0,0 +1,46 @@
+package worms.internal.gui.game.modes;
+
+import java.awt.event.KeyEvent;
+
+import worms.internal.gui.InputMode;
+import worms.internal.gui.game.PlayGameScreen;
+import worms.internal.gui.menu.MainMenuScreen;
+
+public class GameOverMode extends InputMode {
+
+ /**
+ * @param playGameScreen
+ */
+ public GameOverMode(PlayGameScreen playGameScreen,
+ InputMode previous) {
+ super(playGameScreen, previous);
+ }
+
+ @Override
+ public void keyTyped(KeyEvent e) {
+ switch (e.getKeyChar()) {
+ case 'r':
+ case 'R':
+ // need to do this on a new thread, because otherwise the GUI will be blocked on the main menu
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ getScreen().getGUI().switchToScreen(
+ new MainMenuScreen(getScreen().getGUI()));
+ }
+ }).start();
+ break;
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ switch (e.getKeyCode()) {
+ case KeyEvent.VK_ESCAPE:
+ getScreen().getGUI().exit();
+ break;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/modes/SetupInputMode.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/modes/SetupInputMode.java
new file mode 100644
index 0000000..a3c8517
--- /dev/null
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/modes/SetupInputMode.java
@@ -0,0 +1,55 @@
+package worms.internal.gui.game.modes;
+
+import java.awt.Graphics2D;
+import java.awt.event.KeyEvent;
+
+import worms.internal.gui.InputMode;
+import worms.internal.gui.game.PlayGameScreen;
+
+public class SetupInputMode extends InputMode {
+
+ public SetupInputMode(PlayGameScreen screen,
+ InputMode previous) {
+ super(screen, previous);
+ }
+
+ @Override
+ public void keyTyped(KeyEvent e) {
+ switch (e.getKeyChar()) {
+ case 't':
+ case 'T':
+ getScreen().addEmptyTeam();
+ break;
+ case 'w':
+ case 'W':
+ getScreen().addPlayerControlledWorm();
+ break;
+ case 'f':
+ case 'F':
+ getScreen().addFood();
+ break;
+ case 's':
+ case 'S':
+ getScreen().startGame();
+ break;
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ switch (e.getKeyCode()) {
+ case KeyEvent.VK_ESCAPE:
+ getScreen().getGUI().exit();
+ break;
+ }
+ }
+
+ @Override
+ public void paintOverlay(Graphics2D g) {
+ getScreen()
+ .showInstructions(
+ g,
+ "Press\n'T' to create a new team\n'W' to add a new worm\n'F' to add food\n'S' to start the game");
+ }
+
+}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/sprites/FoodSprite.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/sprites/FoodSprite.java
new file mode 100644
index 0000000..e990efa
--- /dev/null
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/sprites/FoodSprite.java
@@ -0,0 +1,74 @@
+package worms.internal.gui.game.sprites;
+
+import worms.internal.gui.game.ImageSprite;
+import worms.internal.gui.game.PlayGameScreen;
+import worms.model.Food;
+
+public class FoodSprite extends ImageSprite {
+
+ private static final double MAX_SCALE = 100;
+ private static final double MIN_SCALE = 0.05;
+
+ private final Food food;
+
+ private double radius;
+
+ public FoodSprite(PlayGameScreen screen, Food food) {
+ super(screen, "images/burger.png");
+ this.food = food;
+ update();
+ }
+
+ /**
+ * @param radius
+ * (in worm-meter)
+ */
+ public synchronized void setRadius(double radius) {
+ this.radius = radius;
+
+ /*
+ * Height of the image (when drawn at native size) in worm-meters, given
+ * the scale at which the world is drawn to screen
+ */
+ double imageHeightInMeters = getScreen().screenToWorldDistance(
+ getImageHeight());
+
+ /*
+ * scale factor to nicely fit the image in a circle with diameter equal
+ * to the image height (value determined experimentally)
+ */
+ double fitFactor = 1.2;
+
+ double scaleFactor = fitFactor * 2 * radius / imageHeightInMeters;
+
+ scaleFactor = Math.max(MIN_SCALE, Math.min(scaleFactor, MAX_SCALE));
+
+ setScale(scaleFactor);
+ }
+
+ @Override
+ public synchronized void update() {
+ setRadius(getFacade().getRadius(getFood()));
+ double[] xy = getFacade().getLocation(getFood());
+ setCenterLocation(getScreen().getScreenX(xy[0]), getScreen().getScreenY(xy[1]));
+ }
+
+ @Override
+ public Food getObject() {
+ return getFood();
+ }
+
+ public Food getFood() {
+ return food;
+ }
+
+ @Override
+ public boolean isObjectAlive() {
+ return !getFacade().isTerminated(food);
+ }
+
+ public synchronized double getRadius() {
+ return radius;
+ }
+
+}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/game/sprites/WormSprite.java b/OGP1718-Worms/src-provided/worms/internal/gui/game/sprites/WormSprite.java
index 25a7350..f295828 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/game/sprites/WormSprite.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/game/sprites/WormSprite.java
@@ -1,24 +1,32 @@
package worms.internal.gui.game.sprites;
+import java.math.BigInteger;
+
+import worms.internal.gui.GUIConstants;
import worms.internal.gui.GUIUtils;
import worms.internal.gui.game.ImageSprite;
import worms.internal.gui.game.PlayGameScreen;
+import worms.model.Team;
import worms.model.Worm;
import worms.util.ModelException;
+import worms.util.MustNotImplementException;
public class WormSprite extends ImageSprite {
private static final double MAX_SCALE = 100;
private static final double MIN_SCALE = 0.05;
private final Worm worm;
-
+
private boolean isJumping;
private boolean isMoving;
private double[][] xys;
private double orientation;
private String name;
+ private String teamName;
+ private boolean atImpassableTerrain;
private long actionPoints;
private long maxActionPoints;
+ private BigInteger hitPoints;
private double actualX;
private double actualY;
private double radius;
@@ -82,23 +90,43 @@ public class WormSprite extends ImageSprite {
return dx * dx + dy * dy <= radius * radius;
}
+ @Override
+ public boolean isObjectAlive() {
+ return !getFacade().isTerminated(getWorm());
+ }
+
@Override
public synchronized void update() {
+ double[] xy = getFacade().getLocation(getWorm());
if (isJumping || isMoving) {
// don't update the location here, because it may differ from the
- // location in the model
+ // location in the model (interpolation)
} else {
- setCenterLocation(getScreen().getScreenX(getFacade().getX(getWorm())),
- getScreen().getScreenY(getFacade().getY(worm)));
+ setCenterLocation(
+ getScreen().getScreenX(xy[0]),
+ getScreen().getScreenY(xy[1]));
}
- this.actualX = getFacade().getX(getWorm());
- this.actualY = getFacade().getY(getWorm());
+ this.actualX = xy[0];
+ this.actualY = xy[1];
setRadius(getFacade().getRadius(getWorm()));
setDirection(getFacade().getOrientation(getWorm()));
updateJumpTime();
setName(getFacade().getName(getWorm()));
+ try {
+ Team team = getFacade().getTeam(getWorm());
+ if (team != null) {
+ setTeamName(getFacade().getName(team));
+ } else {
+ setTeamName("_teamless_");
+ }
+ } catch (MustNotImplementException e) {
+ setTeamName("No Teams");
+ }
+ this.atImpassableTerrain = !getFacade().isPassable(getScreen().getWorld(),
+ xy, getFacade().getRadius(getWorm()));
this.actionPoints = getFacade().getNbActionPoints(getWorm());
this.maxActionPoints = getFacade().getMaxNbActionPoints(getWorm());
+ this.hitPoints = getFacade().getNbHitPoints(getWorm());
}
public void setIsJumping(boolean isJumping) {
@@ -113,7 +141,7 @@ public class WormSprite extends ImageSprite {
private void updateJumpTime() {
try {
- double time = getFacade().getJumpTime(getWorm());
+ double time = getFacade().getJumpTime(getWorm(), GUIConstants.JUMP_TIME_STEP);
if (time > 0) {
int n = 1 + (int) (time / JUMP_MARKER_TIME_DISTANCE);
xys = new double[n][];
@@ -146,6 +174,18 @@ public class WormSprite extends ImageSprite {
this.name = name;
}
+ public synchronized String getTeamName() {
+ return teamName;
+ }
+
+ private void setTeamName(String teamName) {
+ this.teamName = teamName;
+ }
+
+ public synchronized boolean isAtImpassableTerrain() {
+ return atImpassableTerrain;
+ }
+
public synchronized long getActionPoints() {
return actionPoints;
}
@@ -154,6 +194,10 @@ public class WormSprite extends ImageSprite {
return maxActionPoints;
}
+ public synchronized BigInteger getHitPoints() {
+ return hitPoints;
+ }
+
public synchronized double getActualX() {
return actualX;
}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/menu/ChooseLevelScreen.java b/OGP1718-Worms/src-provided/worms/internal/gui/menu/ChooseLevelScreen.java
new file mode 100644
index 0000000..72b7c59
--- /dev/null
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/menu/ChooseLevelScreen.java
@@ -0,0 +1,30 @@
+package worms.internal.gui.menu;
+
+import worms.internal.gui.Level;
+import worms.internal.gui.WormsGUI;
+
+class ChooseLevelScreen extends AbstractMenuScreen {
+
+ public ChooseLevelScreen(WormsGUI gui) {
+ super(gui);
+ }
+
+ @Override
+ protected Level[] getChoices() {
+ return Level.getAvailableLevels();
+ }
+
+ @Override
+ protected String getDisplayName(Level level) {
+ return level.getName();
+ }
+
+ @Override
+ protected String getInstructions() {
+ return "Choose the level you want to play";
+ }
+
+ @Override
+ public void screenStarted() {
+ }
+}
diff --git a/OGP1718-Worms/src-provided/worms/internal/gui/menu/MainMenuScreen.java b/OGP1718-Worms/src-provided/worms/internal/gui/menu/MainMenuScreen.java
index 3453d40..49fdfd4 100644
--- a/OGP1718-Worms/src-provided/worms/internal/gui/menu/MainMenuScreen.java
+++ b/OGP1718-Worms/src-provided/worms/internal/gui/menu/MainMenuScreen.java
@@ -1,8 +1,10 @@
package worms.internal.gui.menu;
import worms.internal.gui.GameState;
+import worms.internal.gui.Level;
import worms.internal.gui.WormsGUI;
import worms.internal.gui.game.PlayGameScreen;
+import worms.internal.gui.messages.MessageType;
enum MainMenuOption {
Play("Play worms"), PlayDebug("Play worms (debug mode)"), Exit("Exit");
@@ -56,13 +58,28 @@ public class MainMenuScreen extends AbstractMenuScreen {
private void startGame(boolean debugMode) {
WormsGUI gui = getGUI();
+
+ ChooseLevelScreen chooseLevel = new ChooseLevelScreen(gui);
+ getGUI().switchToScreen(chooseLevel);
+ Level level = chooseLevel.select();
+ if (debugMode) {
+ chooseLevel
+ .addMessage(
+ "Loading level, please wait...\n\n(This can take a while in debug mode)",
+ MessageType.NORMAL);
+ } else {
+ chooseLevel.addMessage("Loading level, please wait...",
+ MessageType.NORMAL);
+ }
+
GameState gameState = new GameState(gui.getFacade(),
- gui.getOptions().randomSeed, gui.getWidth(), gui.getHeight());
+ gui.getOptions().randomSeed, level);
PlayGameScreen playGameScreen = PlayGameScreen.create(gui, gameState,
debugMode);
- gameState.startGame();
+ gameState.createWorld();
+
getGUI().switchToScreen(playGameScreen);
}
diff --git a/OGP1718-Worms/src-provided/worms/util/MustNotImplementException.java b/OGP1718-Worms/src-provided/worms/util/MustNotImplementException.java
new file mode 100644
index 0000000..e6de852
--- /dev/null
+++ b/OGP1718-Worms/src-provided/worms/util/MustNotImplementException.java
@@ -0,0 +1,10 @@
+package worms.util;
+
+@SuppressWarnings("serial")
+/**
+ * Students working alone on the project will throw MustNotImplementException
+ * for each method they must not implement.
+ */
+public class MustNotImplementException extends RuntimeException{
+
+}
diff --git a/OGP1718-Worms/tests/worms/model/PartialFacadeTest.java b/OGP1718-Worms/tests/worms/model/PartialFacadeTest.java
deleted file mode 100644
index 56f1df2..0000000
--- a/OGP1718-Worms/tests/worms/model/PartialFacadeTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package worms.model;
-import org.junit.Before;
-import org.junit.Test;
-import worms.facade.Facade;
-import worms.facade.IFacade;
-import worms.util.ModelException;
-
-import static org.junit.Assert.assertEquals;
-
-public class PartialFacadeTest {
-
- private static final double EPS = 1e-4;
-
- private IFacade facade;
-
- @Before
- public void setup() {
- facade = new Facade();
- }
-
- @Test
- public void testMaximumActionPoints() {
- Worm worm = facade.createWorm(new double[] {0.0,0.0}, 0, 1, "Test");
- assertEquals(4448, facade.getMaxNbActionPoints(worm));
- }
-
- @Test
- public void testMoveHorizontal() {
- Worm worm = facade.createWorm(new double[] {0.0,0.0}, 0, 1, "Test");
- facade.move(worm, 5);
- assertEquals(5, facade.getX(worm), EPS);
- assertEquals(0, facade.getY(worm), EPS);
- }
-
- @Test
- public void testMoveVertical() {
- Worm worm = facade.createWorm(new double[] {0.0,0.0}, Math.PI / 2, 1, "Test");
- facade.move(worm, 5);
- assertEquals(0, facade.getX(worm), EPS);
- assertEquals(5, facade.getY(worm), EPS);
- }
-
- @Test(expected = ModelException.class)
- public void testJumpException() {
- Worm worm = facade.createWorm(new double[] {0.0,0.0}, 3 * Math.PI / 2, 1, "Test");
- facade.jump(worm);
- }
-
-}
diff --git a/OGP1718-Worms/tests/worms/model/PartialPart2FacadeTest.java b/OGP1718-Worms/tests/worms/model/PartialPart2FacadeTest.java
new file mode 100644
index 0000000..71f2690
--- /dev/null
+++ b/OGP1718-Worms/tests/worms/model/PartialPart2FacadeTest.java
@@ -0,0 +1,81 @@
+package worms.model;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import worms.facade.Facade;
+import worms.facade.IFacade;
+import worms.model.Worm;
+
+public class PartialPart2FacadeTest {
+
+ private static final double EPS = 1e-4;
+
+ private IFacade facade;
+
+ // X X X X //
+ // . . . . //
+ // . . . . //
+ // X X X X //
+ private boolean[][] passableMap = new boolean[][] { //
+ { false, false, false, false }, //
+ { true, true, true, true }, //
+ { true, true, true, true }, //
+ { false, false, false, false } };
+
+ private World world;
+
+ @Before
+ public void setup() {
+ facade = new Facade();
+ world = facade.createWorld(4.0, 4.0, passableMap);
+ }
+
+ @Test
+ public void testMaximumActionPoints() {
+ Worm worm = facade.createWorm(world, new double[] { 1, 2 }, 0, 1, "Test", null);
+ assertEquals(4448, facade.getMaxNbActionPoints(worm));
+ }
+
+ @Test
+ public void testMoveHorizontal() {
+ Worm worm = facade.createWorm(world, new double[] { 1, 2 }, 0, 1, "Test", null);
+ facade.move(worm);
+ double[] xy = facade.getLocation(worm);
+ assertEquals(2, xy[0], EPS);
+ assertEquals(2, xy[1], EPS);
+ }
+
+ @Test
+ public void testMoveVertical() {
+ Worm worm = facade.createWorm(world, new double[] { 1, 1.5 }, Math.PI / 2, 0.5, "Test", null);
+ facade.move(worm);
+ double[] xy = facade.getLocation(worm);
+ assertEquals(1, xy[0], EPS);
+ assertEquals(2.0, xy[1], EPS);
+ }
+
+ @Test
+ public void testFall() {
+ // . X .
+ // . w .
+ // . . .
+ // X X X
+ World world = facade.createWorld(3.0, 4.0, new boolean[][] { //
+ { true, false, true }, //
+ { true, true, true }, //
+ { true, true, true }, //
+ { false, false, false } });
+ Worm worm = facade.createWorm(world, new double[] { 1.5, 2.5 }, 3*Math.PI/2, 0.5, "Test", null);
+ assertFalse(facade.canFall(worm));
+ facade.move(worm);
+ assertTrue(facade.canFall(worm));
+ facade.fall(worm);
+ double[] xy = facade.getLocation(worm);
+ assertEquals(1.5, xy[0], EPS);
+ assertEquals(1.5, xy[1], EPS);
+ }
+
+}