package net.javlov.world.grid;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import net.javlov.world.Body;
import net.javlov.world.CollisionEvent;
/**
* Grid world implementation that can be used with bodies of any size and shape.
* @author matthijs
*
*/
public class GridWorldGeneric extends GridWorld {
private List<CollisionEvent> collisionList;
public GridWorldGeneric(int width, int height, double cellWidth,
double cellHeight) {
super(width, height, cellWidth, cellHeight);
collisionList = new ArrayList<CollisionEvent>();
}
//TODO this only works correctly if no recursion is required, i.e. if there is
//no collision with movable objects next to other objects. In the latter case,
//all collision events might be fired before it can be guaranteed
//that the move is allowed.
/**
* @return true if the move was completed entirely (the number of cells indicated
* by the speed parameter), false otherwise.
*/
@Override
public boolean translateBody(Body b, Direction d, int speed) {
List<GridCell> currCells = getIntersectingCellsLarge(getTransformedShape(b)),
targetCells = new ArrayList<GridCell>(currCells.size());
GridCell locCell = grid.getCell(b.getX(), b.getY()),
targetCell;
int i;
boolean moveAllowed;
for ( i = 0; i < speed; i++) {
moveAllowed = true;
for ( GridCell currCell : currCells ) {
targetCell = currCell.go(d);
targetCells.add(targetCell);
if ( targetCell.isBorder() || !move(b, d, targetCell) )
moveAllowed = false;
}
if ( moveAllowed ) {
fireQueuedCollisionEvents();
for ( int j = 0; j < currCells.size(); j++ ) {
currCells.get(j).removeBody(b);
targetCells.get(j).addBody(b);
currCells.set(j, targetCells.get(j));
}
locCell = locCell.go(d); //track body's location
} else {
cleanQueuedCollisionEvents();
break;
}
}
b.setLocation(locCell.getCenterX(), locCell.getCenterY());
if ( i < speed-1 ) //move couldn't be entirely completed
return false;
return true;
}
/**
* Checks if the body can be moved to the specified target cell. If an obstacle is
* encountered in the target cell, the move is invalidated and a CollisionEvent is
* fired immediately.
* If any other type of Body is encountered, a CollisionEvent is created and put in
* a list of collision events. These events will be fired if the move as a whole (as
* executed by {@link #translateBody(Body, Direction, int)}) is allowed (the move
* method only checks a single cell).
*/
@Override
protected boolean move( Body b, Direction d, GridCell targetCell ) {
//TODO Don't like all these fors and ifs
List<Body> occupiers = targetCell.getOccupiers();
//obstacles take precedence over all other bodies
for ( Body targetBody : occupiers )
if ( targetBody.getType() == Body.OBSTACLE ) {
fireCollisionEvent(b, targetBody, new Point2D.Double(d.x(), d.y()));
return false;
}
//next check if movables can be moved
for ( Body targetBody : occupiers )
if ( targetBody.getType() == Body.MOVABLE ) {
queueCollisionEvent(b, targetBody, new Point2D.Double(d.x(), d.y()));
if ( !translateBody(targetBody, d, 1) )
return false;
}
//just queue collisionevent for the other body types
for ( Body targetBody : occupiers )
if ( targetBody.getType() != Body.OBSTACLE && targetBody.getType() != Body.MOVABLE )
queueCollisionEvent(b, targetBody, new Point2D.Double(d.x(), d.y()));
return true;
}
protected void queueCollisionEvent(Body b1, Body b2, Point2D.Double speed) {
CollisionEvent e = new CollisionEvent(b1, b2, speed, (Point2D.Double)b2.getLocation());
collisionList.add(e);
}
protected void fireQueuedCollisionEvents() {
for ( CollisionEvent event : collisionList )
fireCollisionEvent(event);
cleanQueuedCollisionEvents();
}
protected void cleanQueuedCollisionEvents() {
collisionList.clear();
}
protected Shape getTransformedShape(Body b) {
//rotate figure
AffineTransform at = new AffineTransform();
//rotate around the body's center
at.rotate(b.getBearing(), b.getX(), b.getY());
return at.createTransformedShape(b.getFigure());
}
@Override
protected void addToCells(Body b) {
int count = 0;
for ( GridCell c : getIntersectingCellsLarge(getTransformedShape(b)) ) {
count++;
c.addBody(b);
}
}
@Override
protected void removeFromCells(Body b) {
for ( GridCell c : getIntersectingCellsLarge(getTransformedShape(b)) )
c.removeBody(b);
}
}