Release v1.0
This commit is contained in:
30
OGP1718-Worms/src-provided/worms/Worms.java
Normal file
30
OGP1718-Worms/src-provided/worms/Worms.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package worms;
|
||||
|
||||
import worms.facade.Facade;
|
||||
import worms.internal.gui.GUIOptions;
|
||||
import worms.internal.gui.WormsGUI;
|
||||
|
||||
public class Worms {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new WormsGUI(new Facade(), parseOptions(args)).start();
|
||||
}
|
||||
|
||||
private static GUIOptions parseOptions(String[] args) {
|
||||
GUIOptions options = new GUIOptions();
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String arg = args[i];
|
||||
if ("-window".equals(arg)) {
|
||||
options.disableFullScreen = true;
|
||||
} else if ("-seed".equals(arg)) {
|
||||
long randomSeed = Long.parseLong(args[++i]);
|
||||
options.randomSeed = randomSeed;
|
||||
} else if ("-clickselect".equals(arg)) {
|
||||
options.enableClickToSelect = true;
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
168
OGP1718-Worms/src-provided/worms/facade/IFacade.java
Normal file
168
OGP1718-Worms/src-provided/worms/facade/IFacade.java
Normal file
@@ -0,0 +1,168 @@
|
||||
package worms.facade;
|
||||
|
||||
import worms.model.Worm;
|
||||
import worms.util.ModelException;
|
||||
|
||||
/**
|
||||
* Implement this interface to connect your code to the graphical user interface
|
||||
* (GUI).
|
||||
*
|
||||
* <ul>
|
||||
* <li>For separating the code that you wrote from the code that was provided to
|
||||
* you, put <b>ALL your code in the <code>src</code> folder.</b> The code that
|
||||
* is provided to you stays in the <code>src-provided</code> folder. If you
|
||||
* modify the provided code, you may need to manually merge any future bugfixes
|
||||
* and update.</li>
|
||||
*
|
||||
* <li>You should at least create the following classes for the code to compile:
|
||||
* <ul>
|
||||
* <li>a class <code>Worm</code> in the package <code>worms.model</code> for
|
||||
* representing a worm</li>
|
||||
* <li>a class <code>Facade</code> in the package <code>worms.facade</code> that
|
||||
* implements this interface (<code>IFacade</code>).</li>
|
||||
* </ul>
|
||||
* You may, of course, add additional classes as you see fit.
|
||||
*
|
||||
* <li>The header of that Facade class should look as follows:<br>
|
||||
* <code>class Facade implements IFacade { ... }</code><br>
|
||||
* Consult the <a href=
|
||||
* "http://docs.oracle.com/javase/tutorial/java/IandI/createinterface.html">
|
||||
* Java tutorial</a> for more information on interfaces, if necessary.</li>
|
||||
*
|
||||
* <li>Each method defined in the interface <code>IFacade</code> must be
|
||||
* implemented by the class <code>Facade</code>. For example, the implementation
|
||||
* of <code>getX</code> should call a method of the given <code>worm</code> to
|
||||
* retrieve its x-coordinate.</li>
|
||||
*
|
||||
* <li>Your <code>Facade</code> class should offer a default constructor.</li>
|
||||
*
|
||||
* <li>Methods in this interface are allowed to throw only
|
||||
* <code>ModelException</code>. No other exception types are allowed. This
|
||||
* exception can be thrown only if (1) calling the method with the given
|
||||
* parameters would violate a precondition, or (2) if the method causes an
|
||||
* exception to be thrown in your code (if so, wrap the exception in a
|
||||
* <code>ModelException</code>).</li>
|
||||
*
|
||||
* <li><b>ModelException should not be used anywhere outside of your Facade
|
||||
* implementation.</b></li>
|
||||
*
|
||||
* <li>Your Facade implementation should <b>only contain trivial code</b> (for
|
||||
* example, calling a method, combining multiple return values into an array,
|
||||
* creating @Value instances, catching exceptions and wrapping it in a
|
||||
* ModelException). All non-trivial code should be placed in the other classes
|
||||
* that you create.</li>
|
||||
*
|
||||
* <li>The rules described above and the documentation described below for each
|
||||
* method apply only to the class implementing IFacade. Your class for
|
||||
* representing worms should follow the rules described in the assignment.</li>
|
||||
*
|
||||
* <li>Do not modify the signatures of the methods defined in this
|
||||
* interface.</li>
|
||||
*
|
||||
* </ul>
|
||||
*/
|
||||
public interface IFacade {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param coordinates
|
||||
* An array containing the x-coordinate of the position of the new
|
||||
* worm followed by the y-coordinate of the position of the new worm
|
||||
* (in meter)
|
||||
* @param direction
|
||||
* The direction of the new worm (in radians)
|
||||
* @param radius
|
||||
* The radius of the new worm (in meter)
|
||||
* @param name
|
||||
* The name of the new worm
|
||||
*/
|
||||
Worm createWorm(double[] location, double direction, double radius, String name) throws ModelException;
|
||||
|
||||
/**
|
||||
* Moves the given worm by the given number of steps.
|
||||
*/
|
||||
void move(Worm worm, int nbSteps) throws ModelException;
|
||||
|
||||
/**
|
||||
* Turns the given worm by the given angle.
|
||||
*/
|
||||
void turn(Worm worm, double angle) throws ModelException;
|
||||
|
||||
/**
|
||||
* Makes the given worm jump.
|
||||
*/
|
||||
void jump(Worm worm) throws ModelException;
|
||||
|
||||
/**
|
||||
* Returns the total amount of time (in seconds) that a jump of the given worm
|
||||
* would take.
|
||||
*/
|
||||
double getJumpTime(Worm worm) 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.
|
||||
*/
|
||||
double[] getJumpStep(Worm worm, double t) throws ModelException;
|
||||
|
||||
/**
|
||||
* Returns the x-coordinate of the current location of the given worm.
|
||||
*/
|
||||
double getX(Worm worm) throws ModelException;
|
||||
|
||||
/**
|
||||
* Returns the y-coordinate of the current location of the given worm.
|
||||
*/
|
||||
double getY(Worm worm) throws ModelException;
|
||||
|
||||
/**
|
||||
* Returns the current orientation of the given worm (in radians).
|
||||
*/
|
||||
double getOrientation(Worm worm) throws ModelException;
|
||||
|
||||
/**
|
||||
* Returns the radius of the given worm.
|
||||
*/
|
||||
double getRadius(Worm worm) throws ModelException;
|
||||
|
||||
/**
|
||||
* Sets 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.
|
||||
*/
|
||||
long getNbActionPoints(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.
|
||||
*/
|
||||
long getMaxNbActionPoints(Worm worm) throws ModelException;
|
||||
|
||||
/**
|
||||
* Returns the name the given worm.
|
||||
*/
|
||||
String getName(Worm worm) throws ModelException;
|
||||
|
||||
/**
|
||||
* Renames the given worm.
|
||||
*/
|
||||
void rename(Worm worm, String newName) throws ModelException;
|
||||
|
||||
/**
|
||||
* Returns the mass of the given worm.
|
||||
*/
|
||||
double getMass(Worm worm) throws ModelException;
|
||||
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package worms.internal.gui;
|
||||
|
||||
public abstract class AbstractPainter<S extends Screen> {
|
||||
|
||||
private final S screen;
|
||||
|
||||
protected AbstractPainter(S screen) {
|
||||
this.screen = screen;
|
||||
}
|
||||
|
||||
protected S getScreen() {
|
||||
return screen;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
package worms.internal.gui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public class ErrorScreen extends Screen {
|
||||
|
||||
private final String message;
|
||||
|
||||
public ErrorScreen(WormsGUI gui, String message) {
|
||||
super(gui);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void screenStarted() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputMode<ErrorScreen> createDefaultInputMode() {
|
||||
return new InputMode<ErrorScreen>(this, null) {
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
|
||||
getGUI().exit();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintScreen(Graphics2D g) {
|
||||
g.setColor(Color.RED);
|
||||
GUIUtils.drawCenteredString((Graphics2D) g, "An error has occurred",
|
||||
getScreenWidth(), 20);
|
||||
StringTokenizer tok = new StringTokenizer(message, "\n");
|
||||
int y = 50;
|
||||
while (tok.hasMoreElements()) {
|
||||
String line = tok.nextToken();
|
||||
GUIUtils.drawCenteredString((Graphics2D) g, line, getScreenWidth(),
|
||||
y);
|
||||
y += (g.getFont().getSize() * 7) / 5;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
package worms.internal.gui;
|
||||
|
||||
public final class GUIConstants {
|
||||
|
||||
/**
|
||||
* Default width of the window, when not running in full-screen, in pixels
|
||||
*/
|
||||
public static final int DEFAULT_WINDOW_WIDTH = 1024;
|
||||
|
||||
/**
|
||||
* Default height of the window, when not running in full-screen, in pixels
|
||||
*/
|
||||
public static final int DEFAULT_WINDOW_HEIGHT = 768;
|
||||
|
||||
/**
|
||||
* Framerate at which to re-draw the screen, in frames per (real) second
|
||||
*/
|
||||
public static final int FRAMERATE = 15; // fps
|
||||
|
||||
/**
|
||||
* Time (in worm-seconds) that elapses in 1 real second
|
||||
*/
|
||||
public static final double TIME_SCALE = 0.7;
|
||||
|
||||
/**
|
||||
* Minimal angle to turn when pressing the 'turn' key a single time
|
||||
*/
|
||||
public static final double MIN_TURN_ANGLE = Math.PI / 120.0;
|
||||
|
||||
/**
|
||||
* Angle that is turned per (real) second while keeping the 'turn' keys
|
||||
* pressed.
|
||||
*/
|
||||
public static final double ANGLE_TURNED_PER_SECOND = Math.PI;
|
||||
|
||||
/**
|
||||
* Duration of the move animation for a single step (in worm-seconds)
|
||||
*/
|
||||
public static final double MOVE_DURATION = 0.1;
|
||||
|
||||
/**
|
||||
* Time to display messages on the screen (in real seconds)
|
||||
*/
|
||||
public static final double MESSAGE_DISPLAY_TIME = 1.5;
|
||||
|
||||
/**
|
||||
* Default velocity with which worms fall down (in worm-meter per worm-seconds)
|
||||
*/
|
||||
public static final double FALL_VELOCITY = 5.0;
|
||||
|
||||
/**
|
||||
* Time step to use when calculating jump positions
|
||||
*/
|
||||
public static final double JUMP_TIME_STEP = 1e-4;
|
||||
|
||||
/**
|
||||
* Scale to use when displaying the world (in worm-meter per pixel)
|
||||
*/
|
||||
public static final double DISPLAY_SCALE = 1.0/45;
|
||||
|
||||
/* disable instantiations */
|
||||
private GUIConstants() {
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package worms.internal.gui;
|
||||
|
||||
public class GUIOptions {
|
||||
|
||||
/**
|
||||
* Disable full screen mode
|
||||
* Default: false
|
||||
*
|
||||
* Full screen can also be disabled from the command line by providing the -window argument
|
||||
*/
|
||||
public boolean disableFullScreen = false;
|
||||
|
||||
/**
|
||||
* Random seed for the game
|
||||
* Default: 3
|
||||
*
|
||||
* Can also be set from the command line with the -seed argument
|
||||
*/
|
||||
public long randomSeed = 3;
|
||||
|
||||
/**
|
||||
* Enable quick worm selection by clicking the mouse
|
||||
* Default: false
|
||||
*
|
||||
* Can also be enabled from the command line with the -clickselect argument
|
||||
*/
|
||||
public boolean enableClickToSelect = false;
|
||||
|
||||
}
|
92
OGP1718-Worms/src-provided/worms/internal/gui/GUIUtils.java
Normal file
92
OGP1718-Worms/src-provided/worms/internal/gui/GUIUtils.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package worms.internal.gui;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
public class GUIUtils {
|
||||
|
||||
public static Ellipse2D.Double circleAt(double centerX, double centerY,
|
||||
double r) {
|
||||
return new Ellipse2D.Double(centerX - r, centerY - r, 2 * r, 2 * r);
|
||||
}
|
||||
|
||||
public static void drawCenteredString(Graphics2D g2d, String text,
|
||||
double width, double y) {
|
||||
Rectangle2D bounds = g2d.getFontMetrics().getStringBounds(text, g2d);
|
||||
g2d.drawString(text, (int) (width / 2 - bounds.getCenterX()), (int) y);
|
||||
}
|
||||
|
||||
public static double restrictDirection(double direction) {
|
||||
return restrictAngle(direction, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restrict angle to [min, min+2pi)
|
||||
*/
|
||||
public static double restrictAngle(double angle, double min) {
|
||||
while (angle < min) {
|
||||
angle += 2 * Math.PI;
|
||||
}
|
||||
double max = min + 2 * Math.PI;
|
||||
while (angle >= max) {
|
||||
angle -= 2 * Math.PI;
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
|
||||
public static double distance(double x1, double y1, double x2, double y2) {
|
||||
double dx = x1 - x2;
|
||||
double dy = y1 - y2;
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
public static Image scaleTo(BufferedImage image, int screenWidth,
|
||||
int screenHeight, int hints) {
|
||||
double ratio = Math.min((double) screenHeight / image.getHeight(),
|
||||
(double) screenWidth / image.getWidth());
|
||||
return image.getScaledInstance((int) (ratio * image.getWidth()),
|
||||
(int) (ratio * image.getHeight()), hints);
|
||||
}
|
||||
|
||||
public static InputStream openResource(String filename) throws IOException {
|
||||
URL url = toURL(filename);
|
||||
return openResource(url);
|
||||
}
|
||||
|
||||
public static InputStream openResource(URL url) throws IOException {
|
||||
InputStream result;
|
||||
|
||||
URLConnection conn = url.openConnection();
|
||||
result = conn.getInputStream();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static URL toURL(String filename) throws FileNotFoundException {
|
||||
URL url = GUIUtils.class.getResource("/" + filename);
|
||||
if (url == null) {
|
||||
try {
|
||||
File file = new File(filename);
|
||||
if (file.exists()) {
|
||||
url = new File(filename).toURI().toURL();
|
||||
} else {
|
||||
throw new FileNotFoundException("File not found: " + filename);
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
128
OGP1718-Worms/src-provided/worms/internal/gui/GameState.java
Normal file
128
OGP1718-Worms/src-provided/worms/internal/gui/GameState.java
Normal file
@@ -0,0 +1,128 @@
|
||||
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;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import worms.facade.IFacade;
|
||||
import worms.internal.gui.game.commands.Command;
|
||||
import worms.model.Worm;
|
||||
|
||||
public class GameState {
|
||||
|
||||
private final Random random;
|
||||
private final IFacade facade;
|
||||
private final Collection<Worm> worms = new ArrayList<Worm>();
|
||||
|
||||
private final BlockingQueue<Double> timeDelta = new LinkedBlockingQueue<Double>(
|
||||
1);
|
||||
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
public GameState(IFacade facade, long randomSeed, int width, int height) {
|
||||
this.random = new Random(randomSeed);
|
||||
this.facade = facade;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
private List<String> wormNames = Arrays.asList("Shari", "Shannon",
|
||||
"Willard", "Jodi", "Santos", "Ross", "Cora", "Jacob", "Homer",
|
||||
"Kara");
|
||||
private int nameIndex = 0;
|
||||
private Iterator<Worm> 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 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()) {
|
||||
try {
|
||||
Double dt = timeDelta.poll(1000 / GUIConstants.FRAMERATE,
|
||||
TimeUnit.MILLISECONDS); // blocks, but allows repainting
|
||||
// if necessary
|
||||
if (dt != null) {
|
||||
cmd.update(dt);
|
||||
}
|
||||
cmd.getScreen().repaint(); // repaint while executing command
|
||||
// (which might block GUI thread)
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return cmd.isExecutionCompleted();
|
||||
}
|
||||
|
||||
public void startGame() {
|
||||
createRandomWorms();
|
||||
selectNextWorm();
|
||||
}
|
||||
|
||||
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<Worm> getWorms() {
|
||||
return Collections.unmodifiableCollection(worms);
|
||||
}
|
||||
|
||||
public Random getRandom() {
|
||||
return random;
|
||||
}
|
||||
|
||||
}
|
80
OGP1718-Worms/src-provided/worms/internal/gui/InputMode.java
Normal file
80
OGP1718-Worms/src-provided/worms/internal/gui/InputMode.java
Normal file
@@ -0,0 +1,80 @@
|
||||
package worms.internal.gui;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.event.MouseMotionListener;
|
||||
|
||||
public class InputMode<ScreenType extends Screen> implements KeyListener, MouseListener,
|
||||
MouseMotionListener {
|
||||
|
||||
private final ScreenType screen;
|
||||
private final InputMode<ScreenType> previous;
|
||||
|
||||
public InputMode(ScreenType screen, InputMode<ScreenType> previous) {
|
||||
this.screen = screen;
|
||||
this.previous = previous;
|
||||
}
|
||||
|
||||
public ScreenType getScreen() {
|
||||
return screen;
|
||||
}
|
||||
|
||||
public void leaveInputMode() {
|
||||
if (previous == null) {
|
||||
getScreen().switchInputMode(getScreen().createDefaultInputMode());
|
||||
} else {
|
||||
getScreen().switchInputMode(previous);
|
||||
}
|
||||
}
|
||||
|
||||
public void paintOverlay(Graphics2D g) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
}
|
||||
}
|
133
OGP1718-Worms/src-provided/worms/internal/gui/Screen.java
Normal file
133
OGP1718-Worms/src-provided/worms/internal/gui/Screen.java
Normal file
@@ -0,0 +1,133 @@
|
||||
package worms.internal.gui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import worms.internal.gui.messages.Message;
|
||||
import worms.internal.gui.messages.MessageDisplay;
|
||||
import worms.internal.gui.messages.MessagePainter;
|
||||
import worms.internal.gui.messages.MessageType;
|
||||
|
||||
public abstract class Screen {
|
||||
|
||||
private MessageDisplay messageDisplay = new MessageDisplay();
|
||||
|
||||
private final WormsGUI gui;
|
||||
private final JComponent contents;
|
||||
private final MessagePainter messagePainter;
|
||||
|
||||
protected Screen(WormsGUI gui) {
|
||||
this.gui = gui;
|
||||
|
||||
this.contents = createContents();
|
||||
contents.setFocusable(true);
|
||||
contents.setFocusTraversalKeysEnabled(false);
|
||||
|
||||
this.messagePainter = createMessagePainter();
|
||||
|
||||
switchInputMode(createDefaultInputMode());
|
||||
}
|
||||
|
||||
protected MessagePainter createMessagePainter() {
|
||||
return new MessagePainter(this);
|
||||
}
|
||||
|
||||
public JComponent getContents() {
|
||||
return contents;
|
||||
}
|
||||
|
||||
protected JComponent createContents() {
|
||||
@SuppressWarnings("serial")
|
||||
JComponent result = new JPanel() {
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
Graphics2D graphics = (Graphics2D) g;
|
||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
Screen.this.paintScreen(graphics);
|
||||
|
||||
Screen.this.paintMessage(graphics);
|
||||
|
||||
InputMode<? extends Screen> inputMode = getCurrentInputMode();
|
||||
if (inputMode != null)
|
||||
inputMode.paintOverlay(graphics);
|
||||
}
|
||||
};
|
||||
result.setBackground(Color.BLACK);
|
||||
return result;
|
||||
}
|
||||
|
||||
public WormsGUI getGUI() {
|
||||
return gui;
|
||||
}
|
||||
|
||||
protected abstract InputMode<? extends Screen> createDefaultInputMode();
|
||||
|
||||
private InputMode<? extends Screen> currentInputMode;
|
||||
|
||||
public InputMode<? extends Screen> getCurrentInputMode() {
|
||||
return currentInputMode;
|
||||
}
|
||||
|
||||
public <ST extends Screen> void switchInputMode(InputMode<ST> newMode) {
|
||||
if (currentInputMode != null) {
|
||||
contents.removeKeyListener(currentInputMode);
|
||||
contents.removeMouseListener(currentInputMode);
|
||||
contents.removeMouseMotionListener(currentInputMode);
|
||||
}
|
||||
currentInputMode = newMode;
|
||||
if (newMode != null) {
|
||||
contents.addKeyListener(newMode);
|
||||
contents.addMouseListener(newMode);
|
||||
contents.addMouseMotionListener(newMode);
|
||||
}
|
||||
}
|
||||
|
||||
protected void paintScreen(Graphics2D g) {
|
||||
}
|
||||
|
||||
protected void paintMessage(Graphics2D g) {
|
||||
Message message = messageDisplay.getMessage();
|
||||
if (message != null && messagePainter != null) {
|
||||
messagePainter.paintMessage(g, message);
|
||||
}
|
||||
}
|
||||
|
||||
public void addMessage(String message, MessageType type) {
|
||||
messageDisplay.addMessage(message, type);
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void screenStarted() {
|
||||
}
|
||||
|
||||
public int getScreenHeight() {
|
||||
return getContents().getHeight();
|
||||
}
|
||||
|
||||
public int getScreenWidth() {
|
||||
return getContents().getWidth();
|
||||
}
|
||||
|
||||
public void repaint() {
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
getContents().paintImmediately(getContents().getVisibleRect());
|
||||
} else {
|
||||
getContents().repaint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void screenStopped() {
|
||||
switchInputMode(null);
|
||||
}
|
||||
|
||||
}
|
147
OGP1718-Worms/src-provided/worms/internal/gui/WormsGUI.java
Normal file
147
OGP1718-Worms/src-provided/worms/internal/gui/WormsGUI.java
Normal file
@@ -0,0 +1,147 @@
|
||||
package worms.internal.gui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GraphicsDevice;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import worms.facade.IFacade;
|
||||
import worms.internal.gui.menu.MainMenuScreen;
|
||||
|
||||
public class WormsGUI {
|
||||
|
||||
private JFrame window;
|
||||
private JPanel screenPanel;
|
||||
|
||||
private Screen currentScreen = null;
|
||||
|
||||
private final GUIOptions options;
|
||||
private final IFacade facade;
|
||||
|
||||
public WormsGUI(IFacade facade, GUIOptions options) {
|
||||
this.facade = facade;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public void switchToScreen(Screen newScreen) {
|
||||
if (currentScreen != null) {
|
||||
currentScreen.screenStopped();
|
||||
screenPanel.remove(currentScreen.getContents());
|
||||
}
|
||||
currentScreen = newScreen;
|
||||
if (newScreen != null) {
|
||||
screenPanel.add(newScreen.getContents(), BorderLayout.CENTER);
|
||||
screenPanel.validate();
|
||||
setFocusToCurrentScreen();
|
||||
newScreen.screenStarted();
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
try {
|
||||
initializeGUI();
|
||||
gotoMainMenu();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
showError(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void gotoMainMenu() {
|
||||
MainMenuScreen menuScreen = new MainMenuScreen(this);
|
||||
switchToScreen(menuScreen);
|
||||
}
|
||||
|
||||
public void exit() {
|
||||
window.dispose();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private void initializeGUI() {
|
||||
GraphicsEnvironment env = GraphicsEnvironment
|
||||
.getLocalGraphicsEnvironment();
|
||||
if (env.isHeadlessInstance()) {
|
||||
System.out.println("Graphics not supported");
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
this.window = new JFrame("Worms");
|
||||
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
window.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowGainedFocus(WindowEvent e) {
|
||||
setFocusToCurrentScreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowActivated(WindowEvent e) {
|
||||
setFocusToCurrentScreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
exit();
|
||||
};
|
||||
});
|
||||
window.setFocusable(false);
|
||||
window.setFocusTraversalKeysEnabled(false);
|
||||
|
||||
this.screenPanel = new JPanel();
|
||||
screenPanel.setLayout(new BorderLayout());
|
||||
screenPanel.setBackground(Color.WHITE);
|
||||
screenPanel.setFocusable(false);
|
||||
window.getContentPane().add(screenPanel);
|
||||
|
||||
GraphicsDevice device = env.getDefaultScreenDevice();
|
||||
if (device.isFullScreenSupported() && !options.disableFullScreen) {
|
||||
window.setUndecorated(true);
|
||||
window.pack();
|
||||
device.setFullScreenWindow(window);
|
||||
} else {
|
||||
window.setUndecorated(false);
|
||||
screenPanel.setPreferredSize(new Dimension(
|
||||
GUIConstants.DEFAULT_WINDOW_WIDTH,
|
||||
GUIConstants.DEFAULT_WINDOW_HEIGHT));
|
||||
window.pack();
|
||||
}
|
||||
|
||||
window.setVisible(true);
|
||||
}
|
||||
|
||||
public void showError(String message) {
|
||||
if (message == null) {
|
||||
message = "(Unknown error)";
|
||||
}
|
||||
ErrorScreen errorScreen = new ErrorScreen(this, message);
|
||||
switchToScreen(errorScreen);
|
||||
}
|
||||
|
||||
public IFacade getFacade() {
|
||||
return facade;
|
||||
}
|
||||
|
||||
public GUIOptions getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return currentScreen.getScreenWidth();
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return currentScreen.getScreenHeight();
|
||||
}
|
||||
|
||||
private void setFocusToCurrentScreen() {
|
||||
if (currentScreen != null) {
|
||||
currentScreen.getContents().requestFocusInWindow();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
package worms.internal.gui.game;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import worms.facade.IFacade;
|
||||
import worms.internal.gui.GameState;
|
||||
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.StartGame;
|
||||
import worms.internal.gui.game.commands.Turn;
|
||||
import worms.internal.gui.messages.MessageType;
|
||||
import worms.model.Worm;
|
||||
|
||||
class DefaultActionHandler implements IActionHandler {
|
||||
|
||||
private final PlayGameScreen screen;
|
||||
private final boolean userInitiated;
|
||||
|
||||
private final ExecutorService executor = Executors
|
||||
.newSingleThreadExecutor();
|
||||
|
||||
public DefaultActionHandler(PlayGameScreen screen, boolean userInitiated) {
|
||||
this.screen = screen;
|
||||
this.userInitiated = userInitiated;
|
||||
}
|
||||
|
||||
public PlayGameScreen getScreen() {
|
||||
return screen;
|
||||
}
|
||||
|
||||
protected IFacade getFacade() {
|
||||
return screen.getFacade();
|
||||
}
|
||||
|
||||
protected GameState getGameState() {
|
||||
return screen.getGameState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean turn(Worm worm, double angle) {
|
||||
return executeCommand(new Turn(getFacade(), worm, angle, getScreen()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean move(Worm worm) {
|
||||
return executeCommand(new Move(getFacade(), worm, getScreen()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean jump(Worm worm) {
|
||||
return executeCommand(new Jump(getFacade(), worm, getScreen()));
|
||||
}
|
||||
|
||||
private boolean executeCommand(final Command cmd) {
|
||||
if (userInitiated) {
|
||||
executor.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
getGameState().executeImmediately(cmd);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
boolean result = getGameState().executeImmediately(cmd);
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void print(String message) {
|
||||
getScreen().addMessage(message, MessageType.INFO);
|
||||
}
|
||||
|
||||
public void changeName(Worm worm, String newName) {
|
||||
executeCommand(new Rename(getFacade(), worm, newName, 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()));
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package worms.internal.gui.game;
|
||||
|
||||
import worms.model.Worm;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An action handler executes the actions of a worm as if they were commanded by
|
||||
* a user by pressing the corresponding keys.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* An action, when executed through an action handler,
|
||||
* <ul>
|
||||
* <li>shows periodic updates on the GUI (such as jump animations)
|
||||
* <li>eventually calls the corresponding facade methods, exactly like what
|
||||
* happens with a human player
|
||||
* <li>returns true if the action has been completed successfully; false
|
||||
* otherwise
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* Execution is blocked until the action has been entirely performed.
|
||||
* </p>
|
||||
*/
|
||||
public interface IActionHandler {
|
||||
|
||||
public boolean turn(Worm worm, double angle);
|
||||
|
||||
public boolean move(Worm worm);
|
||||
|
||||
public boolean jump(Worm worm);
|
||||
|
||||
/**
|
||||
* Print a message on the screen for a short amount of time.
|
||||
*
|
||||
* This is a utility method, and not an action. You are not required to use
|
||||
* this method for printing messages; you should only use it if you want the
|
||||
* given message to appear on the screen while playing.
|
||||
*
|
||||
* The method may return before the message has been removed from the screen
|
||||
* again.
|
||||
*/
|
||||
public void print(String message);
|
||||
}
|
@@ -0,0 +1,140 @@
|
||||
package worms.internal.gui.game;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import worms.internal.gui.GUIUtils;
|
||||
|
||||
public abstract class ImageSprite<T> extends Sprite<T> {
|
||||
|
||||
// original image, at original scale
|
||||
private final BufferedImage originalImage;
|
||||
|
||||
// only created when scale != 1.0
|
||||
private BufferedImage scaledImage;
|
||||
// only create when necessary
|
||||
private BufferedImage scaledImageHflipped;
|
||||
|
||||
private boolean hflipped = false;
|
||||
|
||||
private double scale;
|
||||
|
||||
protected ImageSprite(PlayGameScreen screen, String filename) {
|
||||
super(screen);
|
||||
this.scale = 1.0;
|
||||
this.originalImage = loadImage(filename);
|
||||
this.scaledImage = originalImage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getWidth(Graphics2D g) {
|
||||
return getImageWidth() * scale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHeight(Graphics2D g) {
|
||||
return getImageHeight() * scale;
|
||||
}
|
||||
|
||||
public int getImageWidth() {
|
||||
return originalImage.getWidth();
|
||||
}
|
||||
|
||||
public int getImageHeight() {
|
||||
return originalImage.getHeight();
|
||||
}
|
||||
|
||||
public void setScale(double newScale) {
|
||||
if (newScale == this.scale) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.scale = newScale;
|
||||
if (newScale != 1.0) {
|
||||
this.scaledImage = toBufferedImage(originalImage.getScaledInstance(
|
||||
(int) (newScale * originalImage.getWidth()),
|
||||
(int) (newScale * originalImage.getHeight()),
|
||||
Image.SCALE_SMOOTH));
|
||||
} else {
|
||||
this.scaledImage = originalImage;
|
||||
}
|
||||
|
||||
if (isHflipped()) {
|
||||
this.scaledImageHflipped = hflip(this.scaledImage);
|
||||
} else {
|
||||
this.scaledImageHflipped = null;
|
||||
}
|
||||
}
|
||||
|
||||
public double getScale() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
protected Image getImageToDraw() {
|
||||
Image imageToDraw = scaledImage;
|
||||
if (isHflipped()) {
|
||||
if (scaledImageHflipped == null) {
|
||||
scaledImageHflipped = hflip(scaledImage);
|
||||
}
|
||||
imageToDraw = scaledImageHflipped;
|
||||
}
|
||||
return imageToDraw;
|
||||
}
|
||||
|
||||
protected BufferedImage loadImage(String filename) {
|
||||
try {
|
||||
InputStream inputStream = GUIUtils.openResource(filename);
|
||||
BufferedImage result = ImageIO.read(inputStream);
|
||||
inputStream.close();
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Could not read file '" + filename + "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setHflipped(boolean value) {
|
||||
hflipped = value;
|
||||
}
|
||||
|
||||
public boolean isHflipped() {
|
||||
return hflipped;
|
||||
}
|
||||
|
||||
protected static BufferedImage hflip(BufferedImage image) {
|
||||
BufferedImage flippedImage = new BufferedImage(image.getWidth(),
|
||||
image.getHeight(), image.getType());
|
||||
Graphics2D flippedGraphics = flippedImage.createGraphics();
|
||||
flippedGraphics.scale(-1, 1);
|
||||
flippedGraphics.drawImage(image, -image.getWidth(null), 0, null);
|
||||
flippedGraphics.dispose();
|
||||
return flippedImage;
|
||||
}
|
||||
|
||||
protected static BufferedImage toBufferedImage(Image img) {
|
||||
if (img instanceof BufferedImage) {
|
||||
return (BufferedImage) img;
|
||||
}
|
||||
|
||||
BufferedImage result = new BufferedImage(img.getWidth(null),
|
||||
img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
Graphics2D resultGraphics = result.createGraphics();
|
||||
resultGraphics.drawImage(img, 0, 0, null);
|
||||
resultGraphics.dispose();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g) {
|
||||
int x = (int) (getCenterX() - getWidth(g) / 2);
|
||||
int y = (int) (getCenterY() - getHeight(g) / 2);
|
||||
g.drawImage(getImageToDraw(), x, y, null);
|
||||
}
|
||||
}
|
@@ -0,0 +1,332 @@
|
||||
package worms.internal.gui.game;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
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 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.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.sprites.WormSprite;
|
||||
import worms.model.Worm;
|
||||
|
||||
public class PlayGameScreen extends Screen {
|
||||
|
||||
final PlayGameScreenPainter painter;
|
||||
private final GameState gameState;
|
||||
|
||||
private final Set<Sprite<?>> sprites = new HashSet<Sprite<?>>();
|
||||
private final DefaultActionHandler userActionHandler;
|
||||
private final IActionHandler programActionHandler;
|
||||
|
||||
public PlayGameScreen(WormsGUI gui, GameState state) {
|
||||
super(gui);
|
||||
this.gameState = state;
|
||||
this.painter = createPainter();
|
||||
this.userActionHandler = createUserActionHandler();
|
||||
this.programActionHandler = createProgramActionHandler();
|
||||
}
|
||||
|
||||
protected DefaultActionHandler createUserActionHandler() {
|
||||
return new DefaultActionHandler(this, true);
|
||||
}
|
||||
|
||||
protected IActionHandler createProgramActionHandler() {
|
||||
return new DefaultActionHandler(this, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputMode<PlayGameScreen> createDefaultInputMode() {
|
||||
return new DefaultInputMode(this, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void screenStarted() {
|
||||
runGameLoop();
|
||||
userActionHandler.startGame();
|
||||
}
|
||||
|
||||
final AtomicLong lastUpdateTimestamp = new AtomicLong();
|
||||
|
||||
final TimerTask gameLoop = new TimerTask() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
long now = System.currentTimeMillis();
|
||||
long delta = now - lastUpdateTimestamp.getAndSet(now);
|
||||
double dt = delta / 1000.0 * GUIConstants.TIME_SCALE;
|
||||
gameState.evolve(dt);
|
||||
repaint();
|
||||
}
|
||||
};
|
||||
|
||||
private void runGameLoop() {
|
||||
Timer timer = new Timer();
|
||||
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
gameLoop.cancel();
|
||||
e.printStackTrace();
|
||||
getGUI().showError(
|
||||
e.getClass().getName() + ": " + e.getMessage());
|
||||
}
|
||||
});
|
||||
lastUpdateTimestamp.set(System.currentTimeMillis());
|
||||
timer.scheduleAtFixedRate(gameLoop, 0, 1000 / GUIConstants.FRAMERATE);
|
||||
}
|
||||
|
||||
public synchronized void update() {
|
||||
addNewSprites();
|
||||
for (Sprite<?> sprite : sprites) {
|
||||
sprite.update();
|
||||
}
|
||||
}
|
||||
|
||||
protected void addNewSprites() {
|
||||
addNewWormSprites();
|
||||
}
|
||||
|
||||
private void addNewWormSprites() {
|
||||
Collection<Worm> worms = getGameState().getWorms();
|
||||
if (worms != null) {
|
||||
for (Worm worm : worms) {
|
||||
WormSprite sprite = getWormSprite(worm);
|
||||
if (sprite == null) {
|
||||
createWormSprite(worm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createWormSprite(Worm worm) {
|
||||
WormSprite sprite = new WormSprite(this, worm);
|
||||
addSprite(sprite);
|
||||
}
|
||||
|
||||
public GameState getGameState() {
|
||||
return gameState;
|
||||
}
|
||||
|
||||
public IFacade getFacade() {
|
||||
return getGameState().getFacade();
|
||||
}
|
||||
|
||||
protected PlayGameScreenPainter createPainter() {
|
||||
return new PlayGameScreenPainter(this);
|
||||
}
|
||||
|
||||
public <T extends Sprite<?>> Set<T> getSpritesOfType(Class<T> type) {
|
||||
Set<T> result = new HashSet<T>();
|
||||
for (Sprite<?> sprite : sprites) {
|
||||
if (type.isInstance(sprite)) {
|
||||
result.add(type.cast(sprite));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public <ObjectType, SpriteType extends Sprite<ObjectType>> SpriteType getSpriteOfTypeFor(
|
||||
Class<SpriteType> type, ObjectType object) {
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
for (SpriteType sprite : getSpritesOfType(type)) {
|
||||
if (object.equals(sprite.getObject())) {
|
||||
return sprite;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public WormSprite getWormSprite(Worm worm) {
|
||||
return getSpriteOfTypeFor(WormSprite.class, worm);
|
||||
}
|
||||
|
||||
public void move() {
|
||||
Worm worm = getSelectedWorm();
|
||||
|
||||
if (worm != null) {
|
||||
userActionHandler.move(worm);
|
||||
}
|
||||
}
|
||||
|
||||
public void jump() {
|
||||
Worm worm = getSelectedWorm();
|
||||
if (worm != null) {
|
||||
userActionHandler.jump(worm);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void turn(double angle) {
|
||||
Worm worm = getSelectedWorm();
|
||||
angle = GUIUtils.restrictAngle(angle, -Math.PI);
|
||||
|
||||
if (worm != null) {
|
||||
userActionHandler.turn(worm, angle);
|
||||
}
|
||||
}
|
||||
|
||||
public void changeName(String newName) {
|
||||
Worm worm = getSelectedWorm();
|
||||
|
||||
if (worm != null) {
|
||||
userActionHandler.changeName(worm, newName);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized Worm getSelectedWorm() {
|
||||
return getGameState().getSelectedWorm();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintScreen(Graphics2D g) {
|
||||
painter.paint(g);
|
||||
}
|
||||
|
||||
public static PlayGameScreen create(WormsGUI gui, GameState gameState,
|
||||
boolean debugMode) {
|
||||
if (!debugMode) {
|
||||
return new PlayGameScreen(gui, gameState);
|
||||
} else {
|
||||
return new PlayGameScreen(gui, gameState) {
|
||||
@Override
|
||||
protected PlayGameScreenPainter createPainter() {
|
||||
return new PlayGameScreenDebugPainter(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public void addSprite(Sprite<?> sprite) {
|
||||
sprites.add(sprite);
|
||||
}
|
||||
|
||||
public void removeSprite(Sprite<?> sprite) {
|
||||
sprites.remove(sprite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale of the displayed world (in worm-meter per pixel)
|
||||
*/
|
||||
private double getDisplayScale() {
|
||||
return GUIConstants.DISPLAY_SCALE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Distance in the world (worm-meter) to distance on the screen (pixels)
|
||||
*/
|
||||
public double worldToScreenDistance(double ds) {
|
||||
return ds / getDisplayScale();
|
||||
}
|
||||
|
||||
/**
|
||||
* Distance on the screen (pixels) to distance in the world (worm-meter)
|
||||
*/
|
||||
public double screenToWorldDistance(double ds) {
|
||||
return ds * getDisplayScale();
|
||||
}
|
||||
|
||||
/**
|
||||
* World x coordinate to screen x coordinate
|
||||
*/
|
||||
public double getScreenX(double x) {
|
||||
return getScreenWidth()/2.0 + worldToScreenDistance(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen x coordinate to world x coordinate
|
||||
*/
|
||||
public double getLogicalX(double screenX) {
|
||||
return screenToWorldDistance(screenX - getScreenWidth()/2.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* World y coordinate to screen y coordinate
|
||||
*/
|
||||
public double getScreenY(double y) {
|
||||
return getScreenHeight()/2.0 - worldToScreenDistance(y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen y coordinate to world y coordinate
|
||||
*/
|
||||
public double getLogicalY(double screenY) {
|
||||
return screenToWorldDistance(getScreenHeight()/2.0 - screenY);
|
||||
}
|
||||
|
||||
public void paintTextEntry(Graphics2D g, String message, String enteredName) {
|
||||
painter.paintTextEntry(g, message, enteredName);
|
||||
}
|
||||
|
||||
public void drawTurnAngleIndicator(Graphics2D g, WormSprite wormSprite,
|
||||
double currentAngle) {
|
||||
painter.drawTurnAngleIndicator(g, wormSprite, currentAngle);
|
||||
}
|
||||
|
||||
public <T, S extends Sprite<T>> void removeSpriteFor(Class<S> type, T object) {
|
||||
S sprite = getSpriteOfTypeFor(type, object);
|
||||
sprites.remove(sprite);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public InputMode<PlayGameScreen> getCurrentInputMode() {
|
||||
return (InputMode<PlayGameScreen>) super.getCurrentInputMode();
|
||||
}
|
||||
|
||||
public void gameStarted() {
|
||||
switchInputMode(new DefaultInputMode(this, getCurrentInputMode()));
|
||||
}
|
||||
|
||||
public void renameWorm() {
|
||||
switchInputMode(new EnteringNameMode("Enter new name for worm: ", this,
|
||||
getCurrentInputMode(), new EnteringNameMode.Callback() {
|
||||
@Override
|
||||
public void onNameEntered(String newName) {
|
||||
changeName(newName);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public void showInstructions(Graphics2D g, String string) {
|
||||
painter.paintInstructions(g, string);
|
||||
}
|
||||
|
||||
public WormSprite getSelectedWormSprite() {
|
||||
return getWormSprite(getSelectedWorm());
|
||||
}
|
||||
|
||||
public void selectNextWorm() {
|
||||
getGameState().selectNextWorm();
|
||||
}
|
||||
|
||||
public IActionHandler getProgramActionHandler() {
|
||||
return programActionHandler;
|
||||
}
|
||||
|
||||
public void selectWorm(Worm worm) {
|
||||
while (getSelectedWorm() != worm) {
|
||||
selectNextWorm();
|
||||
}
|
||||
}
|
||||
|
||||
public void resizeWorm(int sign) {
|
||||
Worm worm = getSelectedWorm();
|
||||
|
||||
if (worm != null) {
|
||||
userActionHandler.resizeWorm(worm, sign);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
package worms.internal.gui.game;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Shape;
|
||||
|
||||
import worms.internal.gui.GUIUtils;
|
||||
import worms.internal.gui.game.sprites.WormSprite;
|
||||
|
||||
public class PlayGameScreenDebugPainter extends PlayGameScreenPainter {
|
||||
|
||||
private static final int LOCATION_MARKER_SIZE = 4;
|
||||
|
||||
public PlayGameScreenDebugPainter(PlayGameScreen screen) {
|
||||
super(screen);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintWorm(WormSprite sprite) {
|
||||
|
||||
drawName(sprite);
|
||||
|
||||
drawActionBar(sprite);
|
||||
|
||||
drawOutline(sprite);
|
||||
drawJumpMarkers(sprite); // also draw for other worms
|
||||
|
||||
drawDirectionLine(sprite);
|
||||
|
||||
drawLocationMarker(sprite);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintLevel() {
|
||||
drawCrossMarker(getScreenX(0), getScreenY(0), 10, Color.BLUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawJumpMarkers(WormSprite sprite) {
|
||||
|
||||
double[][] xys = sprite.getJumpSteps();
|
||||
if (xys != null) {
|
||||
double[] prevXY = xys[0];
|
||||
for (int i = 1; i < xys.length; i++) {
|
||||
double[] xy = xys[i];
|
||||
if (xy != null && prevXY != null) {
|
||||
double jumpX = getScreenX(xy[0]);
|
||||
double jumpY = getScreenY(xy[1]);
|
||||
currentGraphics.setColor(JUMP_MARKER_COLOR);
|
||||
currentGraphics.drawLine((int) getScreenX(prevXY[0]),
|
||||
(int) getScreenY(prevXY[1]), (int) jumpX,
|
||||
(int) jumpY);
|
||||
prevXY = xy;
|
||||
drawCrossMarker(jumpX, jumpY, JUMP_MARKER_SIZE,
|
||||
JUMP_MARKER_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a marker at the current location of the worm (which is not
|
||||
* necessarily equal to the sprite's location)
|
||||
*/
|
||||
protected void drawLocationMarker(WormSprite worm) {
|
||||
double x = worm.getActualX();
|
||||
double y = worm.getActualY();
|
||||
|
||||
drawCrossMarker(getScreenX(x), getScreenY(y), LOCATION_MARKER_SIZE,
|
||||
Color.YELLOW);
|
||||
}
|
||||
|
||||
protected void drawOutline(WormSprite sprite) {
|
||||
double r = sprite.getRadius();
|
||||
double x = sprite.getCenterX();
|
||||
double y = sprite.getCenterY();
|
||||
|
||||
currentGraphics.setColor(Color.YELLOW);
|
||||
Shape circle = GUIUtils.circleAt(x, y, getScreen()
|
||||
.worldToScreenDistance(r));
|
||||
currentGraphics.draw(circle);
|
||||
|
||||
}
|
||||
|
||||
protected void drawDirectionLine(WormSprite sprite) {
|
||||
double x = sprite.getCenterX();
|
||||
double y = sprite.getCenterY();
|
||||
double dist = sprite.getHeight(currentGraphics) / 2.0;
|
||||
double direction = sprite.getOrientation();
|
||||
|
||||
currentGraphics.setColor(Color.YELLOW);
|
||||
currentGraphics.drawLine((int) x, (int) y,
|
||||
(int) (x + dist * Math.cos(direction)),
|
||||
(int) (y - dist * Math.sin(direction)));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,260 @@
|
||||
package worms.internal.gui.game;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import worms.internal.gui.AbstractPainter;
|
||||
import worms.internal.gui.GUIUtils;
|
||||
import worms.internal.gui.GameState;
|
||||
import worms.internal.gui.game.sprites.WormSprite;
|
||||
|
||||
public class PlayGameScreenPainter extends AbstractPainter<PlayGameScreen> {
|
||||
|
||||
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 double ACTION_BAR_WIDTH = 30;
|
||||
protected static final double ACTION_BAR_HEIGHT = 5;
|
||||
|
||||
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_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_TEXT_COLOR = Color.WHITE;
|
||||
protected static final Color JUMP_MARKER_COLOR = Color.GRAY;
|
||||
|
||||
protected static final int JUMP_MARKER_SIZE = 1;
|
||||
protected static final double DIRECTION_INDICATOR_SIZE = 10;
|
||||
|
||||
protected Graphics2D currentGraphics;
|
||||
|
||||
public PlayGameScreenPainter(PlayGameScreen screen) {
|
||||
super(screen);
|
||||
}
|
||||
|
||||
protected GameState getState() {
|
||||
return getScreen().getGameState();
|
||||
}
|
||||
|
||||
public void paint(Graphics2D g) {
|
||||
this.currentGraphics = g;
|
||||
|
||||
paintLevel();
|
||||
|
||||
for (WormSprite sprite : getScreen().getSpritesOfType(WormSprite.class)) {
|
||||
if (sprite.getWorm() == getScreen().getSelectedWorm()) {
|
||||
drawSelection(sprite);
|
||||
}
|
||||
paintWorm(sprite);
|
||||
}
|
||||
|
||||
this.currentGraphics = null;
|
||||
}
|
||||
|
||||
protected void paintLevel() {
|
||||
|
||||
}
|
||||
|
||||
protected double getScreenX(double x) {
|
||||
return getScreen().getScreenX(x);
|
||||
}
|
||||
|
||||
protected double getScreenY(double y) {
|
||||
return getScreen().getScreenY(y);
|
||||
}
|
||||
|
||||
protected void paintWorm(WormSprite sprite) {
|
||||
|
||||
sprite.draw(currentGraphics);
|
||||
|
||||
drawName(sprite);
|
||||
|
||||
drawActionBar(sprite);
|
||||
|
||||
if (getScreen().getSelectedWorm() == sprite.getWorm()) {
|
||||
drawDirectionIndicator(sprite);
|
||||
drawJumpMarkers(sprite);
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawName(WormSprite sprite) {
|
||||
final double voffset = sprite.getHeight(currentGraphics) / 2;
|
||||
String name = sprite.getName();
|
||||
|
||||
if (name == null) {
|
||||
name = "(null)";
|
||||
}
|
||||
|
||||
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);
|
||||
currentGraphics.setColor(NAME_BAR_BACKGROUND);
|
||||
currentGraphics.fill(nameBarFill);
|
||||
|
||||
currentGraphics.setColor(NAME_BAR_TEXT);
|
||||
|
||||
currentGraphics.drawString(name, (float) x, (float) (y));
|
||||
}
|
||||
|
||||
protected void drawActionBar(WormSprite sprite) {
|
||||
double x = sprite.getCenterX();
|
||||
double y = sprite.getCenterY();
|
||||
double spriteHeight = sprite.getHeight(currentGraphics);
|
||||
|
||||
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);
|
||||
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);
|
||||
currentGraphics.setColor(BAR_OUTLINE_COLOR);
|
||||
currentGraphics.draw(actionBar);
|
||||
}
|
||||
|
||||
protected void drawSelection(WormSprite sprite) {
|
||||
double x = sprite.getCenterX();
|
||||
double y = sprite.getCenterY();
|
||||
double spriteHeight = Math.max(sprite.getWidth(currentGraphics), sprite.getHeight(currentGraphics));
|
||||
|
||||
currentGraphics.setColor(SELECTION_FILL_COLOR);
|
||||
|
||||
Shape circle = GUIUtils.circleAt(x, y, spriteHeight / 2);
|
||||
currentGraphics.fill(circle);
|
||||
}
|
||||
|
||||
protected void drawDirectionIndicator(WormSprite sprite) {
|
||||
double x = sprite.getCenterX();
|
||||
double y = sprite.getCenterY();
|
||||
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);
|
||||
currentGraphics.fill(directionIndicator);
|
||||
}
|
||||
|
||||
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;
|
||||
distance += DIRECTION_INDICATOR_SIZE / 2;
|
||||
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); }
|
||||
*/
|
||||
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);
|
||||
graphics.fill(directionIndicator);
|
||||
}
|
||||
|
||||
protected void drawJumpMarkers(WormSprite sprite) {
|
||||
double[][] xys = sprite.getJumpSteps();
|
||||
if (xys != null) {
|
||||
for (double[] xy : xys) {
|
||||
if (xy != null) {
|
||||
double jumpX = getScreenX(xy[0]);
|
||||
double jumpY = getScreenY(xy[1]);
|
||||
drawCrossMarker(jumpX, jumpY, JUMP_MARKER_SIZE, JUMP_MARKER_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void paintTextEntry(Graphics2D g, String message, String enteredText) {
|
||||
g.setColor(RENAME_BACKGROUND_COLOR);
|
||||
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);
|
||||
}
|
||||
|
||||
public void paintInstructions(Graphics2D g, String message) {
|
||||
int lineHeight = 25;
|
||||
Font oldFont = g.getFont();
|
||||
g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 2 * lineHeight / 3));
|
||||
|
||||
StringTokenizer tok = new StringTokenizer(message, "\n");
|
||||
int nbLines = tok.countTokens();
|
||||
List<String> lines = new ArrayList<String>(nbLines);
|
||||
while (tok.hasMoreTokens()) {
|
||||
lines.add(tok.nextToken());
|
||||
}
|
||||
|
||||
int maxWidth = 0;
|
||||
for (String line : lines) {
|
||||
Rectangle2D bounds = g.getFontMetrics().getStringBounds(line, g);
|
||||
maxWidth = Math.max(maxWidth, (int) (bounds.getWidth() + 0.5));
|
||||
}
|
||||
|
||||
int width = 2 * lineHeight + maxWidth;
|
||||
int height = 2 * lineHeight + lineHeight * nbLines;
|
||||
int top = 0;
|
||||
int left = 0;
|
||||
|
||||
g.setColor(new Color(0xa0565656, true));
|
||||
g.fillRect(left, top, width, height);
|
||||
g.setColor(Color.WHITE);
|
||||
|
||||
int y = top + 2 * lineHeight;
|
||||
for (String line : lines) {
|
||||
g.drawString(line, left + lineHeight, y);
|
||||
y += lineHeight;
|
||||
}
|
||||
|
||||
g.setFont(oldFont);
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
package worms.internal.gui.game;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
import worms.facade.IFacade;
|
||||
|
||||
public abstract class Sprite<T> {
|
||||
|
||||
private double x;
|
||||
private double y;
|
||||
private final PlayGameScreen screen;
|
||||
|
||||
protected Sprite(PlayGameScreen screen) {
|
||||
this.screen = screen;
|
||||
}
|
||||
|
||||
public PlayGameScreen getScreen() {
|
||||
return screen;
|
||||
}
|
||||
|
||||
public abstract T getObject();
|
||||
|
||||
protected IFacade getFacade() {
|
||||
return getScreen().getFacade();
|
||||
}
|
||||
|
||||
public abstract void draw(Graphics2D g);
|
||||
|
||||
/**
|
||||
* Height (in pixels) of this sprite, when drawn to the given graphics object
|
||||
* @return
|
||||
*/
|
||||
public abstract double getHeight(Graphics2D g);
|
||||
|
||||
/**
|
||||
* Width (in pixels) of this sprite, when drawn to the given graphics object
|
||||
* @return
|
||||
*/
|
||||
public abstract double getWidth(Graphics2D g);
|
||||
|
||||
public synchronized double[] getCenterLocation() {
|
||||
return new double[] { getCenterX(), getCenterY() };
|
||||
}
|
||||
|
||||
public synchronized void setCenterLocation(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public synchronized double getCenterX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public synchronized double getCenterY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update attributes of this sprite with values from the object
|
||||
*/
|
||||
public synchronized void update() {
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,141 @@
|
||||
package worms.internal.gui.game.commands;
|
||||
|
||||
import worms.facade.IFacade;
|
||||
import worms.internal.gui.game.PlayGameScreen;
|
||||
|
||||
public abstract class Command {
|
||||
|
||||
private final IFacade facade;
|
||||
private final PlayGameScreen screen;
|
||||
|
||||
private double elapsedTime;
|
||||
private boolean cancelled = false;
|
||||
private boolean completed = false;
|
||||
private boolean started = false;
|
||||
|
||||
protected Command(IFacade facade, PlayGameScreen screen) {
|
||||
this.facade = facade;
|
||||
this.screen = screen;
|
||||
}
|
||||
|
||||
public PlayGameScreen getScreen() {
|
||||
return screen;
|
||||
}
|
||||
|
||||
protected IFacade getFacade() {
|
||||
return facade;
|
||||
}
|
||||
|
||||
public final void startExecution() {
|
||||
if (canStart()) {
|
||||
started = true;
|
||||
doStartExecution();
|
||||
afterExecutionStarted();
|
||||
} else {
|
||||
cancelExecution();
|
||||
}
|
||||
}
|
||||
|
||||
protected final void cancelExecution() {
|
||||
cancelled = true;
|
||||
afterExecutionCancelled();
|
||||
}
|
||||
|
||||
protected final void completeExecution() {
|
||||
completed = true;
|
||||
afterExecutionCompleted();
|
||||
}
|
||||
|
||||
public final void update(double dt) {
|
||||
if (!isTerminated()) {
|
||||
elapsedTime += dt;
|
||||
doUpdate(dt);
|
||||
if (isTerminated()) {
|
||||
getScreen().update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total time that has elapsed while executing this command
|
||||
*/
|
||||
public double getElapsedTime() {
|
||||
return elapsedTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this command has been started
|
||||
*/
|
||||
public boolean hasBeenStarted() {
|
||||
return started;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the execution of the command has been cancelled.
|
||||
*/
|
||||
public final boolean isExecutionCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the execution of the command has been completed
|
||||
* successfully.
|
||||
*/
|
||||
public final boolean isExecutionCompleted() {
|
||||
return completed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the execution of the command can start
|
||||
*/
|
||||
protected abstract boolean canStart();
|
||||
|
||||
/**
|
||||
* Start executing the command
|
||||
*/
|
||||
protected abstract void doStartExecution();
|
||||
|
||||
/**
|
||||
* Called when the execution of the command has been completed successfully.
|
||||
*/
|
||||
protected void afterExecutionCompleted() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the execution of the command has been cancelled.
|
||||
*/
|
||||
protected void afterExecutionCancelled() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the execution of the command has been started.
|
||||
*/
|
||||
protected void afterExecutionStarted() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the execution of the command by the given time interval
|
||||
*
|
||||
* @param dt
|
||||
*/
|
||||
protected abstract void doUpdate(double dt);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName()
|
||||
+ " ("
|
||||
+ (hasBeenStarted() ? "elapsed: "
|
||||
+ String.format("%.2f", getElapsedTime()) + "s)"
|
||||
: "queued)");
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package worms.internal.gui.game.commands;
|
||||
|
||||
import worms.facade.IFacade;
|
||||
import worms.internal.gui.game.PlayGameScreen;
|
||||
|
||||
public abstract class InstantaneousCommand extends Command {
|
||||
protected InstantaneousCommand(IFacade facade, PlayGameScreen screen) {
|
||||
super(facade, screen);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecutionStarted() {
|
||||
completeExecution();
|
||||
getScreen().update();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void doUpdate(double dt) {
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
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 Jump extends Command {
|
||||
private boolean hasJumped;
|
||||
private final Worm worm;
|
||||
private double jumpDuration;
|
||||
|
||||
public Jump(IFacade facade, Worm worm, PlayGameScreen screen) {
|
||||
super(facade, screen);
|
||||
this.worm = worm;
|
||||
}
|
||||
|
||||
public Worm getWorm() {
|
||||
return worm;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean canStart() {
|
||||
return getWorm() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStartExecution() {
|
||||
try {
|
||||
this.jumpDuration = getFacade().getJumpTime(worm);
|
||||
} catch (ModelException e) {
|
||||
cancelExecution();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecutionCancelled() {
|
||||
WormSprite sprite = getScreen().getWormSprite(getWorm());
|
||||
if (sprite != null) {
|
||||
sprite.setIsJumping(false);
|
||||
}
|
||||
getScreen().addMessage("This worm cannot jump :(", MessageType.ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecutionCompleted() {
|
||||
WormSprite sprite = getScreen().getWormSprite(getWorm());
|
||||
if (sprite != null) {
|
||||
sprite.setIsJumping(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doUpdate(double dt) {
|
||||
WormSprite sprite = getScreen().getWormSprite(getWorm());
|
||||
if (sprite != null) {
|
||||
try {
|
||||
sprite.setIsJumping(true);
|
||||
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));
|
||||
completeExecution();
|
||||
}
|
||||
} else {
|
||||
double[] xy = getFacade().getJumpStep(getWorm(),
|
||||
getElapsedTime());
|
||||
sprite.setCenterLocation(getScreen().getScreenX(xy[0]),
|
||||
getScreen().getScreenY(xy[1]));
|
||||
}
|
||||
} catch (ModelException e) {
|
||||
e.printStackTrace();
|
||||
cancelExecution();
|
||||
}
|
||||
} else {
|
||||
cancelExecution();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
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;
|
||||
import worms.model.Worm;
|
||||
import worms.util.ModelException;
|
||||
|
||||
public class Move extends Command {
|
||||
|
||||
private double startX;
|
||||
private double startY;
|
||||
|
||||
private double finalX;
|
||||
private double finalY;
|
||||
|
||||
private final Worm worm;
|
||||
|
||||
public Move(IFacade facade, Worm worm, PlayGameScreen screen) {
|
||||
super(facade, screen);
|
||||
this.worm = worm;
|
||||
}
|
||||
|
||||
public Worm getWorm() {
|
||||
return worm;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canStart() {
|
||||
return getWorm() != null;
|
||||
}
|
||||
|
||||
private double getDuration() {
|
||||
return GUIConstants.MOVE_DURATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doUpdate(double dt) {
|
||||
WormSprite sprite = getScreen().getWormSprite(getWorm());
|
||||
if (sprite != null) {
|
||||
sprite.setIsMoving(true);
|
||||
if (getElapsedTime() < getDuration()) {
|
||||
double t = getElapsedTime() / getDuration();
|
||||
t = t * t * (3 - 2 * t); // smooth-step interpolation
|
||||
double x = (1.0 - t) * startX + t * finalX;
|
||||
double y = (1.0 - t) * startY + t * finalY;
|
||||
sprite.setCenterLocation(x, y);
|
||||
} else {
|
||||
completeExecution();
|
||||
}
|
||||
} else {
|
||||
cancelExecution();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecutionCompleted() {
|
||||
WormSprite sprite = getScreen().getWormSprite(getWorm());
|
||||
if (sprite != null) {
|
||||
sprite.setIsMoving(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecutionCancelled() {
|
||||
WormSprite sprite = getScreen().getWormSprite(getWorm());
|
||||
if (sprite != null) {
|
||||
sprite.setIsMoving(false);
|
||||
}
|
||||
getScreen().addMessage("This worm cannot move like that :(",
|
||||
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());
|
||||
} catch (ModelException e) {
|
||||
e.printStackTrace();
|
||||
cancelExecution();
|
||||
}
|
||||
}
|
||||
|
||||
protected double getObjectX() {
|
||||
return getFacade().getX(getWorm());
|
||||
}
|
||||
|
||||
protected double getObjectY() {
|
||||
return getFacade().getY(getWorm());
|
||||
}
|
||||
}
|
@@ -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.model.Worm;
|
||||
import worms.util.ModelException;
|
||||
|
||||
public class Rename extends InstantaneousCommand {
|
||||
private final String newName;
|
||||
private final Worm worm;
|
||||
|
||||
public Rename(IFacade facade, Worm worm, String newName,
|
||||
PlayGameScreen screen) {
|
||||
super(facade, screen);
|
||||
this.worm = worm;
|
||||
this.newName = newName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canStart() {
|
||||
return worm != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStartExecution() {
|
||||
try {
|
||||
getFacade().rename(worm, newName);
|
||||
} catch (ModelException e) {
|
||||
// an invalid name
|
||||
getScreen().addMessage("Invalid name: " + newName, MessageType.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
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();
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
package worms.internal.gui.game.commands;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import worms.facade.IFacade;
|
||||
import worms.internal.gui.game.PlayGameScreen;
|
||||
import worms.internal.gui.messages.MessageType;
|
||||
import worms.model.Worm;
|
||||
|
||||
public class StartGame extends InstantaneousCommand {
|
||||
|
||||
public StartGame(IFacade facade, PlayGameScreen screen) {
|
||||
super(facade, screen);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canStart() {
|
||||
Collection<Worm> worms = getScreen().getGameState().getWorms();
|
||||
return worms != null && !worms.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecutionCancelled() {
|
||||
getScreen().addMessage("Cannot start the game without worms", MessageType.ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStartExecution() {
|
||||
getScreen().gameStarted();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package worms.internal.gui.game.commands;
|
||||
|
||||
import worms.facade.IFacade;
|
||||
import worms.internal.gui.GUIUtils;
|
||||
import worms.internal.gui.game.PlayGameScreen;
|
||||
import worms.internal.gui.messages.MessageType;
|
||||
import worms.model.Worm;
|
||||
|
||||
public class Turn extends InstantaneousCommand {
|
||||
private final Worm worm;
|
||||
private final double angle;
|
||||
|
||||
public Turn(IFacade facade, Worm worm, double angle, PlayGameScreen screen) {
|
||||
super(facade, screen);
|
||||
this.worm = worm;
|
||||
this.angle = angle;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canStart() {
|
||||
return worm != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecutionCancelled() {
|
||||
getScreen().addMessage("This worm cannot perform that turn :(",
|
||||
MessageType.ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStartExecution() {
|
||||
double direction = getFacade().getOrientation(worm);
|
||||
double angleToTurn = GUIUtils.restrictDirection(direction + angle) - direction;
|
||||
getFacade().turn(worm, angleToTurn);
|
||||
}
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
package worms.internal.gui.game.modes;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import worms.internal.gui.InputMode;
|
||||
import worms.internal.gui.game.PlayGameScreen;
|
||||
import worms.internal.gui.game.sprites.WormSprite;
|
||||
import worms.model.Worm;
|
||||
|
||||
public class DefaultInputMode extends InputMode<PlayGameScreen> {
|
||||
|
||||
/**
|
||||
* @param playGameScreen
|
||||
*/
|
||||
public DefaultInputMode(PlayGameScreen playGameScreen,
|
||||
InputMode<PlayGameScreen> previous) {
|
||||
super(playGameScreen, previous);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (getScreen().getGUI().getOptions().enableClickToSelect) {
|
||||
Point point = e.getPoint();
|
||||
for (WormSprite sprite : getScreen().getSpritesOfType(
|
||||
WormSprite.class)) {
|
||||
Worm worm = sprite.getWorm();
|
||||
if (sprite.hitTest(point.getX(), point.getY())) {
|
||||
getScreen().selectWorm(worm);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
getScreen().switchInputMode(new TurningMode(getScreen(), this));
|
||||
getScreen().getCurrentInputMode().mouseDragged(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e) {
|
||||
switch (e.getKeyChar()) {
|
||||
case 'j':
|
||||
case 'J':
|
||||
getScreen().jump();
|
||||
break;
|
||||
case 'n':
|
||||
case 'N':
|
||||
getScreen().renameWorm();
|
||||
break;
|
||||
case '+':
|
||||
getScreen().resizeWorm(+1);
|
||||
break;
|
||||
case '-':
|
||||
getScreen().resizeWorm(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
switch (e.getKeyCode()) {
|
||||
case KeyEvent.VK_UP:
|
||||
getScreen().move();
|
||||
break;
|
||||
case KeyEvent.VK_ESCAPE:
|
||||
getScreen().getGUI().exit();
|
||||
break;
|
||||
case KeyEvent.VK_TAB:
|
||||
getScreen().selectNextWorm();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
switch (e.getKeyCode()) {
|
||||
case KeyEvent.VK_LEFT:
|
||||
case KeyEvent.VK_RIGHT:
|
||||
getScreen().switchInputMode(new TurningMode(getScreen(), this));
|
||||
getScreen().getCurrentInputMode().keyPressed(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
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 EnteringNameMode extends InputMode<PlayGameScreen> {
|
||||
|
||||
public static interface Callback {
|
||||
public void onNameEntered(String newName);
|
||||
}
|
||||
|
||||
private final String message;
|
||||
private final Callback callback;
|
||||
|
||||
/**
|
||||
* @param playGameScreen
|
||||
*/
|
||||
public EnteringNameMode(String message, PlayGameScreen playGameScreen, InputMode<PlayGameScreen> previous, Callback callback) {
|
||||
super(playGameScreen, previous);
|
||||
this.message = message;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
private String enteredName = "";
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
switch (e.getKeyCode()) {
|
||||
case KeyEvent.VK_ENTER:
|
||||
if (callback != null) {
|
||||
callback.onNameEntered(enteredName);
|
||||
}
|
||||
leaveInputMode();
|
||||
break;
|
||||
case KeyEvent.VK_ESCAPE:
|
||||
leaveInputMode();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e) {
|
||||
if (e.getKeyChar() == '\b') {
|
||||
enteredName = enteredName.substring(0,
|
||||
Math.max(0, enteredName.length() - 1));
|
||||
} else if (!Character.isISOControl(e.getKeyChar())
|
||||
&& e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) {
|
||||
enteredName += e.getKeyChar();
|
||||
}
|
||||
getScreen().repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintOverlay(Graphics2D g) {
|
||||
super.paintOverlay(g);
|
||||
getScreen().paintTextEntry(g, message, enteredName);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,116 @@
|
||||
package worms.internal.gui.game.modes;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import worms.internal.gui.GUIConstants;
|
||||
import worms.internal.gui.GUIUtils;
|
||||
import worms.internal.gui.InputMode;
|
||||
import worms.internal.gui.game.PlayGameScreen;
|
||||
import worms.internal.gui.game.sprites.WormSprite;
|
||||
|
||||
public class TurningMode extends InputMode<PlayGameScreen> {
|
||||
|
||||
public TurningMode(PlayGameScreen playGameScreen,
|
||||
InputMode<PlayGameScreen> previous) {
|
||||
super(playGameScreen, previous);
|
||||
}
|
||||
|
||||
private double angle = 0;
|
||||
|
||||
private long pressedSince = 0; // 0 if not turning
|
||||
private boolean clockwise;
|
||||
|
||||
private void startTurning(boolean clockwise) {
|
||||
if (!isTurning()) {
|
||||
pressedSince = System.currentTimeMillis();
|
||||
this.clockwise = clockwise;
|
||||
}
|
||||
}
|
||||
|
||||
private void stopTurning() {
|
||||
angle = getCurrentAngle();
|
||||
pressedSince = 0;
|
||||
}
|
||||
|
||||
private boolean isTurning() {
|
||||
return pressedSince != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
WormSprite sprite = getScreen().getSelectedWormSprite();
|
||||
if (sprite != null) {
|
||||
double[] wormXY = sprite.getCenterLocation();
|
||||
double currentOrientation = sprite.getOrientation();
|
||||
this.angle = Math.PI
|
||||
- currentOrientation
|
||||
+ Math.atan2((e.getY() - wormXY[1]), (wormXY[0] - e.getX()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
finishTurn();
|
||||
}
|
||||
|
||||
private void finishTurn() {
|
||||
if (angle != 0) {
|
||||
getScreen().turn(angle);
|
||||
leaveInputMode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
switch (e.getKeyCode()) {
|
||||
case KeyEvent.VK_RIGHT:
|
||||
startTurning(true);
|
||||
break;
|
||||
case KeyEvent.VK_LEFT:
|
||||
startTurning(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
switch (e.getKeyCode()) {
|
||||
case KeyEvent.VK_ESCAPE:
|
||||
leaveInputMode();
|
||||
break;
|
||||
case KeyEvent.VK_ENTER:
|
||||
finishTurn();
|
||||
break;
|
||||
case KeyEvent.VK_LEFT: // no-break
|
||||
case KeyEvent.VK_RIGHT:
|
||||
stopTurning();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private double getCurrentAngle() {
|
||||
double delta = 0;
|
||||
if (isTurning()) {
|
||||
long now = System.currentTimeMillis();
|
||||
delta = Math.max(GUIConstants.MIN_TURN_ANGLE, (now - pressedSince)
|
||||
/ 1000.0 * GUIConstants.ANGLE_TURNED_PER_SECOND);
|
||||
if (clockwise) {
|
||||
delta = -delta;
|
||||
}
|
||||
return GUIUtils.restrictAngle(angle + delta, -Math.PI);
|
||||
} else {
|
||||
return angle;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintOverlay(Graphics2D g) {
|
||||
super.paintOverlay(g);
|
||||
WormSprite sprite = getScreen().getSelectedWormSprite();
|
||||
if (sprite != null) {
|
||||
getScreen().drawTurnAngleIndicator(g, sprite, getCurrentAngle());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,168 @@
|
||||
package worms.internal.gui.game.sprites;
|
||||
|
||||
import worms.internal.gui.GUIUtils;
|
||||
import worms.internal.gui.game.ImageSprite;
|
||||
import worms.internal.gui.game.PlayGameScreen;
|
||||
import worms.model.Worm;
|
||||
import worms.util.ModelException;
|
||||
|
||||
public class WormSprite extends ImageSprite<Worm> {
|
||||
|
||||
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 long actionPoints;
|
||||
private long maxActionPoints;
|
||||
private double actualX;
|
||||
private double actualY;
|
||||
private double radius;
|
||||
|
||||
public WormSprite(PlayGameScreen screen, Worm worm) {
|
||||
super(screen, "images/worm.png");
|
||||
this.worm = worm;
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Worm getObject() {
|
||||
return getWorm();
|
||||
}
|
||||
|
||||
public Worm getWorm() {
|
||||
return worm;
|
||||
}
|
||||
|
||||
private void setDirection(double newDirection) {
|
||||
double direction = GUIUtils.restrictDirection(newDirection);
|
||||
this.orientation = direction;
|
||||
|
||||
if (Math.PI / 2 > direction || 3 * Math.PI / 2 < direction) {
|
||||
setHflipped(true);
|
||||
} else {
|
||||
setHflipped(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 = 0.8;
|
||||
|
||||
double scaleFactor = fitFactor * 2 * radius / imageHeightInMeters;
|
||||
|
||||
// limit scaling
|
||||
scaleFactor = Math.max(MIN_SCALE, Math.min(scaleFactor, MAX_SCALE));
|
||||
|
||||
setScale(scaleFactor);
|
||||
}
|
||||
|
||||
public boolean hitTest(double screenX, double screenY) {
|
||||
double radius = getScale() * Math.max(getImageWidth(), getImageHeight()) / 2.0;
|
||||
double dx = screenX - getCenterX();
|
||||
double dy = screenY - getCenterY();
|
||||
return dx * dx + dy * dy <= radius * radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void update() {
|
||||
if (isJumping || isMoving) {
|
||||
// don't update the location here, because it may differ from the
|
||||
// location in the model
|
||||
} else {
|
||||
setCenterLocation(getScreen().getScreenX(getFacade().getX(getWorm())),
|
||||
getScreen().getScreenY(getFacade().getY(worm)));
|
||||
}
|
||||
this.actualX = getFacade().getX(getWorm());
|
||||
this.actualY = getFacade().getY(getWorm());
|
||||
setRadius(getFacade().getRadius(getWorm()));
|
||||
setDirection(getFacade().getOrientation(getWorm()));
|
||||
updateJumpTime();
|
||||
setName(getFacade().getName(getWorm()));
|
||||
this.actionPoints = getFacade().getNbActionPoints(getWorm());
|
||||
this.maxActionPoints = getFacade().getMaxNbActionPoints(getWorm());
|
||||
}
|
||||
|
||||
public void setIsJumping(boolean isJumping) {
|
||||
this.isJumping = isJumping;
|
||||
}
|
||||
|
||||
public void setIsMoving(boolean isMoving) {
|
||||
this.isMoving = isMoving;
|
||||
}
|
||||
|
||||
protected static final double JUMP_MARKER_TIME_DISTANCE = 0.1; // worm-seconds
|
||||
|
||||
private void updateJumpTime() {
|
||||
try {
|
||||
double time = getFacade().getJumpTime(getWorm());
|
||||
if (time > 0) {
|
||||
int n = 1 + (int) (time / JUMP_MARKER_TIME_DISTANCE);
|
||||
xys = new double[n][];
|
||||
for (int i = 1; i <= n; i++) {
|
||||
double dt = i * time / n;
|
||||
double[] xy = getFacade().getJumpStep(getWorm(), dt);
|
||||
xys[i - 1] = xy;
|
||||
}
|
||||
} else {
|
||||
this.xys = null;
|
||||
}
|
||||
} catch (ModelException e) {
|
||||
this.xys = null;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized double[][] getJumpSteps() {
|
||||
return xys;
|
||||
}
|
||||
|
||||
public synchronized double getOrientation() {
|
||||
return orientation;
|
||||
}
|
||||
|
||||
public synchronized String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
private void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public synchronized long getActionPoints() {
|
||||
return actionPoints;
|
||||
}
|
||||
|
||||
public synchronized long getMaxActionPoints() {
|
||||
return maxActionPoints;
|
||||
}
|
||||
|
||||
public synchronized double getActualX() {
|
||||
return actualX;
|
||||
}
|
||||
|
||||
public synchronized double getActualY() {
|
||||
return actualY;
|
||||
}
|
||||
|
||||
public synchronized double getRadius() {
|
||||
return radius;
|
||||
}
|
||||
}
|
@@ -0,0 +1,114 @@
|
||||
package worms.internal.gui.menu;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
import worms.internal.gui.GUIUtils;
|
||||
import worms.internal.gui.InputMode;
|
||||
import worms.internal.gui.Screen;
|
||||
import worms.internal.gui.WormsGUI;
|
||||
|
||||
public abstract class AbstractMenuScreen<Choice> extends Screen {
|
||||
|
||||
private static final int INSTRUCTIONS_AREA_HEIGHT = 100;
|
||||
private static final int CHOICE_HEIGHT = 30;
|
||||
|
||||
private static final Font DEFAULT_CHOICE_FONT = new Font(Font.SANS_SERIF,
|
||||
Font.PLAIN, (CHOICE_HEIGHT * 4) / 6);
|
||||
private static final Color DEFAULT_CHOICE_COLOR = Color.WHITE;
|
||||
private static final Font SELECTED_CHOICE_FONT = new Font(Font.SANS_SERIF,
|
||||
Font.PLAIN, (CHOICE_HEIGHT * 5) / 6);
|
||||
private static final Color SELECTED_CHOICE_COLOR = Color.YELLOW;
|
||||
|
||||
final Choice[] choices;
|
||||
|
||||
BlockingQueue<Choice> selection = new ArrayBlockingQueue<Choice>(1);
|
||||
int selectedIndex = 0;
|
||||
|
||||
public AbstractMenuScreen(WormsGUI gui) {
|
||||
super(gui);
|
||||
this.choices = getChoices();
|
||||
}
|
||||
|
||||
public void selectNext() {
|
||||
selectedIndex = (selectedIndex + 1) % choices.length;
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void selectPrevious() {
|
||||
selectedIndex = (selectedIndex + choices.length - 1) % choices.length;
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void selectCurrent() {
|
||||
if (selection.isEmpty())
|
||||
selection.add(choices[selectedIndex]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputMode<? extends AbstractMenuScreen<Choice>> createDefaultInputMode() {
|
||||
return new MenuInputMode<AbstractMenuScreen<Choice>, Choice>(this, null);
|
||||
}
|
||||
|
||||
protected abstract Choice[] getChoices();
|
||||
|
||||
protected abstract String getDisplayName(Choice choice);
|
||||
|
||||
protected abstract String getInstructions();
|
||||
|
||||
public Choice select() {
|
||||
try {
|
||||
return selection.take();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintScreen(Graphics2D g) {
|
||||
paintInstructions(g);
|
||||
|
||||
int maxNbChoicesOnScreen = (getScreenHeight() - INSTRUCTIONS_AREA_HEIGHT)
|
||||
/ CHOICE_HEIGHT - 1;
|
||||
int start = 0;
|
||||
if (selectedIndex >= maxNbChoicesOnScreen) {
|
||||
start = selectedIndex - maxNbChoicesOnScreen + 1;
|
||||
}
|
||||
|
||||
int lastChoiceToDisplay = Math.min(start + maxNbChoicesOnScreen,
|
||||
choices.length);
|
||||
for (int index = start; index < lastChoiceToDisplay; index++) {
|
||||
Choice choice = choices[index];
|
||||
String str = getDisplayName(choice);
|
||||
if (index == selectedIndex) {
|
||||
g.setColor(SELECTED_CHOICE_COLOR);
|
||||
g.setFont(SELECTED_CHOICE_FONT);
|
||||
str = "\u00bb " + str + " \u00ab";
|
||||
} else {
|
||||
g.setColor(DEFAULT_CHOICE_COLOR);
|
||||
g.setFont(DEFAULT_CHOICE_FONT);
|
||||
}
|
||||
GUIUtils.drawCenteredString(g, str, getScreenWidth(),
|
||||
INSTRUCTIONS_AREA_HEIGHT + CHOICE_HEIGHT * (index - start));
|
||||
}
|
||||
if (lastChoiceToDisplay < choices.length) {
|
||||
g.setFont(DEFAULT_CHOICE_FONT);
|
||||
g.setColor(DEFAULT_CHOICE_COLOR);
|
||||
GUIUtils.drawCenteredString(g, "...", getScreenWidth(),
|
||||
INSTRUCTIONS_AREA_HEIGHT + CHOICE_HEIGHT
|
||||
* maxNbChoicesOnScreen);
|
||||
}
|
||||
}
|
||||
|
||||
private void paintInstructions(Graphics2D g) {
|
||||
g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 20));
|
||||
g.setColor(Color.WHITE);
|
||||
GUIUtils.drawCenteredString(g, getInstructions(), getScreenWidth(),
|
||||
INSTRUCTIONS_AREA_HEIGHT / 2);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
package worms.internal.gui.menu;
|
||||
|
||||
import worms.internal.gui.GameState;
|
||||
import worms.internal.gui.WormsGUI;
|
||||
import worms.internal.gui.game.PlayGameScreen;
|
||||
|
||||
enum MainMenuOption {
|
||||
Play("Play worms"), PlayDebug("Play worms (debug mode)"), Exit("Exit");
|
||||
|
||||
private final String displayString;
|
||||
|
||||
MainMenuOption(String displayString) {
|
||||
this.displayString = displayString;
|
||||
}
|
||||
|
||||
public String getDisplayString() {
|
||||
return displayString;
|
||||
}
|
||||
}
|
||||
|
||||
public class MainMenuScreen extends AbstractMenuScreen<MainMenuOption> {
|
||||
|
||||
public MainMenuScreen(WormsGUI gui) {
|
||||
super(gui);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MainMenuOption[] getChoices() {
|
||||
return MainMenuOption.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDisplayName(MainMenuOption option) {
|
||||
return option.getDisplayString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInstructions() {
|
||||
return "Please make your choice";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void screenStarted() {
|
||||
MainMenuOption option = select();
|
||||
switch (option) {
|
||||
case Play:
|
||||
startGame(false);
|
||||
break;
|
||||
case PlayDebug:
|
||||
startGame(true);
|
||||
break;
|
||||
case Exit:
|
||||
getGUI().exit();
|
||||
}
|
||||
}
|
||||
|
||||
private void startGame(boolean debugMode) {
|
||||
WormsGUI gui = getGUI();
|
||||
GameState gameState = new GameState(gui.getFacade(),
|
||||
gui.getOptions().randomSeed, gui.getWidth(), gui.getHeight());
|
||||
|
||||
PlayGameScreen playGameScreen = PlayGameScreen.create(gui, gameState,
|
||||
debugMode);
|
||||
|
||||
gameState.startGame();
|
||||
getGUI().switchToScreen(playGameScreen);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
package worms.internal.gui.menu;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import worms.internal.gui.InputMode;
|
||||
|
||||
public class MenuInputMode<ST extends AbstractMenuScreen<Choice>, Choice> extends
|
||||
InputMode<ST> {
|
||||
|
||||
public MenuInputMode(ST screen,
|
||||
InputMode<ST> previous) {
|
||||
super(screen, previous);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
|
||||
switch (e.getKeyCode()) {
|
||||
case KeyEvent.VK_ESCAPE:
|
||||
getScreen().getGUI().exit();
|
||||
break;
|
||||
case KeyEvent.VK_DOWN:
|
||||
getScreen().selectNext();
|
||||
|
||||
break;
|
||||
case KeyEvent.VK_UP:
|
||||
getScreen().selectPrevious();
|
||||
break;
|
||||
case KeyEvent.VK_ENTER:
|
||||
getScreen().selectCurrent();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
package worms.internal.gui.messages;
|
||||
|
||||
public class Message {
|
||||
private final String message;
|
||||
private final MessageType type;
|
||||
|
||||
public Message(String message, MessageType type) {
|
||||
this.message = message;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return message;
|
||||
}
|
||||
public MessageType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result
|
||||
+ ((message == null) ? 0 : message.hashCode());
|
||||
result = prime * result
|
||||
+ ((type == null) ? 0 : type.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Message other = (Message) obj;
|
||||
if (message == null) {
|
||||
if (other.message != null)
|
||||
return false;
|
||||
} else if (!message.equals(other.message))
|
||||
return false;
|
||||
if (type != other.type)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package worms.internal.gui.messages;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
import worms.internal.gui.GUIConstants;
|
||||
|
||||
public class MessageDisplay {
|
||||
private LinkedList<Message> messages = new LinkedList<Message>();
|
||||
private long currentMessageDisplayedSince;
|
||||
|
||||
public MessageDisplay() {
|
||||
}
|
||||
|
||||
public void addMessage(String message, MessageType type) {
|
||||
Message newMessage = new Message(message, type);
|
||||
if (messages.isEmpty() || !messages.getLast().equals(newMessage))
|
||||
this.messages.add(newMessage);
|
||||
}
|
||||
|
||||
private boolean isDisplayingMessage() {
|
||||
return currentMessageDisplayedSince > 0;
|
||||
}
|
||||
|
||||
private double currentDisplayTime() {
|
||||
return (System.currentTimeMillis() - currentMessageDisplayedSince) / 1000.0;
|
||||
}
|
||||
|
||||
private Message currentMessage() {
|
||||
return messages.peek();
|
||||
}
|
||||
|
||||
private void gotoNextMessage() {
|
||||
if (!messages.isEmpty()) {
|
||||
currentMessageDisplayedSince = System.currentTimeMillis();
|
||||
} else {
|
||||
currentMessageDisplayedSince = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public Message getMessage() {
|
||||
if (isDisplayingMessage()) {
|
||||
if (currentDisplayTime() >= GUIConstants.MESSAGE_DISPLAY_TIME) {
|
||||
messages.remove();
|
||||
gotoNextMessage();
|
||||
}
|
||||
} else {
|
||||
gotoNextMessage();
|
||||
}
|
||||
return currentMessage();
|
||||
}
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
package worms.internal.gui.messages;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import worms.internal.gui.AbstractPainter;
|
||||
import worms.internal.gui.GUIUtils;
|
||||
import worms.internal.gui.Screen;
|
||||
|
||||
public class MessagePainter extends AbstractPainter<Screen> {
|
||||
|
||||
public MessagePainter(Screen screen) {
|
||||
super(screen);
|
||||
}
|
||||
|
||||
protected static final Color ERROR_MESSAGE_BACKGROUND_COLOR = new Color(
|
||||
0x60a7130e, true);
|
||||
protected static final Color NORMAL_MESSAGE_BACKGROUND_COLOR = new Color(
|
||||
0x600e13a7, true);
|
||||
protected static final Color INFO_MESSAGE_BACKGROUND_COLOR = new Color(
|
||||
0x60565656, true);
|
||||
protected static final Color MESSAGE_TEXT_COLOR = Color.WHITE;
|
||||
|
||||
private static final int LINE_HEIGHT = 30;
|
||||
|
||||
public void paintMessage(Graphics2D g, Message message) {
|
||||
switch (message.getType()) {
|
||||
case ERROR:
|
||||
g.setColor(ERROR_MESSAGE_BACKGROUND_COLOR);
|
||||
break;
|
||||
case INFO:
|
||||
g.setColor(INFO_MESSAGE_BACKGROUND_COLOR);
|
||||
break;
|
||||
default:
|
||||
g.setColor(NORMAL_MESSAGE_BACKGROUND_COLOR);
|
||||
}
|
||||
|
||||
StringTokenizer tok = new StringTokenizer(message.getText(), "\n");
|
||||
int nbLines = tok.countTokens();
|
||||
|
||||
int height = LINE_HEIGHT * (nbLines + 2);
|
||||
int top = (getScreen().getScreenHeight() - height) / 2;
|
||||
|
||||
g.fillRect(0, top, getScreen().getScreenWidth(), height);
|
||||
Font oldFont = g.getFont();
|
||||
g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 2 * LINE_HEIGHT / 3));
|
||||
g.setColor(MESSAGE_TEXT_COLOR);
|
||||
|
||||
int y = top + 2 * LINE_HEIGHT;
|
||||
while (tok.hasMoreTokens()) {
|
||||
String line = tok.nextToken();
|
||||
GUIUtils.drawCenteredString(g, line, getScreen().getScreenWidth(),
|
||||
y);
|
||||
y += LINE_HEIGHT;
|
||||
}
|
||||
|
||||
g.setFont(oldFont);
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package worms.internal.gui.messages;
|
||||
|
||||
public enum MessageType {
|
||||
INFO, NORMAL, ERROR
|
||||
}
|
21
OGP1718-Worms/src-provided/worms/util/ModelException.java
Normal file
21
OGP1718-Worms/src-provided/worms/util/ModelException.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package worms.util;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
/**
|
||||
* <code>Facade</code> is not allowed to throw exceptions except for <code>ModelException</code>.
|
||||
*
|
||||
* Do not use ModelException outside of <code>Facade</code>.
|
||||
*/
|
||||
public class ModelException extends RuntimeException {
|
||||
public ModelException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ModelException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ModelException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user