/*
* 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;
/**
* Handles drawing, placing and detecting collisions with power-ups.
*/
public class PowerUp {
// The number of frames to wait to wait after a powerup has been collected before
// it respawns.
private static final float RESPAWN_MIN_FRAME_COUNT = GameState.secondsToFrameDelta(3.0f);
private static final float RESPAWN_MAX_FRAME_COUNT = GameState.secondsToFrameDelta(6.0f);
private static final float ROTATION_RADIANS_PER_FRAME = 0.05f;
// In radians, the amount the powerup's alpha value varies per frame.
private static final float ALPHA_PULSE_RATE = 3.0f * ROTATION_RADIANS_PER_FRAME;
// The minimum alpha value in the pulsing animation.
private static final float ALPHA_PULSE_MIN_OPACITY = 2.0f / 3.0f;
// When a ship gets within the powerup's COLLISION_RADIUS, it will collect the powerup.
private static final float COLLISION_RADIUS = 5.0f;
// Newly spawned powerups will not be placed too close to players.
private static final float MIN_DISTANCE_TO_PLAYER = 10.0f;
// The size of the powerup on screen.
private static final float POWERUP_SCALE = 3.0f;
// Parameters for the ring burst that is created when the power up spawns.
private static final int RESPAWN_BURST_PARTICLE_COUNT = 50;
private static final float RESPAWN_BURST_PARTICLE_SPEED = 1.5f;
// Parameters for the steady stream of "steam" that comes from the powerup.
private static final int STEAM_PARTICLE_COUNT = 1;
private static final float STEAM_PARTICLE_MIN_SPEED = 0.075f;
private static final float STEAM_PARTICLE_MAX_SPEED = 0.375f;
// Don't try more than 10 times to find a valid location for the powerup.
private static final int MAX_POWERUP_PLACEMENT_ATTEMPTS = 10;
// The powerup should not be placed too close to the edge of the map. The number below
// scales the map size to limit how close the power can get to the edge.
private static final float USABLE_MAP_PERCENT = 0.95f;
private float mPositionX, mPositionY;
private float mTotalFrameCount = 0;
// The respawn counter begins as a small positive number so that the first update will
// trigger a respawn.
private float mRespawnCounter = 1.0f;
private final Utils.Color mColor = new Utils.Color(1.0f, 1.0f, 1.0f, 1.0f);
private float mHeadingX;
private float mHeadingY;
public void update(float timeFactor) {
GameState gameState = GameState.getInstance();
// See if it's time to respawn the powerup.
if (mRespawnCounter > 0.0f) {
mRespawnCounter -= timeFactor;
if (mRespawnCounter <= 0.0f) {
mRespawnCounter = 0.0f;
pickNewLocation();
}
if (mRespawnCounter <= 0.0f) {
gameState.getExplosions().spawnRingBurst(
mPositionX, mPositionY,
Utils.Color.WHITE,
RESPAWN_BURST_PARTICLE_SPEED, RESPAWN_BURST_PARTICLE_SPEED,
RESPAWN_BURST_PARTICLE_COUNT);
}
}
// Keep track of the total elapsed frame count for updating our cyclical animations
// (spinning and pulsing).
mTotalFrameCount += timeFactor;
// Compute an alpha value that varies between fully opaque (1.0)
// and ALPHA_PULSE_MIN_OPACITY (0.66).
float alpha = (float) Math.sin(mTotalFrameCount * ALPHA_PULSE_RATE);
alpha = alpha * (1.0f - ALPHA_PULSE_MIN_OPACITY) + ALPHA_PULSE_MIN_OPACITY;
mColor.setAlpha(alpha);
// Compute the current direction of the powerup.
mHeadingX = (float) Math.sin(mTotalFrameCount * ROTATION_RADIANS_PER_FRAME);
mHeadingY = (float) Math.cos(mTotalFrameCount * ROTATION_RADIANS_PER_FRAME);
if (isSpawned()) {
// The powerup throws off a steady stream of "steam" particles.
// One particle is added each frame.
gameState.getExplosions().spawnRingBurst(
mPositionX, mPositionY,
Utils.Color.WHITE,
STEAM_PARTICLE_MIN_SPEED, STEAM_PARTICLE_MAX_SPEED,
STEAM_PARTICLE_COUNT);
// Check to see if any player is close enough to pick up the powerup.
for (Spaceship player : gameState.getPlayerList()) {
if (player.isActive()) {
float distanceToPlayer = getDistanceToPoint(player.getPositionX(),
player.getPositionY());
if (distanceToPlayer < COLLISION_RADIUS) {
player.giveRandomWeapon();
mRespawnCounter = Utils.randFloatInRange(
RESPAWN_MIN_FRAME_COUNT,
RESPAWN_MAX_FRAME_COUNT);
break;
}
}
}
}
}
public boolean isSpawned() {
return mRespawnCounter == 0.0f;
}
public void draw(ShapeBuffer shapeBuffer) {
if (isSpawned()) {
shapeBuffer.add2DShape(mPositionX, mPositionY, mColor,
Utils.SQUARE_SHAPE, POWERUP_SCALE, POWERUP_SCALE,
mHeadingX, mHeadingY);
}
}
public void pickNewLocation() {
GameState gameState = GameState.getInstance();
boolean validLocation = false;
// Tries to find a location that is not within a wall or too close to one of the players.
// Gives up if it can't find a valid location after 10 tries.
for (int tries = 0; tries < MAX_POWERUP_PLACEMENT_ATTEMPTS; ++tries) {
mPositionX = Utils.randFloatInRange(
GameState.MAP_LEFT_COORDINATE * USABLE_MAP_PERCENT,
GameState.MAP_RIGHT_COORDINATE * USABLE_MAP_PERCENT);
mPositionY = Utils.randFloatInRange(
GameState.MAP_BOTTOM_COORDINATE * USABLE_MAP_PERCENT,
GameState.MAP_TOP_COORDINATE * USABLE_MAP_PERCENT);
// Assume it's true, until it fails...
validLocation = true;
// Don't spawn in a wall.
for (WallSegment wall : gameState.getWallList()) {
if (wall.isInWall(mPositionX, mPositionY)) {
validLocation = false;
break;
}
}
for (Spaceship player : gameState.getPlayerList()) {
// Don't spawn too close to a player.
if (player.isActive()) {
float distanceToPlayer = getDistanceToPoint(player.getPositionX(),
player.getPositionY());
if (distanceToPlayer < MIN_DISTANCE_TO_PLAYER) {
validLocation = false;
break;
}
}
}
}
if (!validLocation) {
// Try again in a second.
mRespawnCounter = GameState.secondsToFrameDelta(1.0f);
}
}
/**
* Convenience function to compute the distance between this powerup and the given point.
*/
private float getDistanceToPoint(float x, float y) {
return Utils.distanceBetweenPoints(mPositionX, mPositionY, x, y);
}
}