Javadoc, small bugfix in levels.xml
[AntiTD.git] / src / se / umu / cs / dit06ajnajs / level / Level.java
blob130c44c56a6b1b96c5d98b04440a89c56bfa8368
1 package se.umu.cs.dit06ajnajs.level;
3 import java.awt.Dimension;
4 import java.awt.Graphics;
5 import java.awt.Image;
6 import java.awt.Point;
7 import java.awt.image.BufferedImage;
8 import java.util.ArrayList;
9 import java.util.List;
10 import java.util.logging.Logger;
12 import se.umu.cs.dit06ajnajs.AntiTD;
13 import se.umu.cs.dit06ajnajs.agent.Direction;
14 import java.util.Collection;
15 import se.umu.cs.dit06ajnajs.agent.Tower;
17 /**
18 * Level is used to contain and manage all information about a level beeing
19 * played in the game.
21 * @author Anton Johansson (dit06ajn@cs.umu.se)
22 * @author Andreas Jacobsson (dit06ajs@cs.umu.se)
23 * @version 1.0
25 public class Level {
26 private static Logger logger = Logger.getLogger("AntiTD");
28 private String name;
29 private int unitsToWin;
30 private int clearedUnits;
31 private int squareSize;
32 private int width;
33 private int height;
34 private int numCol;
35 private int numRow;
36 private MapSquare[][] squareMatrix;
37 private GoalSquare[] goalSquares;
38 private StartSquare[] startSquares;
39 private Collection<Tower> towers;
41 /**
42 * Sets parameters and adds Towers to random locations.
44 * @param name The name of this Level.
45 * @param squareMatrix A matrix containing MapSquares.
46 * @param towers Towers to add to this Level.
47 * @param unitsToWin The amount of Units needed in GoalSquares to win this
48 * Level.
50 public Level(String name, MapSquare[][] squareMatrix,
51 Collection<Tower> towers, int unitsToWin) {
52 this(name, squareMatrix);
54 // Add towers to TowerSquares
55 this.towers = towers;
56 this.unitsToWin = unitsToWin;
57 for (Tower tower : towers) {
58 addTower(tower);
62 /**
63 * Calculates and sets variables such as width and height based on the
64 * supplied matrix containing MapSquares. Makes every StartSquare observe
65 * alle the other. Initializes all TurnSquare to set all valid Direction.
67 * @param name The name of this Level.
68 * @param squareMatrix A matrix containing MapSquares.
70 public Level(String name, MapSquare[][] squareMatrix) {
71 this.name = name;
72 this.squareSize = AntiTD.SQUARE_SIZE;
73 this.squareMatrix = squareMatrix;
74 this.clearedUnits = 0;
75 try {
76 this.numCol = squareMatrix.length;
77 this.numRow = squareMatrix[0].length;
78 } catch(NullPointerException e) {
79 e.printStackTrace();
80 return;
83 // Set width and height for map
84 this.width = squareSize * numCol;
85 this.height = squareSize * numRow;
87 this.goalSquares = extractGoalSquares();
88 this.startSquares = extractStartSquares();
89 // Make all startSquares observe each other
90 for (StartSquare s1 : startSquares) {
91 for (StartSquare s2 : startSquares) {
92 if (s1 != s2) {
93 logger.info("StartSquare observable: " + s1
94 + ", is observed by " + s2);
95 s2.addObserver(s1);
99 initTurnSquares();
103 * Reset the Level information, clears all Units, activates on StartSquare,
104 * reset Towers.
106 public void resetLevel() {
107 this.clearedUnits = 0;
108 // Set one StartSquare as active
109 startSquares[0].click();
110 this.resetTowers();
111 this.resetStartSquares();
115 * Cleares queued units from start squares
116 * @author dit06ajs
118 private void resetStartSquares() {
119 for (StartSquare square : this.startSquares) {
120 square.init();
125 * Returns the MapSquare at given x- y-coordinate.
127 * @param x The x-value to check.
128 * @param y The y-value to check.
129 * @return The MapSquare at given x- y-coordinate or null if none exist.
131 public MapSquare getMapSquareAtPoint(int x, int y) {
132 return getMapSquareAtPoint(new Point(x, y));
136 * Returns the MapSquare at given x- y-coordinate.
138 * @param point The point to check.
139 * @return The MapSquare at given x- y-coordinate or null if none exist.
141 public MapSquare getMapSquareAtPoint(Point point) {
142 //TODO testa algoritmen
143 int x = point.x;
144 int y = point.y;
146 if (x > width || y > height
147 || x < 0 || y < 0) {
148 // Out of bounds
149 return null;
152 int col = x / AntiTD.SQUARE_SIZE;
153 int row = y / AntiTD.SQUARE_SIZE;
155 return squareMatrix[col][row];
159 * Sets a new MapSquare at given x- y-coordinate.
161 * @param x The x-coordinate.
162 * @param y The y-coordinate.
163 * @param mapSquare The MapSquare to set at given x- y-coordinates .
165 public void setMapSquareAtPoint(int x, int y, MapSquare mapSquare) {
166 setMapSquareAtPoint(new Point(x, y), mapSquare);
170 * Sets a new MapSquare at given x- y-coordinate.
172 * @param point The point to set a new MapSquare.
173 * @param mapSquare The MapSquare to set at given point.
175 public void setMapSquareAtPoint(Point point, MapSquare mapSquare) {
176 int x = point.x;
177 int y = point.y;
179 if (x > width || y > height) {
180 throw new IllegalArgumentException("Position is " +
181 "outside of map bounds.");
184 int col = x / AntiTD.SQUARE_SIZE;
185 int row = y / AntiTD.SQUARE_SIZE;
187 squareMatrix[col][row] = mapSquare;
191 * Creates a new Image representing this Level and returns it.
193 * @return A new Image representing this Level and returns it.
195 public Image getMapImage() {
196 logger.fine("------> getMapImage() -> " + Thread.currentThread().toString());
197 Image backgroundImage = new BufferedImage(width, height,
198 BufferedImage.TYPE_INT_RGB);
199 Graphics g = backgroundImage.getGraphics();
201 for (MapSquare[] row : squareMatrix) {
202 for (MapSquare square : row) {
203 square.paint(g);
206 return backgroundImage;
210 * Returns the Dimension of this Level.
212 * @return The Dimension of this Level.
214 public Dimension getDimension() {
215 return new Dimension(width, height);
219 * Extract and return all GoalSquares in this Level.
221 * @return All GoalSquares in this Level.
223 private GoalSquare[] extractGoalSquares() {
224 List<GoalSquare> squares = new ArrayList<GoalSquare>();
225 for (MapSquare[] row : squareMatrix) {
226 for (MapSquare square : row) {
227 if (square instanceof GoalSquare) {
228 squares.add((GoalSquare) square);
232 GoalSquare[] goalSquares
233 = squares.toArray(new GoalSquare[squares.size()]);
234 return goalSquares;
238 * Extract all StartSquares from this Level.
240 * @return All StartSquares in this Level.
242 private StartSquare[] extractStartSquares() {
243 List<StartSquare> squares = new ArrayList<StartSquare>();
244 for (MapSquare[] row : squareMatrix) {
245 for (MapSquare square : row) {
246 if (square instanceof StartSquare) {
247 squares.add((StartSquare) square);
251 StartSquare[] startSquares
252 = squares.toArray(new StartSquare[squares.size()]);
253 return startSquares;
257 * Returns a random free TowerSquare.
259 * @return A random free TowerSquare.
261 private TowerSquare getRandomFreeTowerSquare() {
262 List<TowerSquare> squares = extractTowerSquares();
263 List<TowerSquare> freeSquares = new ArrayList<TowerSquare>();
265 for (TowerSquare square : squares) {
266 if (square.isAvailable()) {
267 freeSquares.add(square);
270 if (freeSquares.isEmpty()) {
271 // TODO What should happen if there are no free towersquares?
272 return null;
274 int index = (int) (freeSquares.size()*Math.random());
275 return freeSquares.get(index);
278 public List<TowerSquare> extractTowerSquares() {
279 // TODO What should happen if there are no towersquares?
280 List<TowerSquare> squares = new ArrayList<TowerSquare>();
281 for (MapSquare[] row : squareMatrix) {
282 for (MapSquare square : row) {
283 if (square instanceof TowerSquare) {
284 squares.add((TowerSquare) square);
288 return squares;
292 * Extract all MapSquare of type TurnSquare from this Level.
294 * @return All TurnSquares in this Level.
296 public List<TurnSquare> extractTurnSquares() {
297 // TODO What should happen if there are no TurnSquares?
298 List<TurnSquare> squares = new ArrayList<TurnSquare>();
299 for (MapSquare[] row : squareMatrix) {
300 for (MapSquare square : row) {
301 if (square instanceof TurnSquare) {
302 squares.add((TurnSquare) square);
306 return squares;
310 * Returns every neighbouring MapSquare of the supplied MapSquare within
311 * given range.
313 * @param square The square to get neighbours from.
314 * @param range Should be an int describing how many MapSquares away from
315 * specified MapSquares to return.
316 * @return A list of neighbouring MapSquares.
318 public List<MapSquare> getNeighbours(MapSquare square, int range) {
319 List<MapSquare> neighbours = new ArrayList<MapSquare>();
321 // Calculate row and col
322 int col = square.getX() / AntiTD.SQUARE_SIZE;
323 int row = square.getY() / AntiTD.SQUARE_SIZE;
325 // Get width to scan for neighbours
326 int scanWidth = range * 2 + 1;
328 // Get top left position to start scanning of neighbours
329 int colLeft = col - range;
330 int rowTop = row - range;
332 logger.info("Range: " + range + ", colLeft: " + colLeft
333 + ", rowTop: " + rowTop + ", scanWidth: " + scanWidth);
335 // Nestled loop from top-left position
336 for (int tmpRow = rowTop;
337 tmpRow < scanWidth + rowTop;
338 tmpRow++) {
339 for (int tmpCol = colLeft;
340 tmpCol < scanWidth + colLeft;
341 tmpCol++) {
342 if (tmpCol >= 0 && tmpCol < numCol // Col is inside bounds
343 // Row is inside bounds
344 && tmpRow >= 0 && tmpRow < numRow
345 // Col Row should not point to square to find neighbours
346 // from
347 && (tmpRow != row || tmpCol != col)) {
348 // Neighbour found
349 logger.info("Adding neigbour for: " + square + "\n"
350 + " At col: " + tmpCol + ", row: " + tmpRow);
351 neighbours.add(squareMatrix[tmpCol][tmpRow]);
355 logger.info(neighbours.size() + " neighbours found.");
356 return neighbours;
360 * Calculate and set every possible direction the TurnSquares in this Level
361 * can have. Adds a Direction to every neighbouring MapSquare that is a
362 * TurnSquare.
364 private void initTurnSquares() {
365 for (TurnSquare turnSquare : extractTurnSquares()) {
366 int col = turnSquare.getX() / AntiTD.SQUARE_SIZE;
367 int row = turnSquare.getY() / AntiTD.SQUARE_SIZE;
368 // UP
369 if (row - 1 >= 0
370 && squareMatrix[col][row - 1] instanceof PathSquare) {
371 turnSquare.addDirection(Direction.UP);
373 // RIGHT
374 if (col + 1 < numCol
375 && squareMatrix[col + 1][row] instanceof PathSquare) {
376 turnSquare.addDirection(Direction.RIGHT);
378 // DOWN
379 if (row + 1 < numRow
380 && squareMatrix[col][row + 1] instanceof PathSquare) {
381 turnSquare.addDirection(Direction.DOWN);
383 // LEFT
384 if (col - 1 >= 0
385 && squareMatrix[col - 1][row] instanceof PathSquare) {
386 turnSquare.addDirection(Direction.LEFT);
392 * Return every GoalSquare in this Level.
394 * @return every GoalSquare in this Level.
396 public GoalSquare[] getGoalSquares() {
397 return this.goalSquares;
401 * Return every StartSquare in this Level.
403 * @return every StartSquare in this Level.
405 public StartSquare[] getStartSquares() {
406 return this.startSquares;
410 * Gets the name of this Level.
412 * @return The name of this Level.
414 public String getName() {
415 return this.name;
419 * Returns an Array representation of this map.
421 * @return An Array representation of this map.
423 public MapSquare[][] toArray() {
424 return squareMatrix;
428 * Return the active StartSquare in this Level.
430 * @return The active StartSquare in this Level.
432 public StartSquare getActiveStartSquare() {
433 for (StartSquare square : startSquares) {
434 if (square.isActive()) {
435 return square;
438 throw new NoActiveStartSquareException();
442 * Add a Tower to this Level. Will place the Tower at a random
443 * TowerSquare. Every neighbouring PathSquare is set to observe the Tower.
445 * @param t The Tower to add.
447 public void addTower(Tower t) {
448 TowerSquare square = getRandomFreeTowerSquare();
449 if (square != null) {
450 Point tPos = new Point(square.getCenterX() - (t.getWidth()/2)
451 , square.getCenterY() - (t.getHeight()/2));
452 t.setPostition(tPos);
453 square.setTower(t);
455 logger.info("Tower placed @ (" + tPos.x + ", " + tPos.y + ")");
457 //Register as observer for the neighbours in range
458 int shootRange = t.getRange();
459 int squareRange =
460 (int) Math.ceil((shootRange - AntiTD.SQUARE_SIZE*0.5)
461 / (AntiTD.SQUARE_SIZE));
463 List<MapSquare> neighbours
464 = getNeighbours(square, squareRange);
465 logger.info("There are >" + neighbours.size()
466 + "< neighbours in range >" + t.getRange() + "< pixels");
468 for (MapSquare neighbour: neighbours) {
469 if (neighbour instanceof PathSquare) {
470 logger.info("Adding tower-observer: " + t
471 + " to: " + neighbour);
472 neighbour.addObserver(t);
475 } else {
476 throw new IllegalArgumentException("No available towersquares");
481 * Returns all Towers in this Level.
483 * @return All Towers in this Level.
485 public Collection<Tower> getTowers() {
486 return this.towers;
490 * Resets the towers.
492 public void resetTowers() {
493 for (Tower tower : this.towers) {
494 tower.init();
499 * Returns the number of Units needed in goal to win this Level.
501 * @return The number of Units needed in goal to win this Level.
503 public int getUnitsToWin() {
504 return this.unitsToWin;
508 * Returns the number of units that have reached a goalsquare.
509 * @return The number of units that have reached a goalsquare.
511 public int getNumOfClearedUnits() {
512 return this.clearedUnits;
516 * Adds to the number of units that have reached a goalsquare.
517 * @param num Number of new units that have reached a goalsquare.
519 public void addClearedUnits(int num) {
520 this.clearedUnits += num;