merge
[AntiTD.git] / src / se / umu / cs / dit06ajnajs / agent / Unit.java
blob383b4dd8c1a192dbe611eba77f23fff75befe706
1 package se.umu.cs.dit06ajnajs.agent;
3 import java.applet.AudioClip;
4 import java.awt.Color;
5 import java.awt.Graphics;
6 import java.awt.Image;
7 import java.awt.Point;
8 import java.awt.Rectangle;
9 import java.util.Map;
10 import java.util.logging.Logger;
12 import se.umu.cs.dit06ajnajs.ATDSoundPlayer;
13 import se.umu.cs.dit06ajnajs.level.Level;
14 import se.umu.cs.dit06ajnajs.level.MapSquare;
15 import se.umu.cs.dit06ajnajs.level.Traversable;
17 import java.awt.Rectangle;
18 import java.awt.Color;
20 /**
21 * Abstract class used as superclass to all Units in a game. All methods are
22 * implemented and can be used as a basic Unit.
24 * @author Anton Johansson (dit06ajn@cs.umu.se)
25 * @author Andreas Jakobsson (dit06ajs@cs.umu.se)
26 * @version 1.0
28 public abstract class Unit implements Agent {
29 private static Logger logger = Logger.getLogger("AntiTD");
31 private int x;
32 private int y;
33 private int centerX;
34 private int centerY;
36 private int width;
37 private int height;
39 private String type;
40 private int speed;
41 private int health;
42 private int currentHealth;
43 private int cost;
45 private Map<Direction, Image> images;
46 private Map<String, AudioClip> sounds;
47 private Direction direction;
48 private Level level;
49 private boolean alive;
50 private boolean pause;
51 private Object lastTurnedBy;
53 /**
54 * Instantiates all supplied parameters. Sets this Unit to be alive and not
55 * paused.
57 * @param type The type or name of this Unit, this is used as a unique
58 * identifier in AgentPrototypeFactory to create different Units from this
59 * class.
60 * @param width The width of this Unit.
61 * @param height The height of this Unit.
62 * @param speed The speed of this Unit. Unit with specified pixels every
63 * timeframe.
64 * @param health The health of this Unit.
65 * @param cost The cost of this Unit.
67 public Unit(String type, int width, int height, int speed, int health,
68 int cost, Map<Direction, Image> images,
69 Map<String, AudioClip> sounds) {
70 this.type = type;
71 this.width = width;
72 this.height = height;
73 setX(x);
74 setY(y);
75 this.speed = speed;
76 this.health = health;
77 this.currentHealth = health;
78 this.cost = cost;
79 this.images = images;
80 this.sounds = sounds;
82 this.alive = true;
83 this.pause = false;
86 /**
87 * Sets the image the Paintable should contain.
88 * @param img The image a Paintable should contain.
90 public void setImage(Image img) {
91 // TODO, change Paintable to Map<Comparable, Image>
94 /**
95 * Returns the type/name of this Unit.
97 * @return The type/name of this Unit.
99 public String getType() {
100 return this.type;
104 * When it's time for this Unit to act, this method is called. It checks
105 * current health to see if it died. Otherwise it calculates next position
106 * and moves there by changing it's x and y coordinates. If the new
107 * possition is on a Traversable MapSquare it calles it's landOn method,
108 * otherwise it dies. If the unit is paused it does not move, but sets pause
109 * back to true, this is the case if there is a collision in next
110 * move. Collsions are set from a controller class.
112 public void act() {
113 // Check if this unit has health left
114 if (this.currentHealth > 0) {
115 // Check if this unit is active
116 if (!pause) {
117 Point nextPos = getNextPosition();
118 // TODO check for collision on next position
119 move(nextPos);
121 // Land on current square
122 MapSquare currentSquare
123 = level.getMapSquareAtPoint(centerX, centerY);
124 if (currentSquare instanceof Traversable) {
125 ((Traversable) currentSquare).landOn(this);
126 } else {
127 // Unit hitting something not Traversable and therefore
128 // dies.
129 this.alive = false;
131 } else {
132 // Toggle pause back to false;
133 pause = false;
135 } else {
136 // Kill unit
137 this.alive = false;
138 ATDSoundPlayer.play(sounds.get("dead"));
143 * Calculates and returns the next position this unit will have after next
144 * move.
146 * @return The new position.
148 public Point getNextPosition() {
149 switch (direction) {
150 case UP:
151 logger.fine("UP");
152 return new Point(x, y - speed);
153 case DOWN:
154 logger.fine("DOWN");
155 return new Point(x, y + speed);
156 case LEFT:
157 logger.fine("LEFT");
158 return new Point(x - speed, y);
159 case RIGHT:
160 logger.fine("RIGHT");
161 return new Point(x + speed, y);
162 default:
163 System.err.println("Unit has not got a valid Direction");
164 // TODO, make a NoValidException and throw
165 return null;
170 * Move Unit to specified Point. New x and y coordinates are set.
172 * @param point The Point to move to.
174 public void move(Point point) {
175 setX(point.x);
176 setY(point.y);
179 /** Used to calculate and set a new x-center after x is changed. */
180 private void recalculateCenterX() {
181 this.centerX = this.x + this.width / 2;
184 /** Used to calculate and set a new y-center after y is changed. */
185 private void recalculateCenterY() {
186 this.centerY = this.y + this.height / 2;
190 * Returns the current x value.
192 * @return The current x value.
194 public int getX() {
195 return x;
199 * Sets the current x value, recalculates and sets a new centerX value.
201 * @param x The new x value.
203 public void setX(int x) {
204 this.x = x;
205 recalculateCenterX();
209 * Returns the current y value.
211 * @return The current y value.
213 public int getY() {
214 return y;
218 * Sets the current y value, recalculates and sets a new centerY value.
220 * @param y The new y value.
222 public void setY(int y) {
223 this.y = y;
224 recalculateCenterY();
228 * Sets the current width, recalculates and sets a new centerX value.
230 * @param width The new width value.
232 public void setWidth(int width) {
233 this.width = width;
234 recalculateCenterX();
238 * Returns the current width.
240 * @return The current width.
242 public int getWidth() {
243 return width;
247 * Sets the current height, recalculates and sets a new centerY value.
249 * @param height The new height value.
251 public void setHeight(int height) {
252 this.height = height;
253 recalculateCenterY();
257 * Returns the current height.
259 * @return The current height.
261 public int getHeight() {
262 return height;
266 * Sets a new centerX position. Recalculates and sets the new x-position.
268 * @param centerX The new centerX position.
270 public void setCenterX(int centerX) {
271 this.centerX = centerX;
272 setX(centerX - (width / 2));
276 * Sets a new centerY position. Recalculates and sets the new y-position.
278 * @param centerY The new centerY position.
280 public void setCenterY(int centerY) {
281 this.centerY = centerY;
282 setY(centerY - (width / 2));
286 * Sets a new center point. Recalculates and sets the new x- and y-position.
288 * @param centerX The new centerX position.
289 * @param centerY The new centerY position.
291 public void setCenterPoint(int centerX, int centerY) {
292 setCenterPoint(new Point(centerX, centerY));
296 * Sets a new center point. Recalculates and sets the new x- and y-position.
298 * @param point The new center point.
300 public void setCenterPoint(Point point) {
301 setCenterX(point.x);
302 setCenterY(point.y);
306 * Returns the center point.
308 * @return The center Point.
310 public Point getCenterPoint() {
311 return new Point(this.centerX, this.centerY);
315 * Sets a new speed. Could be used by Items to give Units speedboosts and
316 * slowdowns.
318 * @param speed The new speed.
320 public void setSpeed(int speed) {
321 this.speed = speed;
325 * Returns the speed of this Unit.
327 * @return The speed of this Unit.
329 public int getSpeed() {
330 return speed;
335 * Returns the cost of this Unit.
337 * @return The cost of this Unit.
339 public int getCost() {
340 return this.cost;
344 * Sets the Direction of this Unit. If this method is called by the same
345 * Object twice in a row the new Direction is not accepted. This is used to
346 * prevent Units from dislocation in TurnSquares.
348 * @param direction The new Direction.
349 * @param caller The caller.
351 public void setDirection(Direction direction, Object caller) {
352 if (this.lastTurnedBy != caller) {
353 this.direction = direction;
354 this.lastTurnedBy = caller;
355 } else {
356 logger.info("Turned by same caller twice: " + caller);
361 * Returns the Direction of this Unit.
363 * @return The Direction of this Unit.
365 public Direction getDirection() {
366 return direction;
370 * Set a Level for this Unit.
372 * @param level The Level this Unit exists in.
374 public void setLevel(Level level) {
375 this.level = level;
379 * Sets this Units alive status.
381 * @param state false if Unit should be killed, true if it should live.
383 public void setAlive(boolean state) {
384 this.alive = state;
388 * Checks if this Unit is alive.
390 * @return true if Unit is alive, false otherwise.
392 public boolean isAlive() {
393 return this.alive;
397 * Decrease the units currentHealth.
398 * @param damage The number of health points to substract
400 public void damage(int damage) {
401 this.currentHealth -= damage;
405 * Attemts to clone this Unit. Used by AgentPrototypeFactory to create new
406 * Units.
408 * @return A new instance of the same type as the instantiated Unit.
410 @Override
411 public Object clone() {
412 try {
413 return super.clone();
414 } catch (CloneNotSupportedException e) {
415 e.printStackTrace();
416 throw new Error("Object " + this.getClass().getName()
417 + " is not Cloneable");
422 * Called if there is a collision in this Units next position. If this Unit
423 * collides with a Unit moving in opposite Direction they both bounce,
424 * otherwise this Unit pauses next time it's act() method is called.
426 * @param unit The Unit this unit collides with.
428 public void collision(Unit unit) {
429 switch (direction) {
430 case UP:
431 if (unit.getDirection() == Direction.DOWN) {
432 setDirection(Direction.DOWN, unit);
433 unit.setDirection(Direction.UP, this);
434 return;
436 break;
437 case DOWN:
438 if (unit.getDirection() == Direction.UP) {
439 setDirection(Direction.UP, unit);
440 unit.setDirection(Direction.DOWN, this);
441 return;
443 break;
444 case LEFT:
445 if (unit.getDirection() == Direction.RIGHT) {
446 setDirection(Direction.RIGHT, unit);
447 unit.setDirection(Direction.LEFT, this);
448 return;
450 break;
451 case RIGHT:
452 if (unit.getDirection() == Direction.LEFT) {
453 setDirection(Direction.LEFT, unit);
454 unit.setDirection(Direction.RIGHT, this);
455 return;
457 break;
458 default:
459 // TODO: create exception
460 System.err.println("Unit hasn't got a valid Direction");
461 break;
463 this.pause = true;
467 * Returns the state of pause.
469 * @return true if Unit is paused, false otherwise.
471 public boolean isPaused() {
472 return this.pause;
476 * Overrides standard toString() method to return information about this
477 * Unit.
479 * @return A String containing type, cost and health.
481 @Override
482 public String toString() {
483 return getType() + ", cost: " + getCost() + ", strenght: " + this.health;
487 * Method to handle mouseClicks, plays sound "onClick" from Map. Override in
488 * subclass to implement special behaviour.
490 public void click() {
491 logger.info("Unit clicked " + this.toString());
492 ATDSoundPlayer.play(sounds.get("onClick"));
496 * Checks if the specified x and y position is inside of this Units bounds.
498 * @param x The x-value to check.
499 * @param y The y-value to check.
500 * @return If the specified x and y position is inside of this Units bounds.
502 public boolean contains(int x, int y) {
503 return new Rectangle(this.x, this.y, width, height).contains(x, y);
507 * Returns the bounding box this Unit exists in.
509 * @return A rectangle with x, y, width, height of this Unit.n
511 public Rectangle getBounds() {
512 return new Rectangle(x, y, width, height);
516 * Returns the Units future bounding box.
518 * @return A rectangle with x, y, width, height of this Units next position.
520 public Rectangle getFutureBounds() {
521 Point nextPoint = getNextPosition();
522 return new Rectangle(nextPoint.x, nextPoint.y, width, height);
526 * Checks if this supplied Unit intersects with this Unit.
528 * @param unit The Unit to check for intersection.
529 * @return true if supplied Unit intersects with this Unit, false otherwise.
531 public boolean intersects(Unit unit) {
532 return this.getBounds().intersects(unit.getBounds());
536 * Checks if this supplied Unit intersects with this Unit in its future
537 * position.
539 * @param unit The Unit to check for intersection.
540 * @return true if supplied Unit intersects with this Unit in its future,
541 * false otherwise.
543 public boolean intersectsNextMove(Unit unit) {
544 return this.getFutureBounds().intersects(unit.getBounds());
548 * Paints this Unit on the supplied Graphics. Draws Units Image, and a
549 * healthbar displaying current health.
551 * @param g The Graphics to paint to.
553 public void paint(Graphics g) {
554 Image image = images.get(direction);
555 g.drawImage(image, x, y, null);
557 // Paint health bar
558 int healthHeight = 2;
559 int yHealth = y + height - healthHeight;
560 g.setColor(Color.RED);
561 g.fillRect(x , yHealth, width, healthHeight);
562 g.setColor(Color.GREEN);
563 int healthBar = (int) (width * (new Double(currentHealth) / health));
564 g.fillRect(x , yHealth, healthBar, healthHeight);