/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.fpl.gamecontroller;
import com.google.fpl.gamecontroller.particles.BaseParticle;
/**
* Handles drawing and collision-detection with map walls.
*/
public class WallSegment {
private static final Utils.Color WALL_COLOR = Utils.Color.WHITE;
private static final int EDGE_COUNT = 4;
private final float mCenterX, mCenterY;
private final float mWidth, mHeight;
// The center points (x, y) for each edge of the wall.
private final float[] mEdgeCenters = new float[EDGE_COUNT * 2];
// The scale values (x, y) for each edge of the wall.
private final float[] mEdgeScales = new float[EDGE_COUNT * 2];
public WallSegment(float x, float y, float width, float height) {
mCenterX = x;
mCenterY = y;
mWidth = width;
mHeight = height;
// Compute the center and scale needed to draw lines along each edge of the wall.
int edgeIndex = 0;
// Left edge.
mEdgeCenters[edgeIndex + 0] = mCenterX - mWidth / 2.0f;
mEdgeCenters[edgeIndex + 1] = mCenterY;
mEdgeScales[edgeIndex + 0] = 1.0f + mHeight / 2.0f;
mEdgeScales[edgeIndex + 1] = 1.0f;
edgeIndex += 2;
// Right edge.
mEdgeCenters[edgeIndex + 0] = mCenterX + mWidth / 2.0f;
mEdgeCenters[edgeIndex + 1] = mCenterY;
mEdgeScales[edgeIndex + 0] = 1.0f + mHeight / 2.0f;
mEdgeScales[edgeIndex + 1] = 1.0f;
edgeIndex += 2;
// Bottom edge.
mEdgeCenters[edgeIndex + 0] = mCenterX;
mEdgeCenters[edgeIndex + 1] = mCenterY - mHeight / 2.0f;
mEdgeScales[edgeIndex + 0] = 1.0f;
mEdgeScales[edgeIndex + 1] = 1.0f + mWidth / 2.0f;
edgeIndex += 2;
// Top edge.
mEdgeCenters[edgeIndex + 0] = mCenterX;
mEdgeCenters[edgeIndex + 1] = mCenterY + mHeight / 2.0f;
mEdgeScales[edgeIndex + 0] = 1.0f;
mEdgeScales[edgeIndex + 1] = 1.0f + mWidth / 2.0f;
}
public void draw(ShapeBuffer sb) {
// Draw a line along each edge of the wall.
for (int i = 0; i < EDGE_COUNT; ++i) {
final int edgeIndex = i * 2;
sb.add2DShape(
mEdgeCenters[edgeIndex + 0], mEdgeCenters[edgeIndex + 1], // position
WALL_COLOR, // color
Utils.SQUARE_SHAPE, // vertices
mEdgeScales[edgeIndex + 0], mEdgeScales[edgeIndex + 1], // scale
0.0f, 1.0f); // heading
}
}
/**
* Returns true if the given point is inside this wall.
*/
public boolean isInWall(float x, float y) {
return x >= mCenterX - mWidth / 2
&& x <= mCenterX + mWidth / 2
&& y >= mCenterY - mHeight / 2
&& y <= mCenterY + mHeight / 2;
}
/**
* Checks for collisions with this wall.
*/
public void update(float timeFactor) {
GameState gameState = GameState.getInstance();
// First, check for collisions with bullets.
BaseParticle[] possibleHits = gameState.getShots().getPotentialCollisions(mCenterX,
mCenterY, mWidth, mHeight);
BaseParticle currentBullet;
// The possibleHits array will likely be longer than the number of entries returned.
// Look for a null entry to indicate the end of the list.
for (int i = 0; possibleHits[i] != null; i++) {
currentBullet = possibleHits[i];
if (isInWall(currentBullet.getPositionX(), currentBullet.getPositionY())) {
// Semi-hacky way to zero in on the collision point.
// When we detect a bullet is inside the wall, iterate backwards and forwards
// along the trajectory of the bullet by smaller and smaller steps.
// This should get us fairly close to the intersection point.
//
// For better collision detection, we could compute the actual line
// intersection point, instead of just approximating it.
float stepSize = -0.5f;
for (int j = 0; j < 3; ++j) {
currentBullet.incrementPosition(stepSize);
if (isInWall(currentBullet.getPositionX(), currentBullet.getPositionY())) {
stepSize = -Math.abs(stepSize) * 0.5f;
} else {
stepSize = Math.abs(stepSize) * 0.5f;
}
}
currentBullet.handleCollision();
}
}
// Now check for ship-wall collisions.
for (Spaceship currentPlayer : gameState.getPlayerList()) {
if (currentPlayer.isActive()
&& isInWall(currentPlayer.getPositionX(), currentPlayer.getPositionY())) {
float xx = (currentPlayer.getPositionX() - mCenterX) * mHeight;
float yy = (currentPlayer.getPositionY() - mCenterY) * mWidth;
float epsilon = 0.1f;
if (Math.abs(xx) > Math.abs(yy)) {
if (xx >= 0) {
currentPlayer.setPositionX(mCenterX + mWidth / 2 + epsilon);
} else {
currentPlayer.setPositionX(mCenterX - mWidth / 2 - epsilon);
}
} else {
if (yy >= 0) {
currentPlayer.setPositionY(mCenterY + mHeight / 2 + epsilon);
} else {
currentPlayer.setPositionY(mCenterY - mHeight / 2 - epsilon);
}
}
}
}
}
}