1 package se
.umu
.cs
.dit06ajnajs
.agent
;
3 import java
.applet
.AudioClip
;
5 import java
.awt
.Graphics
;
8 import java
.awt
.Rectangle
;
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
;
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)
28 public abstract class Unit
implements Agent
{
29 private static Logger logger
= Logger
.getLogger("AntiTD");
42 private int currentHealth
;
45 private Map
<Direction
, Image
> images
;
46 private Map
<String
, AudioClip
> sounds
;
47 private Direction direction
;
49 private boolean alive
;
50 private boolean pause
;
51 private Object lastTurnedBy
;
54 * Instantiates all supplied parameters. Sets this Unit to be alive and not
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
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
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
) {
77 this.currentHealth
= health
;
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>
95 * Returns the type/name of this Unit.
97 * @return The type/name of this Unit.
99 public String
getType() {
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.
113 // Check if this unit has health left
114 if (this.currentHealth
> 0) {
115 // Check if this unit is active
117 Point nextPos
= getNextPosition();
118 // TODO check for collision on next position
121 // Land on current square
122 MapSquare currentSquare
123 = level
.getMapSquareAtPoint(centerX
, centerY
);
124 if (currentSquare
instanceof Traversable
) {
125 ((Traversable
) currentSquare
).landOn(this);
127 // Unit hitting something not Traversable and therefore
132 // Toggle pause back to false;
138 ATDSoundPlayer
.play(sounds
.get("dead"));
143 * Calculates and returns the next position this unit will have after next
146 * @return The new position.
148 public Point
getNextPosition() {
152 return new Point(x
, y
- speed
);
155 return new Point(x
, y
+ speed
);
158 return new Point(x
- speed
, y
);
160 logger
.fine("RIGHT");
161 return new Point(x
+ speed
, y
);
163 System
.err
.println("Unit has not got a valid Direction");
164 // TODO, make a NoValidException and throw
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
) {
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.
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
) {
205 recalculateCenterX();
209 * Returns the current y value.
211 * @return The current y value.
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
) {
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
) {
234 recalculateCenterX();
238 * Returns the current width.
240 * @return The current width.
242 public int getWidth() {
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() {
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
) {
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
318 * @param speed The new speed.
320 public void setSpeed(int speed
) {
325 * Returns the speed of this Unit.
327 * @return The speed of this Unit.
329 public int getSpeed() {
335 * Returns the cost of this Unit.
337 * @return The cost of this Unit.
339 public int getCost() {
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
;
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() {
370 * Set a Level for this Unit.
372 * @param level The Level this Unit exists in.
374 public void setLevel(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
) {
388 * Checks if this Unit is alive.
390 * @return true if Unit is alive, false otherwise.
392 public boolean isAlive() {
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
408 * @return A new instance of the same type as the instantiated Unit.
411 public Object
clone() {
413 return super.clone();
414 } catch (CloneNotSupportedException e
) {
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
) {
431 if (unit
.getDirection() == Direction
.DOWN
) {
432 setDirection(Direction
.DOWN
, unit
);
433 unit
.setDirection(Direction
.UP
, this);
438 if (unit
.getDirection() == Direction
.UP
) {
439 setDirection(Direction
.UP
, unit
);
440 unit
.setDirection(Direction
.DOWN
, this);
445 if (unit
.getDirection() == Direction
.RIGHT
) {
446 setDirection(Direction
.RIGHT
, unit
);
447 unit
.setDirection(Direction
.LEFT
, this);
452 if (unit
.getDirection() == Direction
.LEFT
) {
453 setDirection(Direction
.LEFT
, unit
);
454 unit
.setDirection(Direction
.RIGHT
, this);
459 // TODO: create exception
460 System
.err
.println("Unit hasn't got a valid Direction");
467 * Returns the state of pause.
469 * @return true if Unit is paused, false otherwise.
471 public boolean isPaused() {
476 * Overrides standard toString() method to return information about this
479 * @return A String containing type, cost and health.
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
539 * @param unit The Unit to check for intersection.
540 * @return true if supplied Unit intersects with this Unit in its future,
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);
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
);